src/gui/widgets/qwidgetresizehandler.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtGui module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qwidgetresizehandler_p.h"
       
    43 
       
    44 #ifndef QT_NO_RESIZEHANDLER
       
    45 #include "qframe.h"
       
    46 #include "qapplication.h"
       
    47 #include "qdesktopwidget.h"
       
    48 #include "qcursor.h"
       
    49 #include "qsizegrip.h"
       
    50 #include "qevent.h"
       
    51 #if defined(Q_WS_WIN)
       
    52 #include "qt_windows.h"
       
    53 #endif
       
    54 #include "qdebug.h"
       
    55 #include "private/qlayoutengine_p.h"
       
    56 
       
    57 QT_BEGIN_NAMESPACE
       
    58 
       
    59 #define RANGE 4
       
    60 
       
    61 static bool resizeHorizontalDirectionFixed = false;
       
    62 static bool resizeVerticalDirectionFixed = false;
       
    63 
       
    64 QWidgetResizeHandler::QWidgetResizeHandler(QWidget *parent, QWidget *cw)
       
    65     : QObject(parent), widget(parent), childWidget(cw ? cw : parent),
       
    66       fw(0), extrahei(0), buttonDown(false), moveResizeMode(false), sizeprotect(true), movingEnabled(true)
       
    67 {
       
    68     mode = Nowhere;
       
    69     widget->setMouseTracking(true);
       
    70     QFrame *frame = qobject_cast<QFrame*>(widget);
       
    71     range = frame ? frame->frameWidth() : RANGE;
       
    72     range = qMax(RANGE, range);
       
    73     activeForMove = activeForResize = true;
       
    74     widget->installEventFilter(this);
       
    75 }
       
    76 
       
    77 void QWidgetResizeHandler::setActive(Action ac, bool b)
       
    78 {
       
    79     if (ac & Move)
       
    80         activeForMove = b;
       
    81     if (ac & Resize)
       
    82         activeForResize = b;
       
    83 
       
    84     if (!isActive())
       
    85         setMouseCursor(Nowhere);
       
    86 }
       
    87 
       
    88 bool QWidgetResizeHandler::isActive(Action ac) const
       
    89 {
       
    90     bool b = false;
       
    91     if (ac & Move) b = activeForMove;
       
    92     if (ac & Resize) b |= activeForResize;
       
    93 
       
    94     return b;
       
    95 }
       
    96 
       
    97 bool QWidgetResizeHandler::eventFilter(QObject *o, QEvent *ee)
       
    98 {
       
    99     if (!isActive()
       
   100         || (ee->type() != QEvent::MouseButtonPress
       
   101             && ee->type() != QEvent::MouseButtonRelease
       
   102             && ee->type() != QEvent::MouseMove
       
   103             && ee->type() != QEvent::KeyPress
       
   104             && ee->type() != QEvent::ShortcutOverride)
       
   105         )
       
   106         return false;
       
   107 
       
   108     Q_ASSERT(o == widget);
       
   109     QWidget *w = widget;
       
   110     if (QApplication::activePopupWidget()) {
       
   111         if (buttonDown && ee->type() == QEvent::MouseButtonRelease)
       
   112             buttonDown = false;
       
   113         return false;
       
   114     }
       
   115 
       
   116     QMouseEvent *e = (QMouseEvent*)ee;
       
   117     switch (e->type()) {
       
   118     case QEvent::MouseButtonPress: {
       
   119         if (w->isMaximized())
       
   120             break;
       
   121         if (!widget->rect().contains(widget->mapFromGlobal(e->globalPos())))
       
   122             return false;
       
   123         if (e->button() == Qt::LeftButton) {
       
   124 #if defined(Q_WS_X11)
       
   125             /*
       
   126                Implicit grabs do not stop the X server from changing
       
   127                the cursor in children, which looks *really* bad when
       
   128                doing resizingk, so we grab the cursor. Note that we do
       
   129                not do this on Windows since double clicks are lost due
       
   130                to the grab (see change 198463).
       
   131             */
       
   132             if (e->spontaneous())
       
   133 #  if !defined(QT_NO_CURSOR)
       
   134                 widget->grabMouse(widget->cursor());
       
   135 #  else
       
   136                 widget->grabMouse();
       
   137 #  endif // QT_NO_CURSOR
       
   138 #endif // Q_WS_X11
       
   139             buttonDown = false;
       
   140             emit activate();
       
   141             bool me = movingEnabled;
       
   142             movingEnabled = (me && o == widget);
       
   143             mouseMoveEvent(e);
       
   144             movingEnabled = me;
       
   145             buttonDown = true;
       
   146             moveOffset = widget->mapFromGlobal(e->globalPos());
       
   147             invertedMoveOffset = widget->rect().bottomRight() - moveOffset;
       
   148             if (mode == Center) {
       
   149                 if (movingEnabled)
       
   150                     return true;
       
   151             } else {
       
   152                 return true;
       
   153             }
       
   154         }
       
   155     } break;
       
   156     case QEvent::MouseButtonRelease:
       
   157         if (w->isMaximized())
       
   158             break;
       
   159         if (e->button() == Qt::LeftButton) {
       
   160             moveResizeMode = false;
       
   161             buttonDown = false;
       
   162             widget->releaseMouse();
       
   163             widget->releaseKeyboard();
       
   164             if (mode == Center) {
       
   165                 if (movingEnabled)
       
   166                     return true;
       
   167             } else {
       
   168                 return true;
       
   169             }
       
   170         }
       
   171         break;
       
   172     case QEvent::MouseMove: {
       
   173         if (w->isMaximized())
       
   174             break;
       
   175         buttonDown = buttonDown && (e->buttons() & Qt::LeftButton); // safety, state machine broken!
       
   176         bool me = movingEnabled;
       
   177         movingEnabled = (me && o == widget && (buttonDown || moveResizeMode));
       
   178         mouseMoveEvent(e);
       
   179         movingEnabled = me;
       
   180         if (mode == Center) {
       
   181             if (movingEnabled)
       
   182                 return true;
       
   183         } else {
       
   184             return true;
       
   185         }
       
   186     } break;
       
   187     case QEvent::KeyPress:
       
   188         keyPressEvent((QKeyEvent*)e);
       
   189         break;
       
   190     case QEvent::ShortcutOverride:
       
   191         if (buttonDown) {
       
   192             ((QKeyEvent*)ee)->accept();
       
   193             return true;
       
   194         }
       
   195         break;
       
   196     default:
       
   197         break;
       
   198     }
       
   199 
       
   200     return false;
       
   201 }
       
   202 
       
   203 void QWidgetResizeHandler::mouseMoveEvent(QMouseEvent *e)
       
   204 {
       
   205     QPoint pos = widget->mapFromGlobal(e->globalPos());
       
   206     if (!moveResizeMode && !buttonDown) {
       
   207         if (pos.y() <= range && pos.x() <= range)
       
   208             mode = TopLeft;
       
   209         else if (pos.y() >= widget->height()-range && pos.x() >= widget->width()-range)
       
   210             mode = BottomRight;
       
   211         else if (pos.y() >= widget->height()-range && pos.x() <= range)
       
   212             mode = BottomLeft;
       
   213         else if (pos.y() <= range && pos.x() >= widget->width()-range)
       
   214             mode = TopRight;
       
   215         else if (pos.y() <= range)
       
   216             mode = Top;
       
   217         else if (pos.y() >= widget->height()-range)
       
   218             mode = Bottom;
       
   219         else if (pos.x() <= range)
       
   220             mode = Left;
       
   221         else if ( pos.x() >= widget->width()-range)
       
   222             mode = Right;
       
   223         else if (widget->rect().contains(pos))
       
   224             mode = Center;
       
   225         else
       
   226             mode = Nowhere;
       
   227 
       
   228         if (widget->isMinimized() || !isActive(Resize))
       
   229             mode = Center;
       
   230 #ifndef QT_NO_CURSOR
       
   231         setMouseCursor(mode);
       
   232 #endif
       
   233         return;
       
   234     }
       
   235 
       
   236     if (mode == Center && !movingEnabled)
       
   237         return;
       
   238 
       
   239     if (widget->testAttribute(Qt::WA_WState_ConfigPending))
       
   240         return;
       
   241 
       
   242 
       
   243     QPoint globalPos = (!widget->isWindow() && widget->parentWidget()) ?
       
   244                        widget->parentWidget()->mapFromGlobal(e->globalPos()) : e->globalPos();
       
   245     if (!widget->isWindow() && !widget->parentWidget()->rect().contains(globalPos)) {
       
   246         if (globalPos.x() < 0)
       
   247             globalPos.rx() = 0;
       
   248         if (globalPos.y() < 0)
       
   249             globalPos.ry() = 0;
       
   250         if (sizeprotect && globalPos.x() > widget->parentWidget()->width())
       
   251             globalPos.rx() = widget->parentWidget()->width();
       
   252         if (sizeprotect && globalPos.y() > widget->parentWidget()->height())
       
   253             globalPos.ry() = widget->parentWidget()->height();
       
   254     }
       
   255 
       
   256     QPoint p = globalPos + invertedMoveOffset;
       
   257     QPoint pp = globalPos - moveOffset;
       
   258 
       
   259 #ifdef Q_WS_X11
       
   260     // Workaround for window managers which refuse to move a tool window partially offscreen.
       
   261     QRect desktop = QApplication::desktop()->availableGeometry(widget);
       
   262     pp.rx() = qMax(pp.x(), desktop.left());
       
   263     pp.ry() = qMax(pp.y(), desktop.top());
       
   264     p.rx() = qMin(p.x(), desktop.right());
       
   265     p.ry() = qMin(p.y(), desktop.bottom());
       
   266 #endif
       
   267 
       
   268     QSize ms = qSmartMinSize(childWidget);
       
   269     int mw = ms.width();
       
   270     int mh = ms.height();
       
   271     if (childWidget != widget) {
       
   272         mw += 2 * fw;
       
   273         mh += 2 * fw + extrahei;
       
   274     }
       
   275 
       
   276     QSize maxsize(childWidget->maximumSize());
       
   277     if (childWidget != widget)
       
   278         maxsize += QSize(2 * fw, 2 * fw + extrahei);
       
   279     QSize mpsize(widget->geometry().right() - pp.x() + 1,
       
   280                   widget->geometry().bottom() - pp.y() + 1);
       
   281     mpsize = mpsize.expandedTo(widget->minimumSize()).expandedTo(QSize(mw, mh))
       
   282                     .boundedTo(maxsize);
       
   283     QPoint mp(widget->geometry().right() - mpsize.width() + 1,
       
   284                widget->geometry().bottom() - mpsize.height() + 1);
       
   285 
       
   286     QRect geom = widget->geometry();
       
   287 
       
   288     switch (mode) {
       
   289     case TopLeft:
       
   290         geom = QRect(mp, widget->geometry().bottomRight()) ;
       
   291         break;
       
   292     case BottomRight:
       
   293         geom = QRect(widget->geometry().topLeft(), p) ;
       
   294         break;
       
   295     case BottomLeft:
       
   296         geom = QRect(QPoint(mp.x(), widget->geometry().y()), QPoint(widget->geometry().right(), p.y())) ;
       
   297         break;
       
   298     case TopRight:
       
   299         geom = QRect(QPoint(widget->geometry().x(), mp.y()), QPoint(p.x(), widget->geometry().bottom())) ;
       
   300         break;
       
   301     case Top:
       
   302         geom = QRect(QPoint(widget->geometry().left(), mp.y()), widget->geometry().bottomRight()) ;
       
   303         break;
       
   304     case Bottom:
       
   305         geom = QRect(widget->geometry().topLeft(), QPoint(widget->geometry().right(), p.y())) ;
       
   306         break;
       
   307     case Left:
       
   308         geom = QRect(QPoint(mp.x(), widget->geometry().top()), widget->geometry().bottomRight()) ;
       
   309         break;
       
   310     case Right:
       
   311         geom = QRect(widget->geometry().topLeft(), QPoint(p.x(), widget->geometry().bottom())) ;
       
   312         break;
       
   313     case Center:
       
   314         geom.moveTopLeft(pp);
       
   315         break;
       
   316     default:
       
   317         break;
       
   318     }
       
   319 
       
   320     geom = QRect(geom.topLeft(),
       
   321                   geom.size().expandedTo(widget->minimumSize())
       
   322                              .expandedTo(QSize(mw, mh))
       
   323                              .boundedTo(maxsize));
       
   324 
       
   325     if (geom != widget->geometry() &&
       
   326         (widget->isWindow() || widget->parentWidget()->rect().intersects(geom))) {
       
   327         if (mode == Center)
       
   328             widget->move(geom.topLeft());
       
   329         else
       
   330             widget->setGeometry(geom);
       
   331     }
       
   332 
       
   333     QApplication::syncX();
       
   334 }
       
   335 
       
   336 void QWidgetResizeHandler::setMouseCursor(MousePosition m)
       
   337 {
       
   338 #ifdef QT_NO_CURSOR
       
   339     Q_UNUSED(m);
       
   340 #else
       
   341     QObjectList children = widget->children();
       
   342     for (int i = 0; i < children.size(); ++i) {
       
   343         if (QWidget *w = qobject_cast<QWidget*>(children.at(i))) {
       
   344             if (!w->testAttribute(Qt::WA_SetCursor) && !w->inherits("QWorkspaceTitleBar")) {
       
   345                 w->setCursor(Qt::ArrowCursor);
       
   346             }
       
   347         }
       
   348     }
       
   349 
       
   350     switch (m) {
       
   351     case TopLeft:
       
   352     case BottomRight:
       
   353         widget->setCursor(Qt::SizeFDiagCursor);
       
   354         break;
       
   355     case BottomLeft:
       
   356     case TopRight:
       
   357         widget->setCursor(Qt::SizeBDiagCursor);
       
   358         break;
       
   359     case Top:
       
   360     case Bottom:
       
   361         widget->setCursor(Qt::SizeVerCursor);
       
   362         break;
       
   363     case Left:
       
   364     case Right:
       
   365         widget->setCursor(Qt::SizeHorCursor);
       
   366         break;
       
   367     default:
       
   368         widget->setCursor(Qt::ArrowCursor);
       
   369         break;
       
   370     }
       
   371 #endif // QT_NO_CURSOR
       
   372 }
       
   373 
       
   374 void QWidgetResizeHandler::keyPressEvent(QKeyEvent * e)
       
   375 {
       
   376     if (!isMove() && !isResize())
       
   377         return;
       
   378     bool is_control = e->modifiers() & Qt::ControlModifier;
       
   379     int delta = is_control?1:8;
       
   380     QPoint pos = QCursor::pos();
       
   381     switch (e->key()) {
       
   382     case Qt::Key_Left:
       
   383         pos.rx() -= delta;
       
   384         if (pos.x() <= QApplication::desktop()->geometry().left()) {
       
   385             if (mode == TopLeft || mode == BottomLeft) {
       
   386                 moveOffset.rx() += delta;
       
   387                 invertedMoveOffset.rx() += delta;
       
   388             } else {
       
   389                 moveOffset.rx() -= delta;
       
   390                 invertedMoveOffset.rx() -= delta;
       
   391             }
       
   392         }
       
   393         if (isResize() && !resizeHorizontalDirectionFixed) {
       
   394             resizeHorizontalDirectionFixed = true;
       
   395             if (mode == BottomRight)
       
   396                 mode = BottomLeft;
       
   397             else if (mode == TopRight)
       
   398                 mode = TopLeft;
       
   399 #ifndef QT_NO_CURSOR
       
   400             setMouseCursor(mode);
       
   401             widget->grabMouse(widget->cursor());
       
   402 #else
       
   403             widget->grabMouse();
       
   404 #endif
       
   405         }
       
   406         break;
       
   407     case Qt::Key_Right:
       
   408         pos.rx() += delta;
       
   409         if (pos.x() >= QApplication::desktop()->geometry().right()) {
       
   410             if (mode == TopRight || mode == BottomRight) {
       
   411                 moveOffset.rx() += delta;
       
   412                 invertedMoveOffset.rx() += delta;
       
   413             } else {
       
   414                 moveOffset.rx() -= delta;
       
   415                 invertedMoveOffset.rx() -= delta;
       
   416             }
       
   417         }
       
   418         if (isResize() && !resizeHorizontalDirectionFixed) {
       
   419             resizeHorizontalDirectionFixed = true;
       
   420             if (mode == BottomLeft)
       
   421                 mode = BottomRight;
       
   422             else if (mode == TopLeft)
       
   423                 mode = TopRight;
       
   424 #ifndef QT_NO_CURSOR
       
   425             setMouseCursor(mode);
       
   426             widget->grabMouse(widget->cursor());
       
   427 #else
       
   428             widget->grabMouse();
       
   429 #endif
       
   430         }
       
   431         break;
       
   432     case Qt::Key_Up:
       
   433         pos.ry() -= delta;
       
   434         if (pos.y() <= QApplication::desktop()->geometry().top()) {
       
   435             if (mode == TopLeft || mode == TopRight) {
       
   436                 moveOffset.ry() += delta;
       
   437                 invertedMoveOffset.ry() += delta;
       
   438             } else {
       
   439                 moveOffset.ry() -= delta;
       
   440                 invertedMoveOffset.ry() -= delta;
       
   441             }
       
   442         }
       
   443         if (isResize() && !resizeVerticalDirectionFixed) {
       
   444             resizeVerticalDirectionFixed = true;
       
   445             if (mode == BottomLeft)
       
   446                 mode = TopLeft;
       
   447             else if (mode == BottomRight)
       
   448                 mode = TopRight;
       
   449 #ifndef QT_NO_CURSOR
       
   450             setMouseCursor(mode);
       
   451             widget->grabMouse(widget->cursor());
       
   452 #else
       
   453             widget->grabMouse();
       
   454 #endif
       
   455         }
       
   456         break;
       
   457     case Qt::Key_Down:
       
   458         pos.ry() += delta;
       
   459         if (pos.y() >= QApplication::desktop()->geometry().bottom()) {
       
   460             if (mode == BottomLeft || mode == BottomRight) {
       
   461                 moveOffset.ry() += delta;
       
   462                 invertedMoveOffset.ry() += delta;
       
   463             } else {
       
   464                 moveOffset.ry() -= delta;
       
   465                 invertedMoveOffset.ry() -= delta;
       
   466             }
       
   467         }
       
   468         if (isResize() && !resizeVerticalDirectionFixed) {
       
   469             resizeVerticalDirectionFixed = true;
       
   470             if (mode == TopLeft)
       
   471                 mode = BottomLeft;
       
   472             else if (mode == TopRight)
       
   473                 mode = BottomRight;
       
   474 #ifndef QT_NO_CURSOR
       
   475             setMouseCursor(mode);
       
   476             widget->grabMouse(widget->cursor());
       
   477 #else
       
   478             widget->grabMouse();
       
   479 #endif
       
   480         }
       
   481         break;
       
   482     case Qt::Key_Space:
       
   483     case Qt::Key_Return:
       
   484     case Qt::Key_Enter:
       
   485     case Qt::Key_Escape:
       
   486         moveResizeMode = false;
       
   487         widget->releaseMouse();
       
   488         widget->releaseKeyboard();
       
   489         buttonDown = false;
       
   490         break;
       
   491     default:
       
   492         return;
       
   493     }
       
   494     QCursor::setPos(pos);
       
   495 }
       
   496 
       
   497 
       
   498 void QWidgetResizeHandler::doResize()
       
   499 {
       
   500     if (!activeForResize)
       
   501         return;
       
   502 
       
   503     moveResizeMode = true;
       
   504     moveOffset = widget->mapFromGlobal(QCursor::pos());
       
   505     if (moveOffset.x() < widget->width()/2) {
       
   506         if (moveOffset.y() < widget->height()/2)
       
   507             mode = TopLeft;
       
   508         else
       
   509             mode = BottomLeft;
       
   510     } else {
       
   511         if (moveOffset.y() < widget->height()/2)
       
   512             mode = TopRight;
       
   513         else
       
   514             mode = BottomRight;
       
   515     }
       
   516     invertedMoveOffset = widget->rect().bottomRight() - moveOffset;
       
   517 #ifndef QT_NO_CURSOR
       
   518     setMouseCursor(mode);
       
   519     widget->grabMouse(widget->cursor() );
       
   520 #else
       
   521     widget->grabMouse();
       
   522 #endif
       
   523     widget->grabKeyboard();
       
   524     resizeHorizontalDirectionFixed = false;
       
   525     resizeVerticalDirectionFixed = false;
       
   526 }
       
   527 
       
   528 void QWidgetResizeHandler::doMove()
       
   529 {
       
   530     if (!activeForMove)
       
   531         return;
       
   532 
       
   533     mode = Center;
       
   534     moveResizeMode = true;
       
   535     moveOffset = widget->mapFromGlobal(QCursor::pos());
       
   536     invertedMoveOffset = widget->rect().bottomRight() - moveOffset;
       
   537 #ifndef QT_NO_CURSOR
       
   538     widget->grabMouse(Qt::SizeAllCursor);
       
   539 #else
       
   540     widget->grabMouse();
       
   541 #endif
       
   542     widget->grabKeyboard();
       
   543 }
       
   544 
       
   545 QT_END_NAMESPACE
       
   546 
       
   547 #endif //QT_NO_RESIZEHANDLER