1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "qwsmanager_qws.h"
43 
44 #ifndef QT_NO_QWS_MANAGER
45 
46 #include "qdrawutil.h"
47 #include "qapplication.h"
48 #include "qstyle.h"
49 #include "qwidget.h"
50 #include "qmenu.h"
51 #include "qpainter.h"
52 #include "private/qpainter_p.h"
53 #include "qregion.h"
54 #include "qevent.h"
55 #include "qcursor.h"
56 #include "qwsdisplay_qws.h"
57 #include "qdesktopwidget.h"
58 
59 #include <private/qapplication_p.h>
60 #include <private/qwidget_p.h>
61 #include <private/qbackingstore_p.h>
62 #include <private/qwindowsurface_qws_p.h>
63 #include "qdecorationfactory_qws.h"
64 
65 #include "qlayout.h"
66 
67 #include "qwsmanager_p.h"
68 
69 #include <qdebug.h>
70 
71 QT_BEGIN_NAMESPACE
72 
73 QWidget *QWSManagerPrivate::active = 0;
74 QPoint QWSManagerPrivate::mousePos;
75 
76 
QWSManagerPrivate()77 QWSManagerPrivate::QWSManagerPrivate()
78     : QObjectPrivate(), activeRegion(QDecoration::None), managed(0), popup(0),
79       previousRegionType(0), previousRegionRepainted(false), entireDecorationNeedsRepaint(false)
80 {
81     cached_region.regionType = 0;
82 }
83 
cachedRegion()84 QRegion &QWSManager::cachedRegion()
85 {
86     return d_func()->cached_region.region;
87 }
88 
89 /*!
90     \class QWSManager
91     \ingroup qws
92     \internal
93 */
94 
95 /*!
96 
97 */
QWSManager(QWidget * w)98 QWSManager::QWSManager(QWidget *w)
99     : QObject(*new QWSManagerPrivate, (QObject*)0)
100 {
101     d_func()->managed = w;
102 
103 }
104 
~QWSManager()105 QWSManager::~QWSManager()
106 {
107     Q_D(QWSManager);
108 #ifndef QT_NO_MENU
109     if (d->popup)
110         delete d->popup;
111 #endif
112     if (d->managed == QWSManagerPrivate::active)
113         QWSManagerPrivate::active = 0;
114 }
115 
widget()116 QWidget *QWSManager::widget()
117 {
118     Q_D(QWSManager);
119     return d->managed;
120 }
121 
grabbedMouse()122 QWidget *QWSManager::grabbedMouse()
123 {
124     return QWSManagerPrivate::active;
125 }
126 
region()127 QRegion QWSManager::region()
128 {
129     Q_D(QWSManager);
130     return QApplication::qwsDecoration().region(d->managed, d->managed->geometry());
131 }
132 
event(QEvent * e)133 bool QWSManager::event(QEvent *e)
134 {
135     if (QObject::event(e))
136         return true;
137 
138     switch (e->type()) {
139         case QEvent::MouseMove:
140             mouseMoveEvent((QMouseEvent*)e);
141             break;
142 
143         case QEvent::MouseButtonPress:
144             mousePressEvent((QMouseEvent*)e);
145             break;
146 
147         case QEvent::MouseButtonRelease:
148             mouseReleaseEvent((QMouseEvent*)e);
149             break;
150 
151         case QEvent::MouseButtonDblClick:
152             mouseDoubleClickEvent((QMouseEvent*)e);
153             break;
154 
155         case QEvent::Paint:
156             paintEvent((QPaintEvent*)e);
157             break;
158 
159         default:
160             return false;
161             break;
162     }
163 
164     return true;
165 }
166 
mousePressEvent(QMouseEvent * e)167 void QWSManager::mousePressEvent(QMouseEvent *e)
168 {
169     Q_D(QWSManager);
170     d->mousePos = e->globalPos();
171     d->activeRegion = QApplication::qwsDecoration().regionAt(d->managed, d->mousePos);
172     if(d->cached_region.regionType)
173         d->previousRegionRepainted |= repaintRegion(d->cached_region.regionType, QDecoration::Pressed);
174 
175     if (d->activeRegion == QDecoration::Menu) {
176         QPoint pos = (QApplication::layoutDirection() == Qt::LeftToRight
177                       ? d->managed->geometry().topLeft()
178                       : d->managed->geometry().topRight());
179         menu(pos);
180     }
181     if (d->activeRegion != QDecoration::None &&
182          d->activeRegion != QDecoration::Menu) {
183         d->active = d->managed;
184         d->managed->grabMouse();
185     }
186     if (d->activeRegion != QDecoration::None &&
187          d->activeRegion != QDecoration::Close &&
188          d->activeRegion != QDecoration::Minimize &&
189          d->activeRegion != QDecoration::Menu) {
190         d->managed->raise();
191     }
192 
193     if (e->button() == Qt::RightButton) {
194         menu(e->globalPos());
195     }
196 }
197 
mouseReleaseEvent(QMouseEvent * e)198 void QWSManager::mouseReleaseEvent(QMouseEvent *e)
199 {
200     Q_D(QWSManager);
201     d->managed->releaseMouse();
202     if (d->cached_region.regionType && d->previousRegionRepainted && QApplication::mouseButtons() == 0) {
203         bool doesHover = repaintRegion(d->cached_region.regionType, QDecoration::Hover);
204         if (!doesHover) {
205             repaintRegion(d->cached_region.regionType, QDecoration::Normal);
206             d->previousRegionRepainted = false;
207         }
208     }
209 
210     if (e->button() == Qt::LeftButton) {
211         //handleMove();
212         int itm = QApplication::qwsDecoration().regionAt(d->managed, e->globalPos());
213         int activatedItem = d->activeRegion;
214         d->activeRegion = QDecoration::None;
215         d->active = 0;
216         if (activatedItem == itm)
217             QApplication::qwsDecoration().regionClicked(d->managed, itm);
218     } else if (d->activeRegion == QDecoration::None) {
219         d->active = 0;
220     }
221 }
222 
mouseDoubleClickEvent(QMouseEvent * e)223 void QWSManager::mouseDoubleClickEvent(QMouseEvent *e)
224 {
225     Q_D(QWSManager);
226     if (e->button() == Qt::LeftButton)
227         QApplication::qwsDecoration().regionDoubleClicked(d->managed,
228             QApplication::qwsDecoration().regionAt(d->managed, e->globalPos()));
229 }
230 
regionToShape(int region)231 static inline Qt::CursorShape regionToShape(int region)
232 {
233     if (region == QDecoration::None)
234         return Qt::ArrowCursor;
235 
236     static const struct {
237         int region;
238         Qt::CursorShape shape;
239     } r2s[] = {
240         { QDecoration::TopLeft,     Qt::SizeFDiagCursor },
241         { QDecoration::Top,         Qt::SizeVerCursor},
242         { QDecoration::TopRight,    Qt::SizeBDiagCursor},
243         { QDecoration::Left,        Qt::SizeHorCursor},
244         { QDecoration::Right,       Qt::SizeHorCursor},
245         { QDecoration::BottomLeft,  Qt::SizeBDiagCursor},
246         { QDecoration::Bottom,      Qt::SizeVerCursor},
247         { QDecoration::BottomRight, Qt::SizeFDiagCursor},
248         { QDecoration::None,        Qt::ArrowCursor}
249     };
250 
251     int i = 0;
252     while (region != r2s[i].region && r2s[i].region)
253         ++i;
254     return r2s[i].shape;
255 }
256 
mouseMoveEvent(QMouseEvent * e)257 void QWSManager::mouseMoveEvent(QMouseEvent *e)
258 {
259     Q_D(QWSManager);
260     if (d->newCachedRegion(e->globalPos())) {
261         if(d->previousRegionType && d->previousRegionRepainted)
262             repaintRegion(d->previousRegionType, QDecoration::Normal);
263         if(d->cached_region.regionType) {
264             d->previousRegionRepainted = repaintRegion(d->cached_region.regionType, QDecoration::Hover);
265         }
266     }
267 
268 
269 #ifndef QT_NO_CURSOR
270     if (d->managed->minimumSize() != d->managed->maximumSize()) {
271         QWSDisplay *qwsd = QApplication::desktop()->qwsDisplay();
272         qwsd->selectCursor(d->managed, regionToShape(d->cachedRegionAt()));
273     }
274 #endif //QT_NO_CURSOR
275 
276     if (d->activeRegion)
277         handleMove(e->globalPos());
278 }
279 
handleMove(QPoint g)280 void QWSManager::handleMove(QPoint g)
281 {
282     Q_D(QWSManager);
283 
284     // don't allow dragging to where the user probably cannot click!
285     QApplicationPrivate *ap = QApplicationPrivate::instance();
286     const QRect maxWindowRect = ap->maxWindowRect(qt_screen);
287     if (maxWindowRect.isValid()) {
288         if (g.x() < maxWindowRect.x())
289             g.setX(maxWindowRect.x());
290         if (g.y() < maxWindowRect.y())
291             g.setY(maxWindowRect.y());
292         if (g.x() > maxWindowRect.right())
293             g.setX(maxWindowRect.right());
294         if (g.y() > maxWindowRect.bottom())
295             g.setY(maxWindowRect.bottom());
296     }
297 
298     if (g == d->mousePos)
299         return;
300 
301     if ( d->managed->isMaximized() )
302         return;
303 
304     int x = d->managed->geometry().x();
305     int y = d->managed->geometry().y();
306     int w = d->managed->width();
307     int h = d->managed->height();
308 
309     QRect geom(d->managed->geometry());
310 
311     QPoint delta = g - d->mousePos;
312     d->mousePos = g;
313 
314     if (d->activeRegion == QDecoration::Title) {
315         geom = QRect(x + delta.x(), y + delta.y(), w, h);
316     } else {
317         bool keepTop = true;
318         bool keepLeft = true;
319         switch (d->activeRegion) {
320         case QDecoration::Top:
321             geom.setTop(geom.top() + delta.y());
322             keepTop = false;
323             break;
324         case QDecoration::Bottom:
325             geom.setBottom(geom.bottom() + delta.y());
326             keepTop = true;
327             break;
328         case QDecoration::Left:
329             geom.setLeft(geom.left() + delta.x());
330             keepLeft = false;
331             break;
332         case QDecoration::Right:
333             geom.setRight(geom.right() + delta.x());
334             keepLeft = true;
335             break;
336         case QDecoration::TopRight:
337             geom.setTopRight(geom.topRight() + delta);
338             keepLeft = true;
339             keepTop = false;
340             break;
341         case QDecoration::TopLeft:
342             geom.setTopLeft(geom.topLeft() + delta);
343             keepLeft = false;
344             keepTop = false;
345             break;
346         case QDecoration::BottomLeft:
347             geom.setBottomLeft(geom.bottomLeft() + delta);
348             keepLeft = false;
349             keepTop = true;
350             break;
351         case QDecoration::BottomRight:
352             geom.setBottomRight(geom.bottomRight() + delta);
353             keepLeft = true;
354             keepTop = true;
355             break;
356         default:
357             return;
358         }
359 
360         QSize newSize = QLayout::closestAcceptableSize(d->managed, geom.size());
361 
362         int dx = newSize.width() - geom.width();
363         int dy = newSize.height() - geom.height();
364 
365         if (keepTop) {
366             geom.setBottom(geom.bottom() + dy);
367             d->mousePos.ry() += dy;
368         } else {
369             geom.setTop(geom.top() - dy);
370             d->mousePos.ry() -= dy;
371         }
372         if (keepLeft) {
373             geom.setRight(geom.right() + dx);
374             d->mousePos.rx() += dx;
375         } else {
376             geom.setLeft(geom.left() - dx);
377             d->mousePos.rx() -= dx;
378         }
379     }
380     if (geom != d->managed->geometry()) {
381         QApplication::sendPostedEvents();
382         d->managed->setGeometry(geom);
383     }
384 }
385 
paintEvent(QPaintEvent *)386 void QWSManager::paintEvent(QPaintEvent *)
387 {
388      Q_D(QWSManager);
389      d->dirtyRegion(QDecoration::All, QDecoration::Normal);
390 }
391 
dirtyRegion(int decorationRegion,QDecoration::DecorationState state,const QRegion & clip)392 void QWSManagerPrivate::dirtyRegion(int decorationRegion,
393                                     QDecoration::DecorationState state,
394                                     const QRegion &clip)
395 {
396     QTLWExtra *topextra = managed->d_func()->extra->topextra;
397     QWidgetBackingStore *bs = topextra->backingStore.data();
398     const bool pendingUpdateRequest = bs->isDirty();
399 
400     if (decorationRegion == QDecoration::All) {
401         if (clip.isEmpty())
402             entireDecorationNeedsRepaint = true;
403         dirtyRegions.clear();
404         dirtyStates.clear();
405     }
406     int i = dirtyRegions.indexOf(decorationRegion);
407     if (i >= 0) {
408         dirtyRegions.removeAt(i);
409         dirtyStates.removeAt(i);
410     }
411 
412     dirtyRegions.append(decorationRegion);
413     dirtyStates.append(state);
414     if (!entireDecorationNeedsRepaint)
415         dirtyClip += clip;
416 
417     if (!pendingUpdateRequest)
418         QApplication::postEvent(managed, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority);
419 }
420 
clearDirtyRegions()421 void QWSManagerPrivate::clearDirtyRegions()
422 {
423     dirtyRegions.clear();
424     dirtyStates.clear();
425     dirtyClip = QRegion();
426     entireDecorationNeedsRepaint = false;
427 }
428 
repaintRegion(int decorationRegion,QDecoration::DecorationState state)429 bool QWSManager::repaintRegion(int decorationRegion, QDecoration::DecorationState state)
430 {
431     Q_D(QWSManager);
432 
433     d->dirtyRegion(decorationRegion, state);
434     return true;
435 }
436 
menu(const QPoint & pos)437 void QWSManager::menu(const QPoint &pos)
438 {
439 #ifdef QT_NO_MENU
440     Q_UNUSED(pos);
441 #else
442     Q_D(QWSManager);
443     if (d->popup)
444         delete d->popup;
445 
446     // Basic window operation menu
447     d->popup = new QMenu();
448     QApplication::qwsDecoration().buildSysMenu(d->managed, d->popup);
449     connect(d->popup, SIGNAL(triggered(QAction*)), SLOT(menuTriggered(QAction*)));
450 
451     d->popup->popup(pos);
452     d->activeRegion = QDecoration::None;
453 #endif // QT_NO_MENU
454 }
455 
menuTriggered(QAction * action)456 void QWSManager::menuTriggered(QAction *action)
457 {
458 #ifdef QT_NO_MENU
459     Q_UNUSED(action);
460 #else
461     Q_D(QWSManager);
462     QApplication::qwsDecoration().menuTriggered(d->managed, action);
463     d->popup->deleteLater();
464     d->popup = 0;
465 #endif
466 }
467 
startMove()468 void QWSManager::startMove()
469 {
470     Q_D(QWSManager);
471     d->mousePos = QCursor::pos();
472     d->activeRegion = QDecoration::Title;
473     d->active = d->managed;
474     d->managed->grabMouse();
475 }
476 
startResize()477 void QWSManager::startResize()
478 {
479     Q_D(QWSManager);
480     d->activeRegion = QDecoration::BottomRight;
481     d->active = d->managed;
482     d->managed->grabMouse();
483 }
484 
maximize()485 void QWSManager::maximize()
486 {
487     Q_D(QWSManager);
488     // find out how much space the decoration needs
489     const int screen = QApplication::desktop()->screenNumber(d->managed);
490     const QRect desk = QApplication::desktop()->availableGeometry(screen);
491     QRect dummy(0, 0, 1, 1);
492     QRect nr;
493     QRegion r = QApplication::qwsDecoration().region(d->managed, dummy);
494     if (r.isEmpty()) {
495         nr = desk;
496     } else {
497         r += dummy; // make sure we get the full window region in case of 0 width borders
498         QRect rect = r.boundingRect();
499         nr = QRect(desk.x()-rect.x(), desk.y()-rect.y(),
500                 desk.width() - (rect.width()==1 ? 0 : rect.width()-1), // ==1 -> dummy
501                 desk.height() - (rect.height()==1 ? 0 : rect.height()-1));
502     }
503     d->managed->setGeometry(nr);
504 }
505 
newCachedRegion(const QPoint & pos)506 bool QWSManagerPrivate::newCachedRegion(const QPoint &pos)
507 {
508     // Check if anything has changed that would affect the region caching
509     if (managed->windowFlags() == cached_region.windowFlags
510         && managed->geometry() == cached_region.windowGeometry
511         && cached_region.region.contains(pos))
512         return false;
513 
514     // Update the cached region
515     int reg = QApplication::qwsDecoration().regionAt(managed, pos);
516     if (QWidget::mouseGrabber())
517         reg = QDecoration::None;
518 
519     previousRegionType = cached_region.regionType;
520     cached_region.regionType = reg;
521     cached_region.region = QApplication::qwsDecoration().region(managed, managed->geometry(),
522                                                                 reg);
523     // Make room for borders around the widget, even if the decoration doesn't have a frame.
524     if (reg && !(reg & int(QDecoration::Borders))) {
525         cached_region.region -= QApplication::qwsDecoration().region(managed, managed->geometry(), QDecoration::Borders);
526     }
527     cached_region.windowFlags = managed->windowFlags();
528     cached_region.windowGeometry = managed->geometry();
529 //    QRect rec = cached_region.region.boundingRect();
530 //    qDebug("Updated cached region: 0x%04x (%d, %d)  (%d, %d,  %d, %d)",
531 //           reg, pos.x(), pos.y(), rec.x(), rec.y(), rec.right(), rec.bottom());
532     return true;
533 }
534 
535 QT_END_NAMESPACE
536 
537 #endif //QT_NO_QWS_MANAGER
538