/src/gui/widgets/qsizegrip.cpp

https://bitbucket.org/ultra_iter/qt-vtl · C++ · 570 lines · 365 code · 54 blank · 151 comment · 89 complexity · 44ddc93dc4152fd49d2b9c26761aebf8 MD5 · raw file

  1. /****************************************************************************
  2. **
  3. ** Copyright (C) 2012 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. ** GNU Lesser General Public License Usage
  11. ** This file may be used under the terms of the GNU Lesser General Public
  12. ** License version 2.1 as published by the Free Software Foundation and
  13. ** appearing in the file LICENSE.LGPL included in the packaging of this
  14. ** file. Please review the following information to ensure the GNU Lesser
  15. ** General Public License version 2.1 requirements will be met:
  16. ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
  17. **
  18. ** In addition, as a special exception, Nokia gives you certain additional
  19. ** rights. These rights are described in the Nokia Qt LGPL Exception
  20. ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
  21. **
  22. ** GNU General Public License Usage
  23. ** Alternatively, this file may be used under the terms of the GNU General
  24. ** Public License version 3.0 as published by the Free Software Foundation
  25. ** and appearing in the file LICENSE.GPL included in the packaging of this
  26. ** file. Please review the following information to ensure the GNU General
  27. ** Public License version 3.0 requirements will be met:
  28. ** http://www.gnu.org/copyleft/gpl.html.
  29. **
  30. ** Other Usage
  31. ** Alternatively, this file may be used in accordance with the terms and
  32. ** conditions contained in a signed written agreement between you and Nokia.
  33. **
  34. **
  35. **
  36. **
  37. **
  38. ** $QT_END_LICENSE$
  39. **
  40. ****************************************************************************/
  41. #include "qsizegrip.h"
  42. #ifndef QT_NO_SIZEGRIP
  43. #include "qapplication.h"
  44. #include "qevent.h"
  45. #include "qpainter.h"
  46. #include "qstyle.h"
  47. #include "qstyleoption.h"
  48. #include "qlayout.h"
  49. #include "qdebug.h"
  50. #include <QDesktopWidget>
  51. #if defined(Q_WS_X11)
  52. #include <private/qt_x11_p.h>
  53. #elif defined (Q_WS_WIN)
  54. #include "qt_windows.h"
  55. #endif
  56. #ifdef Q_WS_MAC
  57. #include <private/qt_mac_p.h>
  58. #endif
  59. #include <private/qwidget_p.h>
  60. #include <QtGui/qabstractscrollarea.h>
  61. #define SZ_SIZEBOTTOMRIGHT 0xf008
  62. #define SZ_SIZEBOTTOMLEFT 0xf007
  63. #define SZ_SIZETOPLEFT 0xf004
  64. #define SZ_SIZETOPRIGHT 0xf005
  65. QT_BEGIN_NAMESPACE
  66. static QWidget *qt_sizegrip_topLevelWidget(QWidget* w)
  67. {
  68. while (w && !w->isWindow() && w->windowType() != Qt::SubWindow)
  69. w = w->parentWidget();
  70. return w;
  71. }
  72. class QSizeGripPrivate : public QWidgetPrivate
  73. {
  74. Q_DECLARE_PUBLIC(QSizeGrip)
  75. public:
  76. void init();
  77. QPoint p;
  78. QRect r;
  79. int d;
  80. int dxMax;
  81. int dyMax;
  82. Qt::Corner m_corner;
  83. bool gotMousePress;
  84. QWidget *tlw;
  85. #ifdef Q_WS_MAC
  86. void updateMacSizer(bool hide) const;
  87. #endif
  88. Qt::Corner corner() const;
  89. inline bool atBottom() const
  90. {
  91. return m_corner == Qt::BottomRightCorner || m_corner == Qt::BottomLeftCorner;
  92. }
  93. inline bool atLeft() const
  94. {
  95. return m_corner == Qt::BottomLeftCorner || m_corner == Qt::TopLeftCorner;
  96. }
  97. void updateTopLevelWidget()
  98. {
  99. Q_Q(QSizeGrip);
  100. QWidget *w = qt_sizegrip_topLevelWidget(q);
  101. if (tlw == w)
  102. return;
  103. if (tlw)
  104. tlw->removeEventFilter(q);
  105. tlw = w;
  106. if (tlw)
  107. tlw->installEventFilter(q);
  108. }
  109. // This slot is invoked by QLayout when the size grip is added to
  110. // a layout or reparented after the tlw is shown. This re-implementation is basically
  111. // the same as QWidgetPrivate::_q_showIfNotHidden except that it checks
  112. // for Qt::WindowFullScreen and Qt::WindowMaximized as well.
  113. void _q_showIfNotHidden()
  114. {
  115. Q_Q(QSizeGrip);
  116. bool showSizeGrip = !(q->isHidden() && q->testAttribute(Qt::WA_WState_ExplicitShowHide));
  117. updateTopLevelWidget();
  118. if (tlw && showSizeGrip) {
  119. Qt::WindowStates sizeGripNotVisibleState = Qt::WindowFullScreen;
  120. #ifndef Q_WS_MAC
  121. sizeGripNotVisibleState |= Qt::WindowMaximized;
  122. #endif
  123. // Don't show the size grip if the tlw is maximized or in full screen mode.
  124. showSizeGrip = !(tlw->windowState() & sizeGripNotVisibleState);
  125. }
  126. if (showSizeGrip)
  127. q->setVisible(true);
  128. }
  129. };
  130. #ifdef Q_WS_MAC
  131. void QSizeGripPrivate::updateMacSizer(bool hide) const
  132. {
  133. Q_Q(const QSizeGrip);
  134. if (QApplication::closingDown() || !parent)
  135. return;
  136. QWidget *topLevelWindow = qt_sizegrip_topLevelWidget(const_cast<QSizeGrip *>(q));
  137. if(topLevelWindow && topLevelWindow->isWindow())
  138. QWidgetPrivate::qt_mac_update_sizer(topLevelWindow, hide ? -1 : 1);
  139. }
  140. #endif
  141. Qt::Corner QSizeGripPrivate::corner() const
  142. {
  143. Q_Q(const QSizeGrip);
  144. QWidget *tlw = qt_sizegrip_topLevelWidget(const_cast<QSizeGrip *>(q));
  145. const QPoint sizeGripPos = q->mapTo(tlw, QPoint(0, 0));
  146. bool isAtBottom = sizeGripPos.y() >= tlw->height() / 2;
  147. bool isAtLeft = sizeGripPos.x() <= tlw->width() / 2;
  148. if (isAtLeft)
  149. return isAtBottom ? Qt::BottomLeftCorner : Qt::TopLeftCorner;
  150. else
  151. return isAtBottom ? Qt::BottomRightCorner : Qt::TopRightCorner;
  152. }
  153. /*!
  154. \class QSizeGrip
  155. \brief The QSizeGrip class provides a resize handle for resizing top-level windows.
  156. \ingroup mainwindow-classes
  157. \ingroup basicwidgets
  158. This widget works like the standard Windows resize handle. In the
  159. X11 version this resize handle generally works differently from
  160. the one provided by the system if the X11 window manager does not
  161. support necessary modern post-ICCCM specifications.
  162. Put this widget anywhere in a widget tree and the user can use it
  163. to resize the top-level window or any widget with the Qt::SubWindow
  164. flag set. Generally, this should be in the lower right-hand corner.
  165. Note that QStatusBar already uses this widget, so if you have a
  166. status bar (e.g., you are using QMainWindow), then you don't need
  167. to use this widget explicitly.
  168. On some platforms the size grip automatically hides itself when the
  169. window is shown full screen or maximised.
  170. \table 50%
  171. \row \o \inlineimage plastique-sizegrip.png Screenshot of a Plastique style size grip
  172. \o A size grip widget at the bottom-right corner of a main window, shown in the
  173. \l{Plastique Style Widget Gallery}{Plastique widget style}.
  174. \endtable
  175. The QSizeGrip class inherits QWidget and reimplements the \l
  176. {QWidget::mousePressEvent()}{mousePressEvent()} and \l
  177. {QWidget::mouseMoveEvent()}{mouseMoveEvent()} functions to feature
  178. the resize functionality, and the \l
  179. {QWidget::paintEvent()}{paintEvent()} function to render the
  180. size grip widget.
  181. \sa QStatusBar QWidget::windowState()
  182. */
  183. /*!
  184. Constructs a resize corner as a child widget of the given \a
  185. parent.
  186. */
  187. QSizeGrip::QSizeGrip(QWidget * parent)
  188. : QWidget(*new QSizeGripPrivate, parent, 0)
  189. {
  190. Q_D(QSizeGrip);
  191. d->init();
  192. }
  193. #ifdef QT3_SUPPORT
  194. /*!
  195. \obsolete
  196. Constructs a resize corner with the given \a name, as a child
  197. widget of the given \a parent.
  198. */
  199. QSizeGrip::QSizeGrip(QWidget * parent, const char* name)
  200. : QWidget(*new QSizeGripPrivate, parent, 0)
  201. {
  202. Q_D(QSizeGrip);
  203. setObjectName(QString::fromAscii(name));
  204. d->init();
  205. }
  206. #endif
  207. void QSizeGripPrivate::init()
  208. {
  209. Q_Q(QSizeGrip);
  210. dxMax = 0;
  211. dyMax = 0;
  212. tlw = 0;
  213. m_corner = q->isLeftToRight() ? Qt::BottomRightCorner : Qt::BottomLeftCorner;
  214. gotMousePress = false;
  215. #if !defined(QT_NO_CURSOR) && !defined(Q_WS_MAC)
  216. q->setCursor(m_corner == Qt::TopLeftCorner || m_corner == Qt::BottomRightCorner
  217. ? Qt::SizeFDiagCursor : Qt::SizeBDiagCursor);
  218. #endif
  219. q->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed));
  220. updateTopLevelWidget();
  221. }
  222. /*!
  223. Destroys this size grip.
  224. */
  225. QSizeGrip::~QSizeGrip()
  226. {
  227. }
  228. /*!
  229. \reimp
  230. */
  231. QSize QSizeGrip::sizeHint() const
  232. {
  233. QStyleOption opt(0);
  234. opt.init(this);
  235. return (style()->sizeFromContents(QStyle::CT_SizeGrip, &opt, QSize(13, 13), this).
  236. expandedTo(QApplication::globalStrut()));
  237. }
  238. /*!
  239. Paints the resize grip.
  240. Resize grips are usually rendered as small diagonal textured lines
  241. in the lower-right corner. The paint event is passed in the \a
  242. event parameter.
  243. */
  244. void QSizeGrip::paintEvent(QPaintEvent *event)
  245. {
  246. Q_UNUSED(event);
  247. Q_D(QSizeGrip);
  248. QPainter painter(this);
  249. QStyleOptionSizeGrip opt;
  250. opt.init(this);
  251. opt.corner = d->m_corner;
  252. style()->drawControl(QStyle::CE_SizeGrip, &opt, &painter, this);
  253. }
  254. /*!
  255. \fn void QSizeGrip::mousePressEvent(QMouseEvent * event)
  256. Receives the mouse press events for the widget, and primes the
  257. resize operation. The mouse press event is passed in the \a event
  258. parameter.
  259. */
  260. void QSizeGrip::mousePressEvent(QMouseEvent * e)
  261. {
  262. if (e->button() != Qt::LeftButton) {
  263. QWidget::mousePressEvent(e);
  264. return;
  265. }
  266. Q_D(QSizeGrip);
  267. QWidget *tlw = qt_sizegrip_topLevelWidget(this);
  268. d->p = e->globalPos();
  269. d->gotMousePress = true;
  270. d->r = tlw->geometry();
  271. #ifdef Q_WS_X11
  272. // Use a native X11 sizegrip for "real" top-level windows if supported.
  273. if (tlw->isWindow() && X11->isSupportedByWM(ATOM(_NET_WM_MOVERESIZE))
  274. && !(tlw->windowFlags() & Qt::X11BypassWindowManagerHint)
  275. && !tlw->testAttribute(Qt::WA_DontShowOnScreen) && !qt_widget_private(tlw)->hasHeightForWidth()) {
  276. XEvent xev;
  277. xev.xclient.type = ClientMessage;
  278. xev.xclient.message_type = ATOM(_NET_WM_MOVERESIZE);
  279. xev.xclient.display = X11->display;
  280. xev.xclient.window = tlw->winId();
  281. xev.xclient.format = 32;
  282. xev.xclient.data.l[0] = e->globalPos().x();
  283. xev.xclient.data.l[1] = e->globalPos().y();
  284. if (d->atBottom())
  285. xev.xclient.data.l[2] = d->atLeft() ? 6 : 4; // bottomleft/bottomright
  286. else
  287. xev.xclient.data.l[2] = d->atLeft() ? 0 : 2; // topleft/topright
  288. xev.xclient.data.l[3] = Button1;
  289. xev.xclient.data.l[4] = 0;
  290. XUngrabPointer(X11->display, X11->time);
  291. XSendEvent(X11->display, QX11Info::appRootWindow(x11Info().screen()), False,
  292. SubstructureRedirectMask | SubstructureNotifyMask, &xev);
  293. return;
  294. }
  295. #endif // Q_WS_X11
  296. #ifdef Q_WS_WIN
  297. if (tlw->isWindow() && !tlw->testAttribute(Qt::WA_DontShowOnScreen) && !qt_widget_private(tlw)->hasHeightForWidth()) {
  298. uint orientation = 0;
  299. if (d->atBottom())
  300. orientation = d->atLeft() ? SZ_SIZEBOTTOMLEFT : SZ_SIZEBOTTOMRIGHT;
  301. else
  302. orientation = d->atLeft() ? SZ_SIZETOPLEFT : SZ_SIZETOPRIGHT;
  303. ReleaseCapture();
  304. PostMessage(tlw->winId(), WM_SYSCOMMAND, orientation, 0);
  305. return;
  306. }
  307. #endif // Q_WS_WIN
  308. // Find available desktop/workspace geometry.
  309. QRect availableGeometry;
  310. bool hasVerticalSizeConstraint = true;
  311. bool hasHorizontalSizeConstraint = true;
  312. if (tlw->isWindow())
  313. availableGeometry = QApplication::desktop()->availableGeometry(tlw);
  314. else {
  315. const QWidget *tlwParent = tlw->parentWidget();
  316. // Check if tlw is inside QAbstractScrollArea/QScrollArea.
  317. // If that's the case tlw->parentWidget() will return the viewport
  318. // and tlw->parentWidget()->parentWidget() will return the scroll area.
  319. #ifndef QT_NO_SCROLLAREA
  320. QAbstractScrollArea *scrollArea = qobject_cast<QAbstractScrollArea *>(tlwParent->parentWidget());
  321. if (scrollArea) {
  322. hasHorizontalSizeConstraint = scrollArea->horizontalScrollBarPolicy() == Qt::ScrollBarAlwaysOff;
  323. hasVerticalSizeConstraint = scrollArea->verticalScrollBarPolicy() == Qt::ScrollBarAlwaysOff;
  324. }
  325. #endif // QT_NO_SCROLLAREA
  326. availableGeometry = tlwParent->contentsRect();
  327. }
  328. // Find frame geometries, title bar height, and decoration sizes.
  329. const QRect frameGeometry = tlw->frameGeometry();
  330. const int titleBarHeight = qMax(tlw->geometry().y() - frameGeometry.y(), 0);
  331. const int bottomDecoration = qMax(frameGeometry.height() - tlw->height() - titleBarHeight, 0);
  332. const int leftRightDecoration = qMax((frameGeometry.width() - tlw->width()) / 2, 0);
  333. // Determine dyMax depending on whether the sizegrip is at the bottom
  334. // of the widget or not.
  335. if (d->atBottom()) {
  336. if (hasVerticalSizeConstraint)
  337. d->dyMax = availableGeometry.bottom() - d->r.bottom() - bottomDecoration;
  338. else
  339. d->dyMax = INT_MAX;
  340. } else {
  341. if (hasVerticalSizeConstraint)
  342. d->dyMax = availableGeometry.y() - d->r.y() + titleBarHeight;
  343. else
  344. d->dyMax = -INT_MAX;
  345. }
  346. // In RTL mode, the size grip is to the left; find dxMax from the desktop/workspace
  347. // geometry, the size grip geometry and the width of the decoration.
  348. if (d->atLeft()) {
  349. if (hasHorizontalSizeConstraint)
  350. d->dxMax = availableGeometry.x() - d->r.x() + leftRightDecoration;
  351. else
  352. d->dxMax = -INT_MAX;
  353. } else {
  354. if (hasHorizontalSizeConstraint)
  355. d->dxMax = availableGeometry.right() - d->r.right() - leftRightDecoration;
  356. else
  357. d->dxMax = INT_MAX;
  358. }
  359. }
  360. /*!
  361. \fn void QSizeGrip::mouseMoveEvent(QMouseEvent * event)
  362. Resizes the top-level widget containing this widget. The mouse
  363. move event is passed in the \a event parameter.
  364. */
  365. void QSizeGrip::mouseMoveEvent(QMouseEvent * e)
  366. {
  367. if (e->buttons() != Qt::LeftButton) {
  368. QWidget::mouseMoveEvent(e);
  369. return;
  370. }
  371. Q_D(QSizeGrip);
  372. QWidget* tlw = qt_sizegrip_topLevelWidget(this);
  373. if (!d->gotMousePress || tlw->testAttribute(Qt::WA_WState_ConfigPending))
  374. return;
  375. #ifdef Q_WS_X11
  376. if (tlw->isWindow() && X11->isSupportedByWM(ATOM(_NET_WM_MOVERESIZE))
  377. && tlw->isTopLevel() && !(tlw->windowFlags() & Qt::X11BypassWindowManagerHint)
  378. && !tlw->testAttribute(Qt::WA_DontShowOnScreen) && !qt_widget_private(tlw)->hasHeightForWidth())
  379. return;
  380. #endif
  381. #ifdef Q_WS_WIN
  382. if (tlw->isWindow() && GetSystemMenu(tlw->winId(), FALSE) != 0 && internalWinId()
  383. && !tlw->testAttribute(Qt::WA_DontShowOnScreen) && !qt_widget_private(tlw)->hasHeightForWidth()) {
  384. MSG msg;
  385. while(PeekMessage(&msg, winId(), WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE));
  386. return;
  387. }
  388. #endif
  389. QPoint np(e->globalPos());
  390. // Don't extend beyond the available geometry; bound to dyMax and dxMax.
  391. QSize ns;
  392. if (d->atBottom())
  393. ns.rheight() = d->r.height() + qMin(np.y() - d->p.y(), d->dyMax);
  394. else
  395. ns.rheight() = d->r.height() - qMax(np.y() - d->p.y(), d->dyMax);
  396. if (d->atLeft())
  397. ns.rwidth() = d->r.width() - qMax(np.x() - d->p.x(), d->dxMax);
  398. else
  399. ns.rwidth() = d->r.width() + qMin(np.x() - d->p.x(), d->dxMax);
  400. ns = QLayout::closestAcceptableSize(tlw, ns);
  401. QPoint p;
  402. QRect nr(p, ns);
  403. if (d->atBottom()) {
  404. if (d->atLeft())
  405. nr.moveTopRight(d->r.topRight());
  406. else
  407. nr.moveTopLeft(d->r.topLeft());
  408. } else {
  409. if (d->atLeft())
  410. nr.moveBottomRight(d->r.bottomRight());
  411. else
  412. nr.moveBottomLeft(d->r.bottomLeft());
  413. }
  414. tlw->setGeometry(nr);
  415. }
  416. /*!
  417. \reimp
  418. */
  419. void QSizeGrip::mouseReleaseEvent(QMouseEvent *mouseEvent)
  420. {
  421. if (mouseEvent->button() == Qt::LeftButton) {
  422. Q_D(QSizeGrip);
  423. d->gotMousePress = false;
  424. d->p = QPoint();
  425. } else {
  426. QWidget::mouseReleaseEvent(mouseEvent);
  427. }
  428. }
  429. /*!
  430. \reimp
  431. */
  432. void QSizeGrip::moveEvent(QMoveEvent * /*moveEvent*/)
  433. {
  434. Q_D(QSizeGrip);
  435. // We're inside a resize operation; no update necessary.
  436. if (!d->p.isNull())
  437. return;
  438. d->m_corner = d->corner();
  439. #if !defined(QT_NO_CURSOR) && !defined(Q_WS_MAC)
  440. setCursor(d->m_corner == Qt::TopLeftCorner || d->m_corner == Qt::BottomRightCorner
  441. ? Qt::SizeFDiagCursor : Qt::SizeBDiagCursor);
  442. #endif
  443. }
  444. /*!
  445. \reimp
  446. */
  447. void QSizeGrip::showEvent(QShowEvent *showEvent)
  448. {
  449. #ifdef Q_WS_MAC
  450. d_func()->updateMacSizer(false);
  451. #endif
  452. QWidget::showEvent(showEvent);
  453. }
  454. /*!
  455. \reimp
  456. */
  457. void QSizeGrip::hideEvent(QHideEvent *hideEvent)
  458. {
  459. #ifdef Q_WS_MAC
  460. d_func()->updateMacSizer(true);
  461. #endif
  462. QWidget::hideEvent(hideEvent);
  463. }
  464. /*!
  465. \reimp
  466. */
  467. void QSizeGrip::setVisible(bool visible)
  468. {
  469. QWidget::setVisible(visible);
  470. }
  471. /*! \reimp */
  472. bool QSizeGrip::eventFilter(QObject *o, QEvent *e)
  473. {
  474. Q_D(QSizeGrip);
  475. if ((isHidden() && testAttribute(Qt::WA_WState_ExplicitShowHide))
  476. || e->type() != QEvent::WindowStateChange
  477. || o != d->tlw) {
  478. return QWidget::eventFilter(o, e);
  479. }
  480. Qt::WindowStates sizeGripNotVisibleState = Qt::WindowFullScreen;
  481. #ifndef Q_WS_MAC
  482. sizeGripNotVisibleState |= Qt::WindowMaximized;
  483. #endif
  484. // Don't show the size grip if the tlw is maximized or in full screen mode.
  485. setVisible(!(d->tlw->windowState() & sizeGripNotVisibleState));
  486. setAttribute(Qt::WA_WState_ExplicitShowHide, false);
  487. return QWidget::eventFilter(o, e);
  488. }
  489. /*!
  490. \reimp
  491. */
  492. bool QSizeGrip::event(QEvent *event)
  493. {
  494. return QWidget::event(event);
  495. }
  496. #ifdef Q_WS_WIN
  497. /*! \reimp */
  498. bool QSizeGrip::winEvent( MSG *m, long *result )
  499. {
  500. return QWidget::winEvent(m, result);
  501. }
  502. #endif
  503. QT_END_NAMESPACE
  504. #include "moc_qsizegrip.cpp"
  505. #endif //QT_NO_SIZEGRIP