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

Last change on this file since 460 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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