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 "qtoolbar.h"
43 
44 #ifndef QT_NO_TOOLBAR
45 
46 #include <qapplication.h>
47 #include <qcombobox.h>
48 #include <qevent.h>
49 #include <qlayout.h>
50 #include <qmainwindow.h>
51 #include <qmenu.h>
52 #include <qmenubar.h>
53 #include <qrubberband.h>
54 #include <qsignalmapper.h>
55 #include <qstylepainter.h>
56 #include <qtoolbutton.h>
57 #include <qwidgetaction.h>
58 #include <qtimer.h>
59 #include <private/qwidgetaction_p.h>
60 #ifdef Q_WS_MAC
61 #include <private/qt_mac_p.h>
62 #include <private/qt_cocoa_helpers_mac_p.h>
63 #endif
64 
65 #include <private/qmainwindowlayout_p.h>
66 
67 #include "qtoolbar_p.h"
68 #include "qtoolbarseparator_p.h"
69 #include "qtoolbarlayout_p.h"
70 
71 #include  "qdebug.h"
72 
73 #define POPUP_TIMER_INTERVAL 500
74 
75 QT_BEGIN_NAMESPACE
76 
77 #ifdef Q_WS_MAC
qt_mac_updateToolBarButtonHint(QWidget * parentWidget)78 static void qt_mac_updateToolBarButtonHint(QWidget *parentWidget)
79 {
80     if (!(parentWidget->windowFlags() & Qt::CustomizeWindowHint))
81         parentWidget->setWindowFlags(parentWidget->windowFlags() | Qt::MacWindowToolBarButtonHint);
82 }
83 #endif
84 
85 // qmainwindow.cpp
86 extern QMainWindowLayout *qt_mainwindow_layout(const QMainWindow *window);
87 
88 /******************************************************************************
89 ** QToolBarPrivate
90 */
91 
init()92 void QToolBarPrivate::init()
93 {
94     Q_Q(QToolBar);
95     q->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
96     q->setBackgroundRole(QPalette::Button);
97     q->setAttribute(Qt::WA_Hover);
98     q->setAttribute(Qt::WA_X11NetWmWindowTypeToolBar);
99 
100     QStyle *style = q->style();
101     int e = style->pixelMetric(QStyle::PM_ToolBarIconSize, 0, q);
102     iconSize = QSize(e, e);
103 
104     layout = new QToolBarLayout(q);
105     layout->updateMarginAndSpacing();
106 
107 #ifdef Q_WS_MAC
108     if (q->parentWidget() && q->parentWidget()->isWindow()) {
109         // Make sure that the window has the "toolbar" button.
110         QWidget *parentWidget = q->parentWidget();
111         qt_mac_updateToolBarButtonHint(parentWidget);
112         reinterpret_cast<QToolBar *>(parentWidget)->d_func()->createWinId(); // Please let me create your winId...
113         extern OSWindowRef qt_mac_window_for(const QWidget *); // qwidget_mac.cpp
114         macWindowToolbarShow(q->parentWidget(), true);
115     }
116 #endif
117 
118     toggleViewAction = new QAction(q);
119     toggleViewAction->setCheckable(true);
120     q->setMovable(q->style()->styleHint(QStyle::SH_ToolBar_Movable, 0, q ));
121     QObject::connect(toggleViewAction, SIGNAL(triggered(bool)), q, SLOT(_q_toggleView(bool)));
122 }
123 
_q_toggleView(bool b)124 void QToolBarPrivate::_q_toggleView(bool b)
125 {
126     Q_Q(QToolBar);
127     if (b == q->isHidden()) {
128         if (b)
129             q->show();
130         else
131             q->close();
132     }
133 }
134 
_q_updateIconSize(const QSize & sz)135 void QToolBarPrivate::_q_updateIconSize(const QSize &sz)
136 {
137     Q_Q(QToolBar);
138     if (!explicitIconSize) {
139         // iconSize not explicitly set
140         q->setIconSize(sz);
141         explicitIconSize = false;
142     }
143 }
144 
_q_updateToolButtonStyle(Qt::ToolButtonStyle style)145 void QToolBarPrivate::_q_updateToolButtonStyle(Qt::ToolButtonStyle style)
146 {
147     Q_Q(QToolBar);
148     if (!explicitToolButtonStyle) {
149         q->setToolButtonStyle(style);
150         explicitToolButtonStyle = false;
151     }
152 }
153 
updateWindowFlags(bool floating,bool unplug)154 void QToolBarPrivate::updateWindowFlags(bool floating, bool unplug)
155 {
156     Q_Q(QToolBar);
157     Qt::WindowFlags flags = floating ? Qt::Tool : Qt::Widget;
158 
159     flags |= Qt::FramelessWindowHint;
160 
161     if (unplug) {
162         flags |= Qt::X11BypassWindowManagerHint;
163 #ifdef Q_WS_MAC
164         flags |= Qt::WindowStaysOnTopHint;
165 #endif
166     }
167 
168     q->setWindowFlags(flags);
169 }
170 
setWindowState(bool floating,bool unplug,const QRect & rect)171 void QToolBarPrivate::setWindowState(bool floating, bool unplug, const QRect &rect)
172 {
173     Q_Q(QToolBar);
174     bool visible = !q->isHidden();
175     bool wasFloating = q->isFloating(); // ...is also currently using popup menus
176 
177     q->hide();
178 
179     updateWindowFlags(floating, unplug);
180 
181     if (floating != wasFloating)
182         layout->checkUsePopupMenu();
183 
184     if (!rect.isNull())
185         q->setGeometry(rect);
186 
187     if (visible)
188         q->show();
189 
190     if (floating != wasFloating)
191         emit q->topLevelChanged(floating);
192 }
193 
initDrag(const QPoint & pos)194 void QToolBarPrivate::initDrag(const QPoint &pos)
195 {
196     Q_Q(QToolBar);
197 
198     if (state != 0)
199         return;
200 
201     QMainWindow *win = qobject_cast<QMainWindow*>(parent);
202     Q_ASSERT(win != 0);
203     QMainWindowLayout *layout = qt_mainwindow_layout(win);
204     Q_ASSERT(layout != 0);
205     if (layout->pluggingWidget != 0) // the main window is animating a docking operation
206         return;
207 
208     state = new DragState;
209     state->pressPos = pos;
210     state->dragging = false;
211     state->moving = false;
212     state->widgetItem = 0;
213 
214     if (q->isRightToLeft())
215         state->pressPos = QPoint(q->width() - state->pressPos.x(), state->pressPos.y());
216 }
217 
startDrag(bool moving)218 void QToolBarPrivate::startDrag(bool moving)
219 {
220     Q_Q(QToolBar);
221 
222     Q_ASSERT(state != 0);
223 
224     if ((moving && state->moving) || state->dragging)
225         return;
226 
227     QMainWindow *win = qobject_cast<QMainWindow*>(parent);
228     Q_ASSERT(win != 0);
229     QMainWindowLayout *layout = qt_mainwindow_layout(win);
230     Q_ASSERT(layout != 0);
231 
232     if (!moving) {
233         state->widgetItem = layout->unplug(q);
234 #if defined(Q_WS_MAC) && !defined(QT_MAC_USE_COCOA)
235         if (q->isWindow()) {
236            setWindowState(true, true); //set it to floating
237         }
238 #endif
239         Q_ASSERT(state->widgetItem != 0);
240     }
241     state->dragging = !moving;
242     state->moving = moving;
243 }
244 
endDrag()245 void QToolBarPrivate::endDrag()
246 {
247     Q_Q(QToolBar);
248     Q_ASSERT(state != 0);
249 
250     q->releaseMouse();
251 
252     if (state->dragging) {
253         QMainWindowLayout *layout = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q->parentWidget()));
254         Q_ASSERT(layout != 0);
255 
256         if (!layout->plug(state->widgetItem)) {
257             if (q->isFloatable()) {
258                 layout->restore();
259 #if defined(Q_WS_X11) || defined(Q_WS_MAC)
260                 setWindowState(true); // gets rid of the X11BypassWindowManager window flag
261                                       // and activates the resizer
262 #endif
263                 q->activateWindow();
264             } else {
265                 layout->revert(state->widgetItem);
266             }
267         }
268     }
269 
270     delete state;
271     state = 0;
272 }
273 
mousePressEvent(QMouseEvent * event)274 bool QToolBarPrivate::mousePressEvent(QMouseEvent *event)
275 {
276     Q_Q(QToolBar);
277     QStyleOptionToolBar opt;
278     q->initStyleOption(&opt);
279     if (q->style()->subElementRect(QStyle::SE_ToolBarHandle, &opt, q).contains(event->pos()) == false) {
280 #ifdef Q_WS_MAC
281         // When using the unified toolbar on Mac OS X the user can can click and
282         // drag between toolbar contents to move the window. Make this work by
283         // implementing the standard mouse-dragging code and then call
284         // window->move() in mouseMoveEvent below.
285         if (QMainWindow *mainWindow = qobject_cast<QMainWindow *>(parent)) {
286             if (mainWindow->toolBarArea(q) == Qt::TopToolBarArea
287                     && mainWindow->unifiedTitleAndToolBarOnMac()
288                     && q->childAt(event->pos()) == 0) {
289                 macWindowDragging = true;
290                 macWindowDragPressPosition = event->pos();
291                 return true;
292             }
293         }
294 #endif
295         return false;
296     }
297 
298     if (event->button() != Qt::LeftButton)
299         return true;
300 
301     if (!layout->movable())
302         return true;
303 
304     initDrag(event->pos());
305     return true;
306 }
307 
mouseReleaseEvent(QMouseEvent *)308 bool QToolBarPrivate::mouseReleaseEvent(QMouseEvent*)
309 {
310     if (state != 0) {
311         endDrag();
312         return true;
313     } else {
314 #ifdef Q_WS_MAC
315         if (!macWindowDragging)
316             return false;
317         macWindowDragging = false;
318         macWindowDragPressPosition = QPoint();
319         return true;
320 #endif
321         return false;
322     }
323 }
324 
mouseMoveEvent(QMouseEvent * event)325 bool QToolBarPrivate::mouseMoveEvent(QMouseEvent *event)
326 {
327     Q_Q(QToolBar);
328 
329     if (!state) {
330 #ifdef Q_WS_MAC
331         if (!macWindowDragging)
332             return false;
333         QWidget *w = q->window();
334         const QPoint delta = event->pos() - macWindowDragPressPosition;
335         w->move(w->pos() + delta);
336         return true;
337 #endif
338         return false;
339     }
340 
341     QMainWindow *win = qobject_cast<QMainWindow*>(parent);
342     if (win == 0)
343         return true;
344 
345     QMainWindowLayout *layout = qt_mainwindow_layout(win);
346     Q_ASSERT(layout != 0);
347 
348     if (layout->pluggingWidget == 0
349         && (event->pos() - state->pressPos).manhattanLength() > QApplication::startDragDistance()) {
350             const bool wasDragging = state->dragging;
351             const bool moving = !q->isWindow() && (orientation == Qt::Vertical ?
352                 event->x() >= 0 && event->x() < q->width() :
353                 event->y() >= 0 && event->y() < q->height());
354 
355             startDrag(moving);
356             if (!moving && !wasDragging) {
357 #ifdef Q_OS_WIN
358                 grabMouseWhileInWindow();
359 #else
360                 q->grabMouse();
361 #endif
362             }
363     }
364 
365     if (state->dragging) {
366         QPoint pos = event->globalPos();
367         // if we are right-to-left, we move so as to keep the right edge the same distance
368         // from the mouse
369         if (q->isLeftToRight())
370             pos -= state->pressPos;
371         else
372             pos += QPoint(state->pressPos.x() - q->width(), -state->pressPos.y());
373 
374         q->move(pos);
375         layout->hover(state->widgetItem, event->globalPos());
376     } else if (state->moving) {
377 
378         const QPoint rtl(q->width() - state->pressPos.x(), state->pressPos.y()); //for RTL
379         const QPoint globalPressPos = q->mapToGlobal(q->isRightToLeft() ? rtl : state->pressPos);
380         int pos = 0;
381 
382         QPoint delta = event->globalPos() - globalPressPos;
383         if (orientation == Qt::Vertical) {
384             pos = q->y() + delta.y();
385         } else {
386             if (q->isRightToLeft()) {
387                 pos = win->width() - q->width() - q->x()  - delta.x();
388             } else {
389                 pos = q->x() + delta.x();
390             }
391         }
392 
393         layout->moveToolBar(q, pos);
394     }
395     return true;
396 }
397 
unplug(const QRect & _r)398 void QToolBarPrivate::unplug(const QRect &_r)
399 {
400     Q_Q(QToolBar);
401     QRect r = _r;
402     r.moveTopLeft(q->mapToGlobal(QPoint(0, 0)));
403     setWindowState(true, true, r);
404     layout->setExpanded(false);
405 }
406 
plug(const QRect & r)407 void QToolBarPrivate::plug(const QRect &r)
408 {
409     setWindowState(false, false, r);
410 }
411 
412 /******************************************************************************
413 ** QToolBar
414 */
415 
416 /*!
417     \class QToolBar
418 
419     \brief The QToolBar class provides a movable panel that contains a
420     set of controls.
421 
422     \ingroup mainwindow-classes
423 
424 
425     Toolbar buttons are added by adding \e actions, using addAction()
426     or insertAction(). Groups of buttons can be separated using
427     addSeparator() or insertSeparator(). If a toolbar button is not
428     appropriate, a widget can be inserted instead using addWidget() or
429     insertWidget(); examples of suitable widgets are QSpinBox,
430     QDoubleSpinBox, and QComboBox. When a toolbar button is pressed it
431     emits the actionTriggered() signal.
432 
433     A toolbar can be fixed in place in a particular area (e.g. at the
434     top of the window), or it can be movable (isMovable()) between
435     toolbar areas; see allowedAreas() and isAreaAllowed().
436 
437     When a toolbar is resized in such a way that it is too small to
438     show all the items it contains, an extension button will appear as
439     the last item in the toolbar. Pressing the extension button will
440     pop up a menu containing the items that does not currently fit in
441     the toolbar.
442 
443     When a QToolBar is not a child of a QMainWindow, it loses the ability
444     to populate the extension pop up with widgets added to the toolbar using
445     addWidget(). Please use widget actions created by inheriting QWidgetAction
446     and implementing QWidgetAction::createWidget() instead.
447 
448     \sa QToolButton, QMenu, QAction, {Application Example}
449 */
450 
451 /*!
452     \fn bool QToolBar::isAreaAllowed(Qt::ToolBarArea area) const
453 
454     Returns true if this toolbar is dockable in the given \a area;
455     otherwise returns false.
456 */
457 
458 /*!
459     \fn void QToolBar::addAction(QAction *action)
460     \overload
461 
462     Appends the action \a action to the toolbar's list of actions.
463 
464     \sa QMenu::addAction(), QWidget::addAction()
465 */
466 
467 /*!
468     \fn void QToolBar::actionTriggered(QAction *action)
469 
470     This signal is emitted when an action in this toolbar is triggered.
471     This happens when the action's tool button is pressed, or when the
472     action is triggered in some other way outside the tool bar. The parameter
473     holds the triggered \a action.
474 */
475 
476 /*!
477     \fn void QToolBar::allowedAreasChanged(Qt::ToolBarAreas allowedAreas)
478 
479     This signal is emitted when the collection of allowed areas for the
480     toolbar is changed. The new areas in which the toolbar can be positioned
481     are specified by \a allowedAreas.
482 
483     \sa allowedAreas
484 */
485 
486 /*!
487     \fn void QToolBar::iconSizeChanged(const QSize &iconSize)
488 
489     This signal is emitted when the icon size is changed.  The \a
490     iconSize parameter holds the toolbar's new icon size.
491 
492     \sa iconSize QMainWindow::iconSize
493 */
494 
495 /*!
496     \fn void QToolBar::movableChanged(bool movable)
497 
498     This signal is emitted when the toolbar becomes movable or fixed.
499     If the toolbar can be moved, \a movable is true; otherwise it is
500     false.
501 
502     \sa movable
503 */
504 
505 /*!
506     \fn void QToolBar::orientationChanged(Qt::Orientation orientation)
507 
508     This signal is emitted when the orientation of the toolbar changes.
509     The new orientation is specified by the \a orientation given.
510 
511     \sa orientation
512 */
513 
514 /*!
515     \fn void QToolBar::toolButtonStyleChanged(Qt::ToolButtonStyle toolButtonStyle)
516 
517     This signal is emitted when the tool button style is changed. The
518     \a toolButtonStyle parameter holds the toolbar's new tool button
519     style.
520 
521     \sa toolButtonStyle QMainWindow::toolButtonStyle
522 */
523 
524 /*!
525     \since 4.6
526 
527     \fn void QToolBar::topLevelChanged(bool topLevel)
528 
529     This signal is emitted when the \l floating property changes.
530     The \a topLevel parameter is true if the toolbar is now floating;
531     otherwise it is false.
532 
533     \sa isWindow()
534 */
535 
536 
537 /*!
538     \fn void QToolBar::visibilityChanged(bool visible)
539     \since 4.7
540 
541     This signal is emitted when the toolbar becomes \a visible (or
542     invisible). This happens when the widget is hidden or shown.
543 */
544 
545 /*!
546     Constructs a QToolBar with the given \a parent.
547 */
QToolBar(QWidget * parent)548 QToolBar::QToolBar(QWidget *parent)
549     : QWidget(*new QToolBarPrivate, parent, 0)
550 {
551     Q_D(QToolBar);
552     d->init();
553 }
554 
555 /*!
556     Constructs a QToolBar with the given \a parent.
557 
558     The given window \a title identifies the toolbar and is shown in
559     the context menu provided by QMainWindow.
560 
561     \sa setWindowTitle()
562 */
QToolBar(const QString & title,QWidget * parent)563 QToolBar::QToolBar(const QString &title, QWidget *parent)
564     : QWidget(*new QToolBarPrivate, parent, 0)
565 {
566     Q_D(QToolBar);
567     d->init();
568     setWindowTitle(title);
569 }
570 
571 #ifdef QT3_SUPPORT
572 /*! \obsolete
573     Constructs a QToolBar with the given \a parent and \a name.
574 */
QToolBar(QWidget * parent,const char * name)575 QToolBar::QToolBar(QWidget *parent, const char *name)
576     : QWidget(*new QToolBarPrivate, parent, 0)
577 {
578     Q_D(QToolBar);
579     d->init();
580     setObjectName(QString::fromAscii(name));
581 }
582 #endif
583 
584 /*!
585     Destroys the toolbar.
586 */
~QToolBar()587 QToolBar::~QToolBar()
588 {
589     // Remove the toolbar button if there is nothing left.
590     QMainWindow *mainwindow = qobject_cast<QMainWindow *>(parentWidget());
591     if (mainwindow) {
592 #ifdef Q_WS_MAC
593         QMainWindowLayout *mainwin_layout = qt_mainwindow_layout(mainwindow);
594         if (mainwin_layout && mainwin_layout->layoutState.toolBarAreaLayout.isEmpty()
595                 && mainwindow->testAttribute(Qt::WA_WState_Created))
596             macWindowToolbarShow(mainwindow, false);
597 #endif
598     }
599 }
600 
601 /*! \property QToolBar::movable
602     \brief whether the user can move the toolbar within the toolbar area,
603     or between toolbar areas
604 
605     By default, this property is true.
606 
607     This property only makes sense if the toolbar is in a
608     QMainWindow.
609 
610     \sa allowedAreas
611 */
612 
setMovable(bool movable)613 void QToolBar::setMovable(bool movable)
614 {
615     Q_D(QToolBar);
616     if (!movable == !d->movable)
617         return;
618     d->movable = movable;
619     d->layout->invalidate();
620     emit movableChanged(d->movable);
621 }
622 
isMovable() const623 bool QToolBar::isMovable() const
624 {
625     Q_D(const QToolBar);
626     return d->movable;
627 }
628 
629 /*!
630     \property QToolBar::floatable
631     \brief whether the toolbar can be dragged and dropped as an independent window.
632 
633     The default is true.
634 */
isFloatable() const635 bool QToolBar::isFloatable() const
636 {
637     Q_D(const QToolBar);
638     return d->floatable;
639 }
640 
setFloatable(bool floatable)641 void QToolBar::setFloatable(bool floatable)
642 {
643     Q_D(QToolBar);
644     d->floatable = floatable;
645 }
646 
647 /*!
648     \property QToolBar::floating
649     \brief whether the toolbar is an independent window.
650 
651     By default, this property is true.
652 
653     \sa QWidget::isWindow()
654 */
isFloating() const655 bool QToolBar::isFloating() const
656 {
657     return isWindow();
658 }
659 
660 /*!
661     \property QToolBar::allowedAreas
662     \brief areas where the toolbar may be placed
663 
664     The default is Qt::AllToolBarAreas.
665 
666     This property only makes sense if the toolbar is in a
667     QMainWindow.
668 
669     \sa movable
670 */
671 
setAllowedAreas(Qt::ToolBarAreas areas)672 void QToolBar::setAllowedAreas(Qt::ToolBarAreas areas)
673 {
674     Q_D(QToolBar);
675     areas &= Qt::ToolBarArea_Mask;
676     if (areas == d->allowedAreas)
677         return;
678     d->allowedAreas = areas;
679     emit allowedAreasChanged(d->allowedAreas);
680 }
681 
allowedAreas() const682 Qt::ToolBarAreas QToolBar::allowedAreas() const
683 {
684     Q_D(const QToolBar);
685 #ifdef Q_WS_MAC
686     if (QMainWindow *window = qobject_cast<QMainWindow *>(parentWidget())) {
687         if (window->unifiedTitleAndToolBarOnMac()) // Don't allow drags to the top (for now).
688             return (d->allowedAreas & ~Qt::TopToolBarArea);
689     }
690 #endif
691     return d->allowedAreas;
692 }
693 
694 /*! \property QToolBar::orientation
695     \brief orientation of the toolbar
696 
697     The default is Qt::Horizontal.
698 
699     This function should not be used when the toolbar is managed
700     by QMainWindow. You can use QMainWindow::addToolBar() or
701     QMainWindow::insertToolBar() if you wish to move a toolbar (that
702     is already added to a main window) to another Qt::ToolBarArea.
703 */
704 
setOrientation(Qt::Orientation orientation)705 void QToolBar::setOrientation(Qt::Orientation orientation)
706 {
707     Q_D(QToolBar);
708     if (orientation == d->orientation)
709         return;
710 
711     d->orientation = orientation;
712 
713     if (orientation == Qt::Vertical)
714  	setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred));
715     else
716  	setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
717 
718     d->layout->invalidate();
719     d->layout->activate();
720 
721     emit orientationChanged(d->orientation);
722 }
723 
orientation() const724 Qt::Orientation QToolBar::orientation() const
725 { Q_D(const QToolBar); return d->orientation; }
726 
727 /*!
728     \property QToolBar::iconSize
729     \brief size of icons in the toolbar.
730 
731     The default size is determined by the application's style and is
732     derived from the QStyle::PM_ToolBarIconSize pixel metric. It is
733     the maximum size an icon can have. Icons of smaller size will not
734     be scaled up.
735 */
736 
iconSize() const737 QSize QToolBar::iconSize() const
738 { Q_D(const QToolBar); return d->iconSize; }
739 
setIconSize(const QSize & iconSize)740 void QToolBar::setIconSize(const QSize &iconSize)
741 {
742     Q_D(QToolBar);
743     QSize sz = iconSize;
744     if (!sz.isValid()) {
745         QMainWindow *mw = qobject_cast<QMainWindow *>(parentWidget());
746         if (mw && mw->layout()) {
747             QLayout *layout = mw->layout();
748             int i = 0;
749             QLayoutItem *item = 0;
750             do {
751                 item = layout->itemAt(i++);
752                 if (item && (item->widget() == this))
753                     sz = mw->iconSize();
754             } while (!sz.isValid() && item != 0);
755         }
756     }
757     if (!sz.isValid()) {
758         const int metric = style()->pixelMetric(QStyle::PM_ToolBarIconSize, 0, this);
759         sz = QSize(metric, metric);
760     }
761     if (d->iconSize != sz) {
762         d->iconSize = sz;
763         setMinimumSize(0, 0);
764         emit iconSizeChanged(d->iconSize);
765     }
766     d->explicitIconSize = iconSize.isValid();
767 
768     d->layout->invalidate();
769 }
770 
771 /*!
772     \property QToolBar::toolButtonStyle
773     \brief the style of toolbar buttons
774 
775     This property defines the style of all tool buttons that are added
776     as \l{QAction}s. Note that if you add a QToolButton with the
777     addWidget() method, it will not get this button style.
778 
779     The default is Qt::ToolButtonIconOnly.
780 */
781 
toolButtonStyle() const782 Qt::ToolButtonStyle QToolBar::toolButtonStyle() const
783 { Q_D(const QToolBar); return d->toolButtonStyle; }
784 
setToolButtonStyle(Qt::ToolButtonStyle toolButtonStyle)785 void QToolBar::setToolButtonStyle(Qt::ToolButtonStyle toolButtonStyle)
786 {
787     Q_D(QToolBar);
788     d->explicitToolButtonStyle = true;
789     if (d->toolButtonStyle == toolButtonStyle)
790         return;
791     d->toolButtonStyle = toolButtonStyle;
792     setMinimumSize(0, 0);
793     emit toolButtonStyleChanged(d->toolButtonStyle);
794 }
795 
796 /*!
797     Removes all actions from the toolbar.
798 
799     \sa removeAction()
800 */
clear()801 void QToolBar::clear()
802 {
803     QList<QAction *> actions = this->actions();
804     for(int i = 0; i < actions.size(); i++)
805         removeAction(actions.at(i));
806 }
807 
808 /*!
809     \overload
810 
811     Creates a new action with the given \a text. This action is added to
812     the end of the toolbar.
813 */
addAction(const QString & text)814 QAction *QToolBar::addAction(const QString &text)
815 {
816     QAction *action = new QAction(text, this);
817     addAction(action);
818     return action;
819 }
820 
821 /*!
822     \overload
823 
824     Creates a new action with the given \a icon and \a text. This
825     action is added to the end of the toolbar.
826 */
addAction(const QIcon & icon,const QString & text)827 QAction *QToolBar::addAction(const QIcon &icon, const QString &text)
828 {
829     QAction *action = new QAction(icon, text, this);
830     addAction(action);
831     return action;
832 }
833 
834 /*!
835     \overload
836 
837     Creates a new action with the given \a text. This action is added to
838     the end of the toolbar. The action's \link QAction::triggered()
839     triggered()\endlink signal is connected to \a member in \a
840     receiver.
841 */
addAction(const QString & text,const QObject * receiver,const char * member)842 QAction *QToolBar::addAction(const QString &text,
843                              const QObject *receiver, const char* member)
844 {
845     QAction *action = new QAction(text, this);
846     QObject::connect(action, SIGNAL(triggered(bool)), receiver, member);
847     addAction(action);
848     return action;
849 }
850 
851 /*!
852     \overload
853 
854     Creates a new action with the icon \a icon and text \a text. This
855     action is added to the end of the toolbar. The action's \link
856     QAction::triggered() triggered()\endlink signal is connected to \a
857     member in \a receiver.
858 */
addAction(const QIcon & icon,const QString & text,const QObject * receiver,const char * member)859 QAction *QToolBar::addAction(const QIcon &icon, const QString &text,
860                              const QObject *receiver, const char* member)
861 {
862     QAction *action = new QAction(icon, text, this);
863     QObject::connect(action, SIGNAL(triggered(bool)), receiver, member);
864     addAction(action);
865     return action;
866 }
867 
868 /*!
869      Adds a separator to the end of the toolbar.
870 
871      \sa insertSeparator()
872 */
addSeparator()873 QAction *QToolBar::addSeparator()
874 {
875     QAction *action = new QAction(this);
876     action->setSeparator(true);
877     addAction(action);
878     return action;
879 }
880 
881 /*!
882     Inserts a separator into the toolbar in front of the toolbar
883     item associated with the \a before action.
884 
885     \sa addSeparator()
886 */
insertSeparator(QAction * before)887 QAction *QToolBar::insertSeparator(QAction *before)
888 {
889     QAction *action = new QAction(this);
890     action->setSeparator(true);
891     insertAction(before, action);
892     return action;
893 }
894 
895 /*!
896     Adds the given \a widget to the toolbar as the toolbar's last
897     item.
898 
899     The toolbar takes ownership of \a widget.
900 
901     If you add a QToolButton with this method, the tools bar's
902     Qt::ToolButtonStyle will not be respected.
903 
904     \note You should use QAction::setVisible() to change the
905     visibility of the widget. Using QWidget::setVisible(),
906     QWidget::show() and QWidget::hide() does not work.
907 
908     \sa insertWidget()
909 */
addWidget(QWidget * widget)910 QAction *QToolBar::addWidget(QWidget *widget)
911 {
912     QWidgetAction *action = new QWidgetAction(this);
913     action->setDefaultWidget(widget);
914     action->d_func()->autoCreated = true;
915     addAction(action);
916     return action;
917 }
918 
919 /*!
920     Inserts the given \a widget in front of the toolbar item
921     associated with the \a before action.
922 
923     Note: You should use QAction::setVisible() to change the
924     visibility of the widget. Using QWidget::setVisible(),
925     QWidget::show() and QWidget::hide() does not work.
926 
927     \sa addWidget()
928 */
insertWidget(QAction * before,QWidget * widget)929 QAction *QToolBar::insertWidget(QAction *before, QWidget *widget)
930 {
931     QWidgetAction *action = new QWidgetAction(this);
932     action->setDefaultWidget(widget);
933     action->d_func()->autoCreated = true;
934     insertAction(before, action);
935     return action;
936 }
937 
938 /*!
939     \internal
940 
941     Returns the geometry of the toolbar item associated with the given
942     \a action, or an invalid QRect if no matching item is found.
943 */
actionGeometry(QAction * action) const944 QRect QToolBar::actionGeometry(QAction *action) const
945 {
946     Q_D(const QToolBar);
947 
948     int index = d->layout->indexOf(action);
949     if (index == -1)
950         return QRect();
951     return d->layout->itemAt(index)->widget()->geometry();
952 }
953 
954 /*!
955     Returns the action at point \a p. This function returns zero if no
956     action was found.
957 
958     \sa QWidget::childAt()
959 */
actionAt(const QPoint & p) const960 QAction *QToolBar::actionAt(const QPoint &p) const
961 {
962     Q_D(const QToolBar);
963     QWidget *widget = childAt(p);
964     int index = d->layout->indexOf(widget);
965     if (index == -1)
966         return 0;
967     QLayoutItem *item = d->layout->itemAt(index);
968     return static_cast<QToolBarItem*>(item)->action;
969 }
970 
971 /*! \fn QAction *QToolBar::actionAt(int x, int y) const
972     \overload
973 
974     Returns the action at the point \a x, \a y. This function returns
975     zero if no action was found.
976 */
977 
978 /*! \reimp */
actionEvent(QActionEvent * event)979 void QToolBar::actionEvent(QActionEvent *event)
980 {
981     Q_D(QToolBar);
982     QAction *action = event->action();
983     QWidgetAction *widgetAction = qobject_cast<QWidgetAction *>(action);
984 
985     switch (event->type()) {
986         case QEvent::ActionAdded: {
987             Q_ASSERT_X(widgetAction == 0 || d->layout->indexOf(widgetAction) == -1,
988                         "QToolBar", "widgets cannot be inserted multiple times");
989 
990             // reparent the action to this toolbar if it has been created
991             // using the addAction(text) etc. convenience functions, to
992             // preserve Qt 4.1.x behavior. The widget is already
993             // reparented to us due to the createWidget call inside
994             // createItem()
995             if (widgetAction != 0 && widgetAction->d_func()->autoCreated)
996                 widgetAction->setParent(this);
997 
998             int index = d->layout->count();
999             if (event->before()) {
1000                 index = d->layout->indexOf(event->before());
1001                 Q_ASSERT_X(index != -1, "QToolBar::insertAction", "internal error");
1002             }
1003             d->layout->insertAction(index, action);
1004             break;
1005         }
1006 
1007         case QEvent::ActionChanged:
1008             d->layout->invalidate();
1009             break;
1010 
1011         case QEvent::ActionRemoved: {
1012             int index = d->layout->indexOf(action);
1013             if (index != -1) {
1014                 delete d->layout->takeAt(index);
1015             }
1016             break;
1017         }
1018 
1019         default:
1020             Q_ASSERT_X(false, "QToolBar::actionEvent", "internal error");
1021     }
1022 }
1023 
1024 /*! \reimp */
changeEvent(QEvent * event)1025 void QToolBar::changeEvent(QEvent *event)
1026 {
1027     Q_D(QToolBar);
1028     switch (event->type()) {
1029     case QEvent::WindowTitleChange:
1030         d->toggleViewAction->setText(windowTitle());
1031         break;
1032     case QEvent::StyleChange:
1033         d->layout->invalidate();
1034         if (!d->explicitIconSize)
1035             setIconSize(QSize());
1036         d->layout->updateMarginAndSpacing();
1037         break;
1038     case QEvent::LayoutDirectionChange:
1039         d->layout->invalidate();
1040         break;
1041     default:
1042         break;
1043     }
1044     QWidget::changeEvent(event);
1045 }
1046 
1047 /*! \reimp */
paintEvent(QPaintEvent *)1048 void QToolBar::paintEvent(QPaintEvent *)
1049 {
1050     Q_D(QToolBar);
1051 
1052     QPainter p(this);
1053     QStyle *style = this->style();
1054     QStyleOptionToolBar opt;
1055     initStyleOption(&opt);
1056 
1057     if (d->layout->expanded || d->layout->animating || isWindow()) {
1058         //if the toolbar is expended, we need to fill the background with the window color
1059         //because some styles may expects that.
1060         p.fillRect(opt.rect, palette().background());
1061         style->drawControl(QStyle::CE_ToolBar, &opt, &p, this);
1062         style->drawPrimitive(QStyle::PE_FrameMenu, &opt, &p, this);
1063     } else {
1064         style->drawControl(QStyle::CE_ToolBar, &opt, &p, this);
1065     }
1066 
1067     opt.rect = style->subElementRect(QStyle::SE_ToolBarHandle, &opt, this);
1068     if (opt.rect.isValid())
1069         style->drawPrimitive(QStyle::PE_IndicatorToolBarHandle, &opt, &p, this);
1070 }
1071 
1072 /*
1073     Checks if an expanded toolbar has to wait for this popup to close before
1074     the toolbar collapses. This is true if
1075     1) the popup has the toolbar in its parent chain,
1076     2) the popup is a menu whose menuAction is somewhere in the toolbar.
1077 */
waitForPopup(QToolBar * tb,QWidget * popup)1078 static bool waitForPopup(QToolBar *tb, QWidget *popup)
1079 {
1080     if (popup == 0 || popup->isHidden())
1081         return false;
1082 
1083     QWidget *w = popup;
1084     while (w != 0) {
1085         if (w == tb)
1086             return true;
1087         w = w->parentWidget();
1088     }
1089 
1090     QMenu *menu = qobject_cast<QMenu*>(popup);
1091     if (menu == 0)
1092         return false;
1093 
1094     QAction *action = menu->menuAction();
1095     QList<QWidget*> widgets = action->associatedWidgets();
1096     for (int i = 0; i < widgets.count(); ++i) {
1097         if (waitForPopup(tb, widgets.at(i)))
1098             return true;
1099     }
1100 
1101     return false;
1102 }
1103 
1104 #if defined(Q_WS_MAC)
toolbarInUnifiedToolBar(QToolBar * toolbar)1105 static bool toolbarInUnifiedToolBar(QToolBar *toolbar)
1106 {
1107     const QMainWindow *mainWindow = qobject_cast<const QMainWindow *>(toolbar->parentWidget());
1108     return mainWindow && mainWindow->unifiedTitleAndToolBarOnMac()
1109             && mainWindow->toolBarArea(toolbar) == Qt::TopToolBarArea;
1110 }
1111 #endif
1112 
1113 /*! \reimp */
event(QEvent * event)1114 bool QToolBar::event(QEvent *event)
1115 {
1116     Q_D(QToolBar);
1117 
1118     switch (event->type()) {
1119     case QEvent::Timer:
1120         if (d->waitForPopupTimer.timerId() == static_cast<QTimerEvent*>(event)->timerId()) {
1121             QWidget *w = QApplication::activePopupWidget();
1122             if (!waitForPopup(this, w)) {
1123                 d->waitForPopupTimer.stop();
1124                 if (!this->underMouse())
1125                     d->layout->setExpanded(false);
1126             }
1127         }
1128         break;
1129     case QEvent::Hide:
1130         if (!isHidden())
1131             break;
1132         // fallthrough intended
1133     case QEvent::Show:
1134         d->toggleViewAction->setChecked(event->type() == QEvent::Show);
1135         emit visibilityChanged(event->type() == QEvent::Show);
1136 #if defined(Q_WS_MAC)
1137         if (toolbarInUnifiedToolBar(this)) {
1138              // I can static_cast because I did the qobject_cast in the if above, therefore
1139             // we must have a QMainWindowLayout here.
1140             QMainWindowLayout *mwLayout = qt_mainwindow_layout(qobject_cast<QMainWindow *>(parentWidget()));
1141             mwLayout->fixSizeInUnifiedToolbar(this);
1142             mwLayout->syncUnifiedToolbarVisibility();
1143         }
1144 #  if !defined(QT_MAC_USE_COCOA)
1145         // Fall through
1146     case QEvent::LayoutRequest: {
1147         // There's currently no way to invalidate the size and let
1148         // HIToolbar know about it. This forces a re-check.
1149         int earlyResult = -1;
1150         if (QMainWindow *mainWindow = qobject_cast<QMainWindow *>(parentWidget())) {
1151             bool needUpdate = true;
1152             if (event->type() == QEvent::LayoutRequest) {
1153                 QSize oldSizeHint = sizeHint();
1154                 earlyResult = QWidget::event(event) ? 1 : 0;
1155                 needUpdate = oldSizeHint != sizeHint();
1156             }
1157 
1158             if (needUpdate) {
1159                 OSWindowRef windowRef = qt_mac_window_for(mainWindow);
1160                 if (toolbarInUnifiedToolBar(this)
1161                         && macWindowToolbarIsVisible(windowRef))   {
1162                     DisableScreenUpdates();
1163                     macWindowToolbarShow(this, false);
1164                     macWindowToolbarShow(this, true);
1165                     EnableScreenUpdates();
1166                 }
1167             }
1168 
1169             if (earlyResult != -1)
1170                 return earlyResult;
1171         }
1172     }
1173 #  endif // !QT_MAC_USE_COCOA
1174 #endif // Q_WS_MAC
1175         break;
1176     case QEvent::ParentChange:
1177         d->layout->checkUsePopupMenu();
1178 #if defined(Q_WS_MAC)
1179         if (parentWidget() && parentWidget()->isWindow())
1180             qt_mac_updateToolBarButtonHint(parentWidget());
1181 #endif
1182         break;
1183 
1184     case QEvent::MouseButtonPress: {
1185         if (d->mousePressEvent(static_cast<QMouseEvent*>(event)))
1186             return true;
1187         break;
1188     }
1189     case QEvent::MouseButtonRelease:
1190         if (d->mouseReleaseEvent(static_cast<QMouseEvent*>(event)))
1191             return true;
1192         break;
1193     case QEvent::HoverEnter:
1194     case QEvent::HoverLeave:
1195         // there's nothing special to do here and we don't want to update the whole widget
1196         return true;
1197     case QEvent::HoverMove: {
1198 #ifndef QT_NO_CURSOR
1199         QHoverEvent *e = static_cast<QHoverEvent*>(event);
1200         QStyleOptionToolBar opt;
1201         initStyleOption(&opt);
1202         if (style()->subElementRect(QStyle::SE_ToolBarHandle, &opt, this).contains(e->pos()))
1203             setCursor(Qt::SizeAllCursor);
1204         else
1205             unsetCursor();
1206 #endif
1207         break;
1208     }
1209     case QEvent::MouseMove:
1210         if (d->mouseMoveEvent(static_cast<QMouseEvent*>(event)))
1211             return true;
1212         break;
1213 #ifdef Q_WS_WINCE
1214     case QEvent::ContextMenu:
1215         {
1216             QContextMenuEvent* contextMenuEvent = static_cast<QContextMenuEvent*>(event);
1217             QWidget* child = childAt(contextMenuEvent->pos());
1218             QAbstractButton* button = qobject_cast<QAbstractButton*>(child);
1219             if (button)
1220                 button->setDown(false);
1221         }
1222         break;
1223 #endif
1224     case QEvent::Leave:
1225         if (d->state != 0 && d->state->dragging) {
1226 #ifdef Q_OS_WIN
1227             // This is a workaround for loosing the mouse on Vista.
1228             QPoint pos = QCursor::pos();
1229             QMouseEvent fake(QEvent::MouseMove, mapFromGlobal(pos), pos, Qt::NoButton,
1230                              QApplication::mouseButtons(), QApplication::keyboardModifiers());
1231             d->mouseMoveEvent(&fake);
1232 #endif
1233         } else {
1234             if (!d->layout->expanded)
1235                 break;
1236 
1237             QWidget *w = QApplication::activePopupWidget();
1238             if (waitForPopup(this, w)) {
1239                 d->waitForPopupTimer.start(POPUP_TIMER_INTERVAL, this);
1240                 break;
1241             }
1242 
1243             d->waitForPopupTimer.stop();
1244             d->layout->setExpanded(false);
1245             break;
1246         }
1247     default:
1248         break;
1249     }
1250     return QWidget::event(event);
1251 }
1252 
1253 /*!
1254     Returns a checkable action that can be used to show or hide this
1255     toolbar.
1256 
1257     The action's text is set to the toolbar's window title.
1258 
1259     \sa QAction::text QWidget::windowTitle
1260 */
toggleViewAction() const1261 QAction *QToolBar::toggleViewAction() const
1262 { Q_D(const QToolBar); return d->toggleViewAction; }
1263 
1264 /*!
1265     \fn void QToolBar::setLabel(const QString &label)
1266 
1267     Use setWindowTitle() instead.
1268 */
1269 
1270 /*!
1271     \fn QString QToolBar::label() const
1272 
1273     Use windowTitle() instead.
1274 */
1275 
1276 /*!
1277     \since 4.2
1278 
1279     Returns the widget associated with the specified \a action.
1280 
1281     \sa addWidget()
1282 */
widgetForAction(QAction * action) const1283 QWidget *QToolBar::widgetForAction(QAction *action) const
1284 {
1285     Q_D(const QToolBar);
1286 
1287     int index = d->layout->indexOf(action);
1288     if (index == -1)
1289         return 0;
1290 
1291     return d->layout->itemAt(index)->widget();
1292 }
1293 
1294 extern QMainWindowLayout *qt_mainwindow_layout(const QMainWindow *window);
1295 
1296 /*!
1297     \internal
1298 */
initStyleOption(QStyleOptionToolBar * option) const1299 void QToolBar::initStyleOption(QStyleOptionToolBar *option) const
1300 {
1301     Q_D(const QToolBar);
1302 
1303     if (!option)
1304         return;
1305 
1306     option->initFrom(this);
1307     if (orientation() == Qt::Horizontal)
1308         option->state |= QStyle::State_Horizontal;
1309     option->lineWidth = style()->pixelMetric(QStyle::PM_ToolBarFrameWidth, 0, this);
1310     option->features = d->layout->movable()
1311                         ? QStyleOptionToolBar::Movable
1312                         : QStyleOptionToolBar::None;
1313     // if the tool bar is not in a QMainWindow, this will make the painting right
1314     option->toolBarArea = Qt::NoToolBarArea;
1315 
1316     // Add more styleoptions if the toolbar has been added to a mainwindow.
1317     QMainWindow *mainWindow = qobject_cast<QMainWindow *>(parentWidget());
1318 
1319     if (!mainWindow)
1320         return;
1321 
1322     QMainWindowLayout *layout = qt_mainwindow_layout(mainWindow);
1323     Q_ASSERT_X(layout != 0, "QToolBar::initStyleOption()",
1324                "QMainWindow->layout() != QMainWindowLayout");
1325 
1326     layout->getStyleOptionInfo(option, const_cast<QToolBar *>(this));
1327 }
1328 
1329 /*!
1330     \reimp
1331 */
childEvent(QChildEvent * event)1332 void QToolBar::childEvent(QChildEvent *event) // ### remove me in 5.0
1333 {
1334     QWidget::childEvent(event);
1335 }
1336 
1337 /*!
1338     \reimp
1339 */
resizeEvent(QResizeEvent * event)1340 void QToolBar::resizeEvent(QResizeEvent *event) // ### remove me in 5.0
1341 {
1342     QWidget::resizeEvent(event);
1343 }
1344 
1345 QT_END_NAMESPACE
1346 
1347 #include "moc_qtoolbar.cpp"
1348 
1349 #endif // QT_NO_TOOLBAR
1350