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