source: trunk/src/gui/widgets/qsizegrip.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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