1/****************************************************************************
2**
3** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4** Contact: http://www.qt-project.org/legal
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 Digia. For licensing terms and
14** conditions see http://qt.digia.com/licensing. For further information
15** use the contact form at http://qt.digia.com/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 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 2.1 requirements
23** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24**
25** In addition, as a special exception, Digia gives you certain additional
26** rights. These rights are described in the Digia Qt LGPL Exception
27** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28**
29** GNU General Public License Usage
30** Alternatively, this file may be used under the terms of the GNU
31** General Public License version 3.0 as published by the Free Software
32** Foundation and appearing in the file LICENSE.GPL included in the
33** packaging of this file. Please review the following information to
34** ensure the GNU General Public License version 3.0 requirements will be
35** met: http://www.gnu.org/copyleft/gpl.html.
36**
37**
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qdockwidget.h"
43
44#ifndef QT_NO_DOCKWIDGET
45#include <qaction.h>
46#include <qapplication.h>
47#include <qdesktopwidget.h>
48#include <qdrawutil.h>
49#include <qevent.h>
50#include <qfontmetrics.h>
51#include <qmainwindow.h>
52#include <qrubberband.h>
53#include <qstylepainter.h>
54#include <qtoolbutton.h>
55#include <qdebug.h>
56
57#include <private/qwidgetresizehandler_p.h>
58
59#include "qdockwidget_p.h"
60#include "qmainwindowlayout_p.h"
61#ifdef Q_WS_MAC
62#include <private/qapplication_p.h>
63#include <private/qt_mac_p.h>
64#include <qmacstyle_mac.h>
65#endif
66
67QT_BEGIN_NAMESPACE
68
69extern QString qt_setWindowTitle_helperHelper(const QString&, const QWidget*); // qwidget.cpp
70
71// qmainwindow.cpp
72extern QMainWindowLayout *qt_mainwindow_layout(const QMainWindow *window);
73
74static inline bool hasFeature(const QDockWidgetPrivate *priv, QDockWidget::DockWidgetFeature feature)
75{ return (priv->features & feature) == feature; }
76
77static inline bool hasFeature(const QDockWidget *dockwidget, QDockWidget::DockWidgetFeature feature)
78{ return (dockwidget->features() & feature) == feature; }
79
80
81/*
82 A Dock Window:
83
84 [+] is the float button
85 [X] is the close button
86
87 +-------------------------------+
88 | Dock Window Title [+][X]|
89 +-------------------------------+
90 | |
91 | place to put the single |
92 | QDockWidget child (this space |
93 | does not yet have a name) |
94 | |
95 | |
96 | |
97 | |
98 | |
99 | |
100 | |
101 | |
102 | |
103 +-------------------------------+
104
105*/
106
107/******************************************************************************
108** QDockWidgetTitleButton
109*/
110
111class QDockWidgetTitleButton : public QAbstractButton
112{
113 Q_OBJECT
114
115public:
116 QDockWidgetTitleButton(QDockWidget *dockWidget);
117
118 QSize sizeHint() const;
119 inline QSize minimumSizeHint() const
120 { return sizeHint(); }
121
122 void enterEvent(QEvent *event);
123 void leaveEvent(QEvent *event);
124 void paintEvent(QPaintEvent *event);
125};
126
127
128QDockWidgetTitleButton::QDockWidgetTitleButton(QDockWidget *dockWidget)
129 : QAbstractButton(dockWidget)
130{
131 setFocusPolicy(Qt::NoFocus);
132}
133
134QSize QDockWidgetTitleButton::sizeHint() const
135{
136 ensurePolished();
137
138 int size = 2*style()->pixelMetric(QStyle::PM_DockWidgetTitleBarButtonMargin, 0, this);
139 if (!icon().isNull()) {
140 int iconSize = style()->pixelMetric(QStyle::PM_SmallIconSize, 0, this);
141 QSize sz = icon().actualSize(QSize(iconSize, iconSize));
142 size += qMax(sz.width(), sz.height());
143 }
144
145 return QSize(size, size);
146}
147
148void QDockWidgetTitleButton::enterEvent(QEvent *event)
149{
150 if (isEnabled()) update();
151 QAbstractButton::enterEvent(event);
152}
153
154void QDockWidgetTitleButton::leaveEvent(QEvent *event)
155{
156 if (isEnabled()) update();
157 QAbstractButton::leaveEvent(event);
158}
159
160void QDockWidgetTitleButton::paintEvent(QPaintEvent *)
161{
162 QPainter p(this);
163
164 QStyleOptionToolButton opt;
165 opt.init(this);
166 opt.state |= QStyle::State_AutoRaise;
167
168 if (style()->styleHint(QStyle::SH_DockWidget_ButtonsHaveFrame, 0, this))
169 {
170 if (isEnabled() && underMouse() && !isChecked() && !isDown())
171 opt.state |= QStyle::State_Raised;
172 if (isChecked())
173 opt.state |= QStyle::State_On;
174 if (isDown())
175 opt.state |= QStyle::State_Sunken;
176 style()->drawPrimitive(QStyle::PE_PanelButtonTool, &opt, &p, this);
177 }
178
179 opt.icon = icon();
180 opt.subControls = 0;
181 opt.activeSubControls = 0;
182 opt.features = QStyleOptionToolButton::None;
183 opt.arrowType = Qt::NoArrow;
184 int size = style()->pixelMetric(QStyle::PM_SmallIconSize, 0, this);
185 opt.iconSize = QSize(size, size);
186 style()->drawComplexControl(QStyle::CC_ToolButton, &opt, &p, this);
187}
188
189/******************************************************************************
190** QDockWidgetLayout
191*/
192
193QDockWidgetLayout::QDockWidgetLayout(QWidget *parent)
194 : QLayout(parent), verticalTitleBar(false), item_list(RoleCount, 0)
195{
196}
197
198QDockWidgetLayout::~QDockWidgetLayout()
199{
200 qDeleteAll(item_list);
201}
202
203bool QDockWidgetLayout::nativeWindowDeco() const
204{
205 return nativeWindowDeco(parentWidget()->isWindow());
206}
207
208bool QDockWidgetLayout::nativeWindowDeco(bool floating) const
209{
210#if defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_WS_WINCE)
211 Q_UNUSED(floating);
212 return false;
213#else
214 return floating && item_list[QDockWidgetLayout::TitleBar] == 0;
215#endif
216}
217
218
219void QDockWidgetLayout::addItem(QLayoutItem*)
220{
221 qWarning() << "QDockWidgetLayout::addItem(): please use QDockWidgetLayout::setWidget()";
222 return;
223}
224
225QLayoutItem *QDockWidgetLayout::itemAt(int index) const
226{
227 int cnt = 0;
228 for (int i = 0; i < item_list.count(); ++i) {
229 QLayoutItem *item = item_list.at(i);
230 if (item == 0)
231 continue;
232 if (index == cnt++)
233 return item;
234 }
235 return 0;
236}
237
238QLayoutItem *QDockWidgetLayout::takeAt(int index)
239{
240 int j = 0;
241 for (int i = 0; i < item_list.count(); ++i) {
242 QLayoutItem *item = item_list.at(i);
243 if (item == 0)
244 continue;
245 if (index == j) {
246 item_list[i] = 0;
247 invalidate();
248 return item;
249 }
250 ++j;
251 }
252 return 0;
253}
254
255int QDockWidgetLayout::count() const
256{
257 int result = 0;
258 for (int i = 0; i < item_list.count(); ++i) {
259 if (item_list.at(i))
260 ++result;
261 }
262 return result;
263}
264
265QSize QDockWidgetLayout::sizeFromContent(const QSize &content, bool floating) const
266{
267 QSize result = content;
268
269 if (verticalTitleBar) {
270 result.setHeight(qMax(result.height(), minimumTitleWidth()));
271 result.setWidth(qMax(content.width(), 0));
272 } else {
273 result.setHeight(qMax(result.height(), 0));
274 result.setWidth(qMax(content.width(), minimumTitleWidth()));
275 }
276
277 QDockWidget *w = qobject_cast<QDockWidget*>(parentWidget());
278 const bool nativeDeco = nativeWindowDeco(floating);
279
280 int fw = floating && !nativeDeco
281 ? w->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, w)
282 : 0;
283
284 const int th = titleHeight();
285 if (!nativeDeco) {
286 if (verticalTitleBar)
287 result += QSize(th + 2*fw, 2*fw);
288 else
289 result += QSize(2*fw, th + 2*fw);
290 }
291
292 result.setHeight(qMin(result.height(), (int) QWIDGETSIZE_MAX));
293 result.setWidth(qMin(result.width(), (int) QWIDGETSIZE_MAX));
294
295 if (content.width() < 0)
296 result.setWidth(-1);
297 if (content.height() < 0)
298 result.setHeight(-1);
299
300 int left, top, right, bottom;
301 w->getContentsMargins(&left, &top, &right, &bottom);
302 //we need to substract the contents margin (it will be added by the caller)
303 QSize min = w->minimumSize() - QSize(left + right, top + bottom);
304 QSize max = w->maximumSize() - QSize(left + right, top + bottom);
305
306 /* A floating dockwidget will automatically get its minimumSize set to the layout's
307 minimum size + deco. We're *not* interested in this, we only take minimumSize()
308 into account if the user set it herself. Otherwise we end up expanding the result
309 of a calculation for a non-floating dock widget to a floating dock widget's
310 minimum size + window decorations. */
311
312 uint explicitMin = 0;
313 uint explicitMax = 0;
314 if (w->d_func()->extra != 0) {
315 explicitMin = w->d_func()->extra->explicitMinSize;
316 explicitMax = w->d_func()->extra->explicitMaxSize;
317 }
318
319 if (!(explicitMin & Qt::Horizontal) || min.width() == 0)
320 min.setWidth(-1);
321 if (!(explicitMin & Qt::Vertical) || min.height() == 0)
322 min.setHeight(-1);
323
324 if (!(explicitMax & Qt::Horizontal))
325 max.setWidth(QWIDGETSIZE_MAX);
326 if (!(explicitMax & Qt::Vertical))
327 max.setHeight(QWIDGETSIZE_MAX);
328
329 return result.boundedTo(max).expandedTo(min);
330}
331
332QSize QDockWidgetLayout::sizeHint() const
333{
334 QDockWidget *w = qobject_cast<QDockWidget*>(parentWidget());
335
336 QSize content(-1, -1);
337 if (item_list[Content] != 0)
338 content = item_list[Content]->sizeHint();
339
340 return sizeFromContent(content, w->isFloating());
341}
342
343QSize QDockWidgetLayout::maximumSize() const
344{
345 if (item_list[Content] != 0) {
346 const QSize content = item_list[Content]->maximumSize();
347 return sizeFromContent(content, parentWidget()->isWindow());
348 } else {
349 return parentWidget()->maximumSize();
350 }
351
352}
353
354QSize QDockWidgetLayout::minimumSize() const
355{
356 QDockWidget *w = qobject_cast<QDockWidget*>(parentWidget());
357
358 QSize content(0, 0);
359 if (item_list[Content] != 0)
360 content = item_list[Content]->minimumSize();
361
362 return sizeFromContent(content, w->isFloating());
363}
364
365QWidget *QDockWidgetLayout::widgetForRole(Role r) const
366{
367 QLayoutItem *item = item_list.at(r);
368 return item == 0 ? 0 : item->widget();
369}
370
371QLayoutItem *QDockWidgetLayout::itemForRole(Role r) const
372{
373 return item_list.at(r);
374}
375
376void QDockWidgetLayout::setWidgetForRole(Role r, QWidget *w)
377{
378 QWidget *old = widgetForRole(r);
379 if (old != 0) {
380 old->hide();
381 removeWidget(old);
382 }
383
384 if (w != 0) {
385 addChildWidget(w);
386 item_list[r] = new QWidgetItemV2(w);
387 w->show();
388 } else {
389 item_list[r] = 0;
390 }
391
392 invalidate();
393}
394
395static inline int pick(bool vertical, const QSize &size)
396{
397 return vertical ? size.height() : size.width();
398}
399
400static inline int perp(bool vertical, const QSize &size)
401{
402 return vertical ? size.width() : size.height();
403}
404
405int QDockWidgetLayout::minimumTitleWidth() const
406{
407 QDockWidget *q = qobject_cast<QDockWidget*>(parentWidget());
408
409 if (QWidget *title = widgetForRole(TitleBar))
410 return pick(verticalTitleBar, title->minimumSizeHint());
411
412 QSize closeSize(0, 0);
413 QSize floatSize(0, 0);
414 if (hasFeature(q, QDockWidget::DockWidgetClosable)) {
415 if (QLayoutItem *item = item_list[CloseButton])
416 closeSize = item->widget()->sizeHint();
417 }
418 if (hasFeature(q, QDockWidget::DockWidgetFloatable)) {
419 if (QLayoutItem *item = item_list[FloatButton])
420 floatSize = item->widget()->sizeHint();
421 }
422
423 int titleHeight = this->titleHeight();
424
425 int mw = q->style()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, 0, q);
426 int fw = q->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, q);
427
428 return pick(verticalTitleBar, closeSize)
429 + pick(verticalTitleBar, floatSize)
430 + titleHeight + 2*fw + 3*mw;
431}
432
433int QDockWidgetLayout::titleHeight() const
434{
435 QDockWidget *q = qobject_cast<QDockWidget*>(parentWidget());
436
437 if (QWidget *title = widgetForRole(TitleBar))
438 return perp(verticalTitleBar, title->sizeHint());
439
440 QSize closeSize(0, 0);
441 QSize floatSize(0, 0);
442 if (QLayoutItem *item = item_list[CloseButton])
443 closeSize = item->widget()->sizeHint();
444 if (QLayoutItem *item = item_list[FloatButton])
445 floatSize = item->widget()->sizeHint();
446
447 int buttonHeight = qMax(perp(verticalTitleBar, closeSize),
448 perp(verticalTitleBar, floatSize));
449
450 QFontMetrics titleFontMetrics = q->fontMetrics();
451#ifdef Q_WS_MAC
452 if (qobject_cast<QMacStyle *>(q->style())) {
453 //### this breaks on proxy styles. (But is this code still called?)
454 QFont font = qt_app_fonts_hash()->value("QToolButton", q->font());
455 titleFontMetrics = QFontMetrics(font);
456 }
457#endif
458
459 int mw = q->style()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, 0, q);
460
461 return qMax(buttonHeight + 2, titleFontMetrics.height() + 2*mw);
462}
463
464void QDockWidgetLayout::setGeometry(const QRect &geometry)
465{
466 QDockWidget *q = qobject_cast<QDockWidget*>(parentWidget());
467
468 bool nativeDeco = nativeWindowDeco();
469
470 int fw = q->isFloating() && !nativeDeco
471 ? q->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, q)
472 : 0;
473
474 if (nativeDeco) {
475 if (QLayoutItem *item = item_list[Content])
476 item->setGeometry(geometry);
477 } else {
478 int titleHeight = this->titleHeight();
479
480 if (verticalTitleBar) {
481 _titleArea = QRect(QPoint(fw, fw),
482 QSize(titleHeight, geometry.height() - (fw * 2)));
483 } else {
484 _titleArea = QRect(QPoint(fw, fw),
485 QSize(geometry.width() - (fw * 2), titleHeight));
486 }
487
488 if (QLayoutItem *item = item_list[TitleBar]) {
489 item->setGeometry(_titleArea);
490 } else {
491 QStyleOptionDockWidgetV2 opt;
492 q->initStyleOption(&opt);
493
494 if (QLayoutItem *item = item_list[CloseButton]) {
495 if (!item->isEmpty()) {
496 QRect r = q->style()
497 ->subElementRect(QStyle::SE_DockWidgetCloseButton,
498 &opt, q);
499 if (!r.isNull())
500 item->setGeometry(r);
501 }
502 }
503
504 if (QLayoutItem *item = item_list[FloatButton]) {
505 if (!item->isEmpty()) {
506 QRect r = q->style()
507 ->subElementRect(QStyle::SE_DockWidgetFloatButton,
508 &opt, q);
509 if (!r.isNull())
510 item->setGeometry(r);
511 }
512 }
513 }
514
515 if (QLayoutItem *item = item_list[Content]) {
516 QRect r = geometry;
517 if (verticalTitleBar) {
518 r.setLeft(_titleArea.right() + 1);
519 r.adjust(0, fw, -fw, -fw);
520 } else {
521 r.setTop(_titleArea.bottom() + 1);
522 r.adjust(fw, 0, -fw, -fw);
523 }
524 item->setGeometry(r);
525 }
526 }
527}
528
529void QDockWidgetLayout::setVerticalTitleBar(bool b)
530{
531 if (b == verticalTitleBar)
532 return;
533 verticalTitleBar = b;
534 invalidate();
535 parentWidget()->update();
536}
537
538/******************************************************************************
539** QDockWidgetItem
540*/
541
542QDockWidgetItem::QDockWidgetItem(QDockWidget *dockWidget)
543 : QWidgetItem(dockWidget)
544{
545}
546
547QSize QDockWidgetItem::minimumSize() const
548{
549 QSize widgetMin(0, 0);
550 if (QLayoutItem *item = dockWidgetChildItem())
551 widgetMin = item->minimumSize();
552 return dockWidgetLayout()->sizeFromContent(widgetMin, false);
553}
554
555QSize QDockWidgetItem::maximumSize() const
556{
557 if (QLayoutItem *item = dockWidgetChildItem()) {
558 return dockWidgetLayout()->sizeFromContent(item->maximumSize(), false);
559 } else {
560 return QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
561 }
562}
563
564
565QSize QDockWidgetItem::sizeHint() const
566{
567 if (QLayoutItem *item = dockWidgetChildItem()) {
568 return dockWidgetLayout()->sizeFromContent(item->sizeHint(), false);
569 } else {
570 return QWidgetItem::sizeHint();
571 }
572}
573
574/******************************************************************************
575** QDockWidgetPrivate
576*/
577
578void QDockWidgetPrivate::init()
579{
580 Q_Q(QDockWidget);
581
582 QDockWidgetLayout *layout = new QDockWidgetLayout(q);
583 layout->setSizeConstraint(QLayout::SetMinAndMaxSize);
584
585 QAbstractButton *button = new QDockWidgetTitleButton(q);
586 button->setObjectName(QLatin1String("qt_dockwidget_floatbutton"));
587 QObject::connect(button, SIGNAL(clicked()), q, SLOT(_q_toggleTopLevel()));
588 layout->setWidgetForRole(QDockWidgetLayout::FloatButton, button);
589
590 button = new QDockWidgetTitleButton(q);
591 button->setObjectName(QLatin1String("qt_dockwidget_closebutton"));
592 QObject::connect(button, SIGNAL(clicked()), q, SLOT(close()));
593 layout->setWidgetForRole(QDockWidgetLayout::CloseButton, button);
594
595 resizer = new QWidgetResizeHandler(q);
596 resizer->setMovingEnabled(false);
597 resizer->setActive(false);
598
599#ifndef QT_NO_ACTION
600 toggleViewAction = new QAction(q);
601 toggleViewAction->setCheckable(true);
602 fixedWindowTitle = qt_setWindowTitle_helperHelper(q->windowTitle(), q);
603 toggleViewAction->setText(fixedWindowTitle);
604 QObject::connect(toggleViewAction, SIGNAL(triggered(bool)),
605 q, SLOT(_q_toggleView(bool)));
606#endif
607
608 updateButtons();
609}
610
611/*!
612 Initialize \a option with the values from this QDockWidget. This method
613 is useful for subclasses when they need a QStyleOptionDockWidget, but don't want
614 to fill in all the information themselves.
615
616 \sa QStyleOption::initFrom()
617*/
618void QDockWidget::initStyleOption(QStyleOptionDockWidget *option) const
619{
620 Q_D(const QDockWidget);
621
622 if (!option)
623 return;
624 QDockWidgetLayout *dwlayout = qobject_cast<QDockWidgetLayout*>(layout());
625
626 option->initFrom(this);
627 option->rect = dwlayout->titleArea();
628 option->title = d->fixedWindowTitle;
629 option->closable = hasFeature(this, QDockWidget::DockWidgetClosable);
630 option->movable = hasFeature(this, QDockWidget::DockWidgetMovable);
631 option->floatable = hasFeature(this, QDockWidget::DockWidgetFloatable);
632
633 QDockWidgetLayout *l = qobject_cast<QDockWidgetLayout*>(layout());
634 QStyleOptionDockWidgetV2 *v2
635 = qstyleoption_cast<QStyleOptionDockWidgetV2*>(option);
636 if (v2 != 0)
637 v2->verticalTitleBar = l->verticalTitleBar;
638}
639
640void QDockWidgetPrivate::_q_toggleView(bool b)
641{
642 Q_Q(QDockWidget);
643 if (b == q->isHidden()) {
644 if (b)
645 q->show();
646 else
647 q->close();
648 }
649}
650
651void QDockWidgetPrivate::updateButtons()
652{
653 Q_Q(QDockWidget);
654 QDockWidgetLayout *dwLayout = qobject_cast<QDockWidgetLayout*>(layout);
655
656 QStyleOptionDockWidget opt;
657 q->initStyleOption(&opt);
658
659 bool customTitleBar = dwLayout->widgetForRole(QDockWidgetLayout::TitleBar) != 0;
660 bool nativeDeco = dwLayout->nativeWindowDeco();
661 bool hideButtons = nativeDeco || customTitleBar;
662
663 bool canClose = hasFeature(this, QDockWidget::DockWidgetClosable);
664 bool canFloat = hasFeature(this, QDockWidget::DockWidgetFloatable);
665
666 QAbstractButton *button
667 = qobject_cast<QAbstractButton*>(dwLayout->widgetForRole(QDockWidgetLayout::FloatButton));
668 button->setIcon(q->style()->standardIcon(QStyle::SP_TitleBarNormalButton, &opt, q));
669 button->setVisible(canFloat && !hideButtons);
670
671 button
672 = qobject_cast <QAbstractButton*>(dwLayout->widgetForRole(QDockWidgetLayout::CloseButton));
673 button->setIcon(q->style()->standardIcon(QStyle::SP_TitleBarCloseButton, &opt, q));
674 button->setVisible(canClose && !hideButtons);
675
676 q->setAttribute(Qt::WA_ContentsPropagated,
677 (canFloat || canClose) && !hideButtons);
678
679 layout->invalidate();
680}
681
682void QDockWidgetPrivate::_q_toggleTopLevel()
683{
684 Q_Q(QDockWidget);
685 q->setFloating(!q->isFloating());
686}
687
688void QDockWidgetPrivate::initDrag(const QPoint &pos, bool nca)
689{
690 if (state != 0)
691 return;
692
693 Q_Q(QDockWidget);
694 QMainWindow *win = qobject_cast<QMainWindow*>(parent);
695 Q_ASSERT(win != 0);
696 QMainWindowLayout *layout = qt_mainwindow_layout(win);
697 Q_ASSERT(layout != 0);
698 if (layout->pluggingWidget != 0) // the main window is animating a docking operation
699 return;
700
701 state = new QDockWidgetPrivate::DragState;
702 state->dragging = false;
703 state->widgetItem = 0;
704 state->ownWidgetItem = false;
705 state->nca = nca;
706 state->ctrlDrag = false;
707
708 if (!q->isFloating()) {
709 // When dragging the widget out of the docking area,
710 // use the middle of title area as pressPos
711 QDockWidgetLayout *dwlayout = qobject_cast<QDockWidgetLayout*>(q->layout());
712 Q_ASSERT(dwlayout != 0);
713 int width = undockedGeometry.isNull() ? q->width() : undockedGeometry.width();
714 state->pressPos.setY(dwlayout->titleArea().height() / 2);
715 state->pressPos.setX(width / 2);
716 } else {
717 state->pressPos = pos;
718 }
719}
720
721void QDockWidgetPrivate::startDrag()
722{
723 Q_Q(QDockWidget);
724
725 if (state == 0 || state->dragging)
726 return;
727
728 QMainWindowLayout *layout = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q->parentWidget()));
729 Q_ASSERT(layout != 0);
730
731 state->widgetItem = layout->unplug(q);
732 if (state->widgetItem == 0) {
733 /* I have a QMainWindow parent, but I was never inserted with
734 QMainWindow::addDockWidget, so the QMainWindowLayout has no
735 widget item for me. :( I have to create it myself, and then
736 delete it if I don't get dropped into a dock area. */
737 state->widgetItem = new QDockWidgetItem(q);
738 state->ownWidgetItem = true;
739 }
740
741 if (state->ctrlDrag)
742 layout->restore();
743
744 state->dragging = true;
745}
746
747void QDockWidgetPrivate::endDrag(bool abort)
748{
749 Q_Q(QDockWidget);
750 Q_ASSERT(state != 0);
751
752 q->releaseMouse();
753
754 if (state->dragging) {
755 QMainWindowLayout *mwLayout = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q->parentWidget()));
756 Q_ASSERT(mwLayout != 0);
757
758 if (abort || !mwLayout->plug(state->widgetItem)) {
759 if (hasFeature(this, QDockWidget::DockWidgetFloatable)) {
760 if (state->ownWidgetItem)
761 delete state->widgetItem;
762 mwLayout->restore();
763#ifdef Q_WS_X11
764 // get rid of the X11BypassWindowManager window flag and activate the resizer
765 Qt::WindowFlags flags = q->windowFlags();
766 flags &= ~Qt::X11BypassWindowManagerHint;
767 q->setWindowFlags(flags);
768 resizer->setActive(QWidgetResizeHandler::Resize, true);
769 q->show();
770#else
771 QDockWidgetLayout *myLayout
772 = qobject_cast<QDockWidgetLayout*>(layout);
773 resizer->setActive(QWidgetResizeHandler::Resize,
774 myLayout->widgetForRole(QDockWidgetLayout::TitleBar) != 0);
775#endif
776 undockedGeometry = q->geometry();
777 q->activateWindow();
778 } else {
779 mwLayout->revert(state->widgetItem);
780 }
781 }
782 }
783 delete state;
784 state = 0;
785}
786
787bool QDockWidgetPrivate::isAnimating() const
788{
789 Q_Q(const QDockWidget);
790
791 QMainWindow *mainWin = qobject_cast<QMainWindow*>(parent);
792 if (mainWin == 0)
793 return false;
794
795 QMainWindowLayout *mainWinLayout = qt_mainwindow_layout(mainWin);
796 if (mainWinLayout == 0)
797 return false;
798
799 return (void*)mainWinLayout->pluggingWidget == (void*)q;
800}
801
802bool QDockWidgetPrivate::mousePressEvent(QMouseEvent *event)
803{
804#if !defined(QT_NO_MAINWINDOW)
805 Q_Q(QDockWidget);
806
807 QDockWidgetLayout *dwLayout
808 = qobject_cast<QDockWidgetLayout*>(layout);
809
810 if (!dwLayout->nativeWindowDeco()) {
811 QRect titleArea = dwLayout->titleArea();
812
813 if (event->button() != Qt::LeftButton ||
814 !titleArea.contains(event->pos()) ||
815 // check if the tool window is movable... do nothing if it
816 // is not (but allow moving if the window is floating)
817 (!hasFeature(this, QDockWidget::DockWidgetMovable) && !q->isFloating()) ||
818 qobject_cast<QMainWindow*>(parent) == 0 ||
819 isAnimating() || state != 0) {
820 return false;
821 }
822
823 initDrag(event->pos(), false);
824
825 if (state)
826 state->ctrlDrag = hasFeature(this, QDockWidget::DockWidgetFloatable) && event->modifiers() & Qt::ControlModifier;
827
828 return true;
829 }
830
831#endif // !defined(QT_NO_MAINWINDOW)
832 return false;
833}
834
835bool QDockWidgetPrivate::mouseDoubleClickEvent(QMouseEvent *event)
836{
837 QDockWidgetLayout *dwLayout = qobject_cast<QDockWidgetLayout*>(layout);
838
839 if (!dwLayout->nativeWindowDeco()) {
840 QRect titleArea = dwLayout->titleArea();
841
842 if (event->button() == Qt::LeftButton && titleArea.contains(event->pos()) &&
843 hasFeature(this, QDockWidget::DockWidgetFloatable)) {
844 _q_toggleTopLevel();
845 return true;
846 }
847 }
848 return false;
849}
850
851bool QDockWidgetPrivate::mouseMoveEvent(QMouseEvent *event)
852{
853 bool ret = false;
854#if !defined(QT_NO_MAINWINDOW)
855 Q_Q(QDockWidget);
856
857 if (!state)
858 return ret;
859
860 QDockWidgetLayout *dwlayout
861 = qobject_cast<QDockWidgetLayout *>(layout);
862 QMainWindowLayout *mwlayout = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q->parentWidget()));
863 if (!dwlayout->nativeWindowDeco()) {
864 if (!state->dragging
865 && mwlayout->pluggingWidget == 0
866 && (event->pos() - state->pressPos).manhattanLength()
867 > QApplication::startDragDistance()) {
868 startDrag();
869#ifdef Q_OS_WIN
870 grabMouseWhileInWindow();
871#else
872 q->grabMouse();
873#endif
874 ret = true;
875 }
876 }
877
878 if (state->dragging && !state->nca) {
879 QPoint pos = event->globalPos() - state->pressPos;
880 q->move(pos);
881
882 if (!state->ctrlDrag)
883 mwlayout->hover(state->widgetItem, event->globalPos());
884
885 ret = true;
886 }
887
888#endif // !defined(QT_NO_MAINWINDOW)
889 return ret;
890}
891
892bool QDockWidgetPrivate::mouseReleaseEvent(QMouseEvent *event)
893{
894#if !defined(QT_NO_MAINWINDOW)
895
896 if (event->button() == Qt::LeftButton && state && !state->nca) {
897 endDrag();
898 return true; //filter out the event
899 }
900
901#endif // !defined(QT_NO_MAINWINDOW)
902 return false;
903}
904
905void QDockWidgetPrivate::nonClientAreaMouseEvent(QMouseEvent *event)
906{
907 Q_Q(QDockWidget);
908
909 int fw = q->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, q);
910
911 QRect geo = q->geometry();
912 QRect titleRect = q->frameGeometry();
913#ifdef Q_WS_MAC
914 if ((features & QDockWidget::DockWidgetVerticalTitleBar)) {
915 titleRect.setTop(geo.top());
916 titleRect.setBottom(geo.bottom());
917 titleRect.setRight(geo.left() - 1);
918 } else
919#endif
920 {
921 titleRect.setLeft(geo.left());
922 titleRect.setRight(geo.right());
923 titleRect.setBottom(geo.top() - 1);
924 titleRect.adjust(0, fw, 0, 0);
925 }
926
927 switch (event->type()) {
928 case QEvent::NonClientAreaMouseButtonPress:
929 if (!titleRect.contains(event->globalPos()))
930 break;
931 if (state != 0)
932 break;
933 if (qobject_cast<QMainWindow*>(parent) == 0)
934 break;
935 if (isAnimating())
936 break;
937 initDrag(event->pos(), true);
938 if (state == 0)
939 break;
940#ifdef Q_OS_WIN
941 // On Windows, NCA mouse events don't contain modifier info
942 state->ctrlDrag = GetKeyState(VK_CONTROL) & 0x8000;
943#else
944 state->ctrlDrag = event->modifiers() & Qt::ControlModifier;
945#endif
946 startDrag();
947 break;
948 case QEvent::NonClientAreaMouseMove:
949 if (state == 0 || !state->dragging)
950 break;
951 if (state->nca) {
952 endDrag();
953 }
954#ifdef Q_OS_MAC
955 else { // workaround for lack of mouse-grab on Mac
956 QMainWindowLayout *layout = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q->parentWidget()));
957 Q_ASSERT(layout != 0);
958
959 q->move(event->globalPos() - state->pressPos);
960 if (!state->ctrlDrag)
961 layout->hover(state->widgetItem, event->globalPos());
962 }
963#endif
964 break;
965 case QEvent::NonClientAreaMouseButtonRelease:
966#ifdef Q_OS_MAC
967 if (state)
968 endDrag();
969#endif
970 break;
971 case QEvent::NonClientAreaMouseButtonDblClick:
972 _q_toggleTopLevel();
973 break;
974 default:
975 break;
976 }
977}
978
979void QDockWidgetPrivate::moveEvent(QMoveEvent *event)
980{
981 Q_Q(QDockWidget);
982
983 if (state == 0 || !state->dragging || !state->nca || !q->isWindow())
984 return;
985
986 // When the native window frame is being dragged, all we get is these mouse
987 // move events.
988
989 if (state->ctrlDrag)
990 return;
991
992 QMainWindowLayout *layout = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q->parentWidget()));
993 Q_ASSERT(layout != 0);
994
995 QPoint globalMousePos = event->pos() + state->pressPos;
996 layout->hover(state->widgetItem, globalMousePos);
997}
998
999void QDockWidgetPrivate::unplug(const QRect &rect)
1000{
1001 Q_Q(QDockWidget);
1002 QRect r;
1003 if (!undockedGeometry.isNull()) {
1004 r = undockedGeometry;
1005 } else {
1006 r = rect;
1007 r.moveTopLeft(q->mapToGlobal(QPoint(0, 0)));
1008 QDockWidgetLayout *dwLayout = qobject_cast<QDockWidgetLayout*>(layout);
1009 if (dwLayout->nativeWindowDeco(true))
1010 r.adjust(0, dwLayout->titleHeight(), 0, 0);
1011 }
1012
1013 setWindowState(true, true, r);
1014}
1015
1016void QDockWidgetPrivate::plug(const QRect &rect)
1017{
1018 setWindowState(false, false, rect);
1019}
1020
1021void QDockWidgetPrivate::setWindowState(bool floating, bool unplug, const QRect &rect)
1022{
1023 Q_Q(QDockWidget);
1024
1025 if (!floating && parent) {
1026 QMainWindowLayout *mwlayout = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q->parentWidget()));
1027 if (mwlayout && mwlayout->dockWidgetArea(q) == Qt::NoDockWidgetArea)
1028 return; // this dockwidget can't be redocked
1029 }
1030
1031 bool wasFloating = q->isFloating();
1032 bool hidden = q->isHidden();
1033
1034 if (q->isVisible())
1035 q->hide();
1036
1037 Qt::WindowFlags flags = floating ? Qt::Tool : Qt::Widget;
1038
1039 QDockWidgetLayout *dwLayout = qobject_cast<QDockWidgetLayout*>(layout);
1040 const bool nativeDeco = dwLayout->nativeWindowDeco(floating);
1041
1042 if (nativeDeco) {
1043 flags |= Qt::CustomizeWindowHint | Qt::WindowTitleHint;
1044 if (hasFeature(this, QDockWidget::DockWidgetClosable))
1045 flags |= Qt::WindowCloseButtonHint;
1046 } else {
1047 flags |= Qt::FramelessWindowHint;
1048 }
1049
1050 if (unplug)
1051 flags |= Qt::X11BypassWindowManagerHint;
1052
1053 q->setWindowFlags(flags);
1054
1055#if defined(Q_WS_MAC) && !defined(QT_MAC_USE_COCOA)
1056 if (floating && nativeDeco && (q->features() & QDockWidget::DockWidgetVerticalTitleBar)) {
1057 ChangeWindowAttributes(HIViewGetWindow(HIViewRef(q->winId())), kWindowSideTitlebarAttribute, 0);
1058 }
1059#endif
1060
1061 if (!rect.isNull())
1062 q->setGeometry(rect);
1063
1064 updateButtons();
1065
1066 if (!hidden)
1067 q->show();
1068
1069 if (floating != wasFloating) {
1070 emit q->topLevelChanged(floating);
1071 if (!floating && parent) {
1072 QMainWindowLayout *mwlayout = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q->parentWidget()));
1073 if (mwlayout)
1074 emit q->dockLocationChanged(mwlayout->dockWidgetArea(q));
1075 }
1076 }
1077
1078 resizer->setActive(QWidgetResizeHandler::Resize, !unplug && floating && !nativeDeco);
1079}
1080
1081/*!
1082 \class QDockWidget
1083
1084 \brief The QDockWidget class provides a widget that can be docked
1085 inside a QMainWindow or floated as a top-level window on the
1086 desktop.
1087
1088 \ingroup mainwindow-classes
1089
1090 QDockWidget provides the concept of dock widgets, also know as
1091 tool palettes or utility windows. Dock windows are secondary
1092 windows placed in the \e {dock widget area} around the
1093 \l{QMainWindow::centralWidget()}{central widget} in a
1094 QMainWindow.
1095
1096 \image mainwindow-docks.png
1097
1098 Dock windows can be moved inside their current area, moved into
1099 new areas and floated (e.g., undocked) by the end-user. The
1100 QDockWidget API allows the programmer to restrict the dock widgets
1101 ability to move, float and close, as well as the areas in which
1102 they can be placed.
1103
1104 \section1 Appearance
1105
1106 A QDockWidget consists of a title bar and the content area. The
1107 title bar displays the dock widgets \link QWidget::windowTitle()
1108 window title\endlink, a \e float button and a \e close button.
1109 Depending on the state of the QDockWidget, the \e float and \e
1110 close buttons may be either disabled or not shown at all.
1111
1112 The visual appearance of the title bar and buttons is dependent
1113 on the \l{QStyle}{style} in use.
1114
1115 A QDockWidget acts as a wrapper for its child widget, set with setWidget().
1116 Custom size hints, minimum and maximum sizes and size policies should be
1117 implemented in the child widget. QDockWidget will respect them, adjusting
1118 its own constraints to include the frame and title. Size constraints
1119 should not be set on the QDockWidget itself, because they change depending
1120 on whether it is docked; a docked QDockWidget has no frame and a smaller title
1121 bar.
1122
1123 \sa QMainWindow, {Dock Widgets Example}
1124*/
1125
1126/*!
1127 \enum QDockWidget::DockWidgetFeature
1128
1129 \value DockWidgetClosable The dock widget can be closed. On some systems the dock
1130 widget always has a close button when it's floating
1131 (for example on MacOS 10.5).
1132 \value DockWidgetMovable The dock widget can be moved between docks
1133 by the user.
1134 \value DockWidgetFloatable The dock widget can be detached from the
1135 main window, and floated as an independent
1136 window.
1137 \value DockWidgetVerticalTitleBar The dock widget displays a vertical title
1138 bar on its left side. This can be used to
1139 increase the amount of vertical space in
1140 a QMainWindow.
1141 \value AllDockWidgetFeatures (Deprecated) The dock widget can be closed, moved,
1142 and floated. Since new features might be added in future
1143 releases, the look and behavior of dock widgets might
1144 change if you use this flag. Please specify individual
1145 flags instead.
1146 \value NoDockWidgetFeatures The dock widget cannot be closed, moved,
1147 or floated.
1148
1149 \omitvalue DockWidgetFeatureMask
1150 \omitvalue Reserved
1151*/
1152
1153/*!
1154 \property QDockWidget::windowTitle
1155 \brief the dock widget title (caption)
1156
1157 By default, this property contains an empty string.
1158*/
1159
1160/*!
1161 Constructs a QDockWidget with parent \a parent and window flags \a
1162 flags. The dock widget will be placed in the left dock widget
1163 area.
1164*/
1165QDockWidget::QDockWidget(QWidget *parent, Qt::WindowFlags flags)
1166 : QWidget(*new QDockWidgetPrivate, parent, flags)
1167{
1168 Q_D(QDockWidget);
1169 d->init();
1170}
1171
1172/*!
1173 Constructs a QDockWidget with parent \a parent and window flags \a
1174 flags. The dock widget will be placed in the left dock widget
1175 area.
1176
1177 The window title is set to \a title. This title is used when the
1178 QDockWidget is docked and undocked. It is also used in the context
1179 menu provided by QMainWindow.
1180
1181 \sa setWindowTitle()
1182*/
1183QDockWidget::QDockWidget(const QString &title, QWidget *parent, Qt::WindowFlags flags)
1184 : QWidget(*new QDockWidgetPrivate, parent, flags)
1185{
1186 Q_D(QDockWidget);
1187 d->init();
1188 setWindowTitle(title);
1189}
1190
1191/*!
1192 Destroys the dock widget.
1193*/
1194QDockWidget::~QDockWidget()
1195{ }
1196
1197/*!
1198 Returns the widget for the dock widget. This function returns zero
1199 if the widget has not been set.
1200
1201 \sa setWidget()
1202*/
1203QWidget *QDockWidget::widget() const
1204{
1205 QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(this->layout());
1206 return layout->widgetForRole(QDockWidgetLayout::Content);
1207}
1208
1209/*!
1210 Sets the widget for the dock widget to \a widget.
1211
1212 If the dock widget is visible when \a widget is added, you must
1213 \l{QWidget::}{show()} it explicitly.
1214
1215 Note that you must add the layout of the \a widget before you call
1216 this function; if not, the \a widget will not be visible.
1217
1218 \sa widget()
1219*/
1220void QDockWidget::setWidget(QWidget *widget)
1221{
1222 QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(this->layout());
1223 layout->setWidgetForRole(QDockWidgetLayout::Content, widget);
1224}
1225
1226/*!
1227 \property QDockWidget::features
1228 \brief whether the dock widget is movable, closable, and floatable
1229
1230 By default, this property is set to a combination of DockWidgetClosable,
1231 DockWidgetMovable and DockWidgetFloatable.
1232
1233 \sa DockWidgetFeature
1234*/
1235
1236void QDockWidget::setFeatures(QDockWidget::DockWidgetFeatures features)
1237{
1238 Q_D(QDockWidget);
1239 features &= DockWidgetFeatureMask;
1240 if (d->features == features)
1241 return;
1242 const bool closableChanged = (d->features ^ features) & DockWidgetClosable;
1243 d->features = features;
1244 QDockWidgetLayout *layout
1245 = qobject_cast<QDockWidgetLayout*>(this->layout());
1246 layout->setVerticalTitleBar(features & DockWidgetVerticalTitleBar);
1247 d->updateButtons();
1248 d->toggleViewAction->setEnabled((d->features & DockWidgetClosable) == DockWidgetClosable);
1249 emit featuresChanged(d->features);
1250 update();
1251 if (closableChanged && layout->nativeWindowDeco()) {
1252 //this ensures the native decoration is drawn
1253 d->setWindowState(true /*floating*/, true /*unplug*/);
1254 }
1255}
1256
1257QDockWidget::DockWidgetFeatures QDockWidget::features() const
1258{
1259 Q_D(const QDockWidget);
1260 return d->features;
1261}
1262
1263/*!
1264 \property QDockWidget::floating
1265 \brief whether the dock widget is floating
1266
1267 A floating dock widget is presented to the user as an independent
1268 window "on top" of its parent QMainWindow, instead of being
1269 docked in the QMainWindow.
1270
1271 By default, this property is true.
1272
1273 \sa isWindow()
1274*/
1275void QDockWidget::setFloating(bool floating)
1276{
1277 Q_D(QDockWidget);
1278
1279 // the initial click of a double-click may have started a drag...
1280 if (d->state != 0)
1281 d->endDrag(true);
1282
1283 QRect r = d->undockedGeometry;
1284 // Keep position when undocking for the first time.
1285 if (floating && isVisible() && !r.isValid())
1286 r = QRect(mapToGlobal(QPoint(0, 0)), size());
1287
1288 d->setWindowState(floating, false, floating ? r : QRect());
1289
1290 if (floating && r.isNull()) {
1291 if (x() < 0 || y() < 0) //may happen if we have been hidden
1292 move(QPoint());
1293 setAttribute(Qt::WA_Moved, false); //we want it at the default position
1294 }
1295}
1296
1297/*!
1298 \property QDockWidget::allowedAreas
1299 \brief areas where the dock widget may be placed
1300
1301 The default is Qt::AllDockWidgetAreas.
1302
1303 \sa Qt::DockWidgetArea
1304*/
1305
1306void QDockWidget::setAllowedAreas(Qt::DockWidgetAreas areas)
1307{
1308 Q_D(QDockWidget);
1309 areas &= Qt::DockWidgetArea_Mask;
1310 if (areas == d->allowedAreas)
1311 return;
1312 d->allowedAreas = areas;
1313 emit allowedAreasChanged(d->allowedAreas);
1314}
1315
1316Qt::DockWidgetAreas QDockWidget::allowedAreas() const
1317{
1318 Q_D(const QDockWidget);
1319 return d->allowedAreas;
1320}
1321
1322/*!
1323 \fn bool QDockWidget::isAreaAllowed(Qt::DockWidgetArea area) const
1324
1325 Returns true if this dock widget can be placed in the given \a area;
1326 otherwise returns false.
1327*/
1328
1329/*! \reimp */
1330void QDockWidget::changeEvent(QEvent *event)
1331{
1332 Q_D(QDockWidget);
1333 QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(this->layout());
1334
1335 switch (event->type()) {
1336 case QEvent::ModifiedChange:
1337 case QEvent::WindowTitleChange:
1338 update(layout->titleArea());
1339#ifndef QT_NO_ACTION
1340 d->fixedWindowTitle = qt_setWindowTitle_helperHelper(windowTitle(), this);
1341 d->toggleViewAction->setText(d->fixedWindowTitle);
1342#endif
1343#ifndef QT_NO_TABBAR
1344 {
1345 QMainWindow *win = qobject_cast<QMainWindow*>(parentWidget());
1346 if (QMainWindowLayout *winLayout = qt_mainwindow_layout(win)) {
1347 if (QDockAreaLayoutInfo *info = winLayout->layoutState.dockAreaLayout.info(this))
1348 info->updateTabBar();
1349 }
1350 }
1351#endif // QT_NO_TABBAR
1352 break;
1353 default:
1354 break;
1355 }
1356 QWidget::changeEvent(event);
1357}
1358
1359/*! \reimp */
1360void QDockWidget::closeEvent(QCloseEvent *event)
1361{
1362 Q_D(QDockWidget);
1363 if (d->state)
1364 d->endDrag(true);
1365 QWidget::closeEvent(event);
1366}
1367
1368/*! \reimp */
1369void QDockWidget::paintEvent(QPaintEvent *event)
1370{
1371 Q_UNUSED(event)
1372
1373 QDockWidgetLayout *layout
1374 = qobject_cast<QDockWidgetLayout*>(this->layout());
1375 bool customTitleBar = layout->widgetForRole(QDockWidgetLayout::TitleBar) != 0;
1376 bool nativeDeco = layout->nativeWindowDeco();
1377
1378 if (!nativeDeco && !customTitleBar) {
1379 QStylePainter p(this);
1380 // ### Add PixelMetric to change spacers, so style may show border
1381 // when not floating.
1382 if (isFloating()) {
1383 QStyleOptionFrame framOpt;
1384 framOpt.init(this);
1385 p.drawPrimitive(QStyle::PE_FrameDockWidget, framOpt);
1386 }
1387
1388 // Title must be painted after the frame, since the areas overlap, and
1389 // the title may wish to extend out to all sides (eg. XP style)
1390 QStyleOptionDockWidgetV2 titleOpt;
1391 initStyleOption(&titleOpt);
1392 p.drawControl(QStyle::CE_DockWidgetTitle, titleOpt);
1393 }
1394}
1395
1396/*! \reimp */
1397bool QDockWidget::event(QEvent *event)
1398{
1399 Q_D(QDockWidget);
1400
1401 QMainWindow *win = qobject_cast<QMainWindow*>(parentWidget());
1402 QMainWindowLayout *layout = qt_mainwindow_layout(win);
1403
1404 switch (event->type()) {
1405#ifndef QT_NO_ACTION
1406 case QEvent::Hide:
1407 if (layout != 0)
1408 layout->keepSize(this);
1409 d->toggleViewAction->setChecked(false);
1410 emit visibilityChanged(false);
1411 break;
1412 case QEvent::Show: {
1413 d->toggleViewAction->setChecked(true);
1414 const QPoint parentTopLeft = isWindow() ?
1415 QApplication::desktop()->availableGeometry(this).topLeft() : QPoint(0, 0);
1416 emit visibilityChanged(geometry().right() >= parentTopLeft.x() && geometry().bottom() >= parentTopLeft.y());
1417}
1418 break;
1419#endif
1420 case QEvent::ApplicationLayoutDirectionChange:
1421 case QEvent::LayoutDirectionChange:
1422 case QEvent::StyleChange:
1423 case QEvent::ParentChange:
1424 d->updateButtons();
1425 break;
1426 case QEvent::ZOrderChange: {
1427 bool onTop = false;
1428 if (win != 0) {
1429 const QObjectList &siblings = win->children();
1430 onTop = siblings.count() > 0 && siblings.last() == (QObject*)this;
1431 }
1432 if (!isFloating() && layout != 0 && onTop)
1433 layout->raise(this);
1434 break;
1435 }
1436 case QEvent::WindowActivate:
1437 case QEvent::WindowDeactivate:
1438 update(qobject_cast<QDockWidgetLayout *>(this->layout())->titleArea());
1439 break;
1440 case QEvent::ContextMenu:
1441 if (d->state) {
1442 event->accept();
1443 return true;
1444 }
1445 break;
1446 // return true after calling the handler since we don't want
1447 // them to be passed onto the default handlers
1448 case QEvent::MouseButtonPress:
1449 if (d->mousePressEvent(static_cast<QMouseEvent *>(event)))
1450 return true;
1451 break;
1452 case QEvent::MouseButtonDblClick:
1453 if (d->mouseDoubleClickEvent(static_cast<QMouseEvent *>(event)))
1454 return true;
1455 break;
1456 case QEvent::MouseMove:
1457 if (d->mouseMoveEvent(static_cast<QMouseEvent *>(event)))
1458 return true;
1459 break;
1460#ifdef Q_OS_WIN
1461 case QEvent::Leave:
1462 if (d->state != 0 && d->state->dragging && !d->state->nca) {
1463 // This is a workaround for loosing the mouse on Vista.
1464 QPoint pos = QCursor::pos();
1465 QMouseEvent fake(QEvent::MouseMove, mapFromGlobal(pos), pos, Qt::NoButton,
1466 QApplication::mouseButtons(), QApplication::keyboardModifiers());
1467 d->mouseMoveEvent(&fake);
1468 }
1469 break;
1470#endif
1471 case QEvent::MouseButtonRelease:
1472 if (d->mouseReleaseEvent(static_cast<QMouseEvent *>(event)))
1473 return true;
1474 break;
1475 case QEvent::NonClientAreaMouseMove:
1476 case QEvent::NonClientAreaMouseButtonPress:
1477 case QEvent::NonClientAreaMouseButtonRelease:
1478 case QEvent::NonClientAreaMouseButtonDblClick:
1479 d->nonClientAreaMouseEvent(static_cast<QMouseEvent*>(event));
1480 return true;
1481 case QEvent::Move:
1482 d->moveEvent(static_cast<QMoveEvent*>(event));
1483 break;
1484 case QEvent::Resize:
1485 // if the mainwindow is plugging us, we don't want to update undocked geometry
1486 if (isFloating() && layout != 0 && layout->pluggingWidget != this)
1487 d->undockedGeometry = geometry();
1488 break;
1489 default:
1490 break;
1491 }
1492 return QWidget::event(event);
1493}
1494
1495#ifndef QT_NO_ACTION
1496/*!
1497 Returns a checkable action that can be used to show or close this
1498 dock widget.
1499
1500 The action's text is set to the dock widget's window title.
1501
1502 \sa QAction::text QWidget::windowTitle
1503 */
1504QAction * QDockWidget::toggleViewAction() const
1505{
1506 Q_D(const QDockWidget);
1507 return d->toggleViewAction;
1508}
1509#endif // QT_NO_ACTION
1510
1511/*!
1512 \fn void QDockWidget::featuresChanged(QDockWidget::DockWidgetFeatures features)
1513
1514 This signal is emitted when the \l features property changes. The
1515 \a features parameter gives the new value of the property.
1516*/
1517
1518/*!
1519 \fn void QDockWidget::topLevelChanged(bool topLevel)
1520
1521 This signal is emitted when the \l floating property changes.
1522 The \a topLevel parameter is true if the dock widget is now floating;
1523 otherwise it is false.
1524
1525 \sa isWindow()
1526*/
1527
1528/*!
1529 \fn void QDockWidget::allowedAreasChanged(Qt::DockWidgetAreas allowedAreas)
1530
1531 This signal is emitted when the \l allowedAreas property changes. The
1532 \a allowedAreas parameter gives the new value of the property.
1533*/
1534
1535/*!
1536 \fn void QDockWidget::visibilityChanged(bool visible)
1537 \since 4.3
1538
1539 This signal is emitted when the dock widget becomes \a visible (or
1540 invisible). This happens when the widget is hidden or shown, as
1541 well as when it is docked in a tabbed dock area and its tab
1542 becomes selected or unselected.
1543*/
1544
1545/*!
1546 \fn void QDockWidget::dockLocationChanged(Qt::DockWidgetArea area)
1547 \since 4.3
1548
1549 This signal is emitted when the dock widget is moved to another
1550 dock \a area, or is moved to a different location in its current
1551 dock area. This happens when the dock widget is moved
1552 programmatically or is dragged to a new location by the user.
1553*/
1554
1555/*!
1556 \since 4.3
1557
1558 Sets an arbitrary \a widget as the dock widget's title bar. If \a widget
1559 is 0, any custom title bar widget previously set on the dock widget is
1560 removed, but not deleted, and the default title bar will be used
1561 instead.
1562
1563 If a title bar widget is set, QDockWidget will not use native window
1564 decorations when it is floated.
1565
1566 Here are some tips for implementing custom title bars:
1567
1568 \list
1569 \o Mouse events that are not explicitly handled by the title bar widget
1570 must be ignored by calling QMouseEvent::ignore(). These events then
1571 propagate to the QDockWidget parent, which handles them in the usual
1572 manner, moving when the title bar is dragged, docking and undocking
1573 when it is double-clicked, etc.
1574
1575 \o When DockWidgetVerticalTitleBar is set on QDockWidget, the title
1576 bar widget is repositioned accordingly. In resizeEvent(), the title
1577 bar should check what orientation it should assume:
1578 \snippet doc/src/snippets/code/src_gui_widgets_qdockwidget.cpp 0
1579
1580 \o The title bar widget must have a valid QWidget::sizeHint() and
1581 QWidget::minimumSizeHint(). These functions should take into account
1582 the current orientation of the title bar.
1583
1584 \o It is not possible to remove a title bar from a dock widget. However,
1585 a similar effect can be achieved by setting a default constructed
1586 QWidget as the title bar widget.
1587 \endlist
1588
1589 Using qobject_cast() as shown above, the title bar widget has full access
1590 to its parent QDockWidget. Hence it can perform such operations as docking
1591 and hiding in response to user actions.
1592
1593 \sa titleBarWidget() DockWidgetVerticalTitleBar
1594*/
1595
1596void QDockWidget::setTitleBarWidget(QWidget *widget)
1597{
1598 Q_D(QDockWidget);
1599 QDockWidgetLayout *layout
1600 = qobject_cast<QDockWidgetLayout*>(this->layout());
1601 layout->setWidgetForRole(QDockWidgetLayout::TitleBar, widget);
1602 d->updateButtons();
1603 if (isWindow()) {
1604 //this ensures the native decoration is drawn
1605 d->setWindowState(true /*floating*/, true /*unplug*/);
1606 }
1607}
1608
1609/*!
1610 \since 4.3
1611 Returns the custom title bar widget set on the QDockWidget, or 0 if no
1612 custom title bar has been set.
1613
1614 \sa setTitleBarWidget()
1615*/
1616
1617QWidget *QDockWidget::titleBarWidget() const
1618{
1619 QDockWidgetLayout *layout
1620 = qobject_cast<QDockWidgetLayout*>(this->layout());
1621 return layout->widgetForRole(QDockWidgetLayout::TitleBar);
1622}
1623
1624QT_END_NAMESPACE
1625
1626#include "qdockwidget.moc"
1627#include "moc_qdockwidget.cpp"
1628
1629#endif // QT_NO_DOCKWIDGET
1630