/**************************************************************************** ** ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this ** file. Please review the following information to ensure the GNU Lesser ** General Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU General ** Public License version 3.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of this ** file. Please review the following information to ensure the GNU General ** Public License version 3.0 requirements will be met: ** http://www.gnu.org/copyleft/gpl.html. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include #include #include #include "qmainwindowlayout_p.h" #include "qtoolbarextension_p.h" #include "qtoolbarlayout_p.h" #include "qtoolbarseparator_p.h" #ifndef QT_NO_TOOLBAR QT_BEGIN_NAMESPACE // qmainwindow.cpp extern QMainWindowLayout *qt_mainwindow_layout(const QMainWindow *window); /****************************************************************************** ** QToolBarItem */ QToolBarItem::QToolBarItem(QWidget *widget) : QWidgetItem(widget), action(0), customWidget(false) { } bool QToolBarItem::isEmpty() const { return action == 0 || !action->isVisible(); } /****************************************************************************** ** QToolBarLayout */ QToolBarLayout::QToolBarLayout(QWidget *parent) : QLayout(parent), expanded(false), animating(false), dirty(true), expanding(false), empty(true), expandFlag(false), popupMenu(0) { QToolBar *tb = qobject_cast(parent); if (!tb) return; extension = new QToolBarExtension(tb); extension->setFocusPolicy(Qt::NoFocus); extension->hide(); QObject::connect(tb, SIGNAL(orientationChanged(Qt::Orientation)), extension, SLOT(setOrientation(Qt::Orientation))); setUsePopupMenu(qobject_cast(tb->parentWidget()) == 0); } QToolBarLayout::~QToolBarLayout() { while (!items.isEmpty()) { QToolBarItem *item = items.takeFirst(); if (QWidgetAction *widgetAction = qobject_cast(item->action)) { if (item->customWidget) widgetAction->releaseWidget(item->widget()); } delete item; } } void QToolBarLayout::updateMarginAndSpacing() { QToolBar *tb = qobject_cast(parentWidget()); if (!tb) return; QStyle *style = tb->style(); QStyleOptionToolBar opt; tb->initStyleOption(&opt); setMargin(style->pixelMetric(QStyle::PM_ToolBarItemMargin, &opt, tb) + style->pixelMetric(QStyle::PM_ToolBarFrameWidth, &opt, tb)); setSpacing(style->pixelMetric(QStyle::PM_ToolBarItemSpacing, &opt, tb)); } bool QToolBarLayout::hasExpandFlag() const { return expandFlag; } void QToolBarLayout::setUsePopupMenu(bool set) { if (!dirty && ((popupMenu == 0) == set)) invalidate(); if (!set) { QObject::connect(extension, SIGNAL(clicked(bool)), this, SLOT(setExpanded(bool)), Qt::UniqueConnection); extension->setPopupMode(QToolButton::DelayedPopup); extension->setMenu(0); delete popupMenu; popupMenu = 0; } else { QObject::disconnect(extension, SIGNAL(clicked(bool)), this, SLOT(setExpanded(bool))); extension->setPopupMode(QToolButton::InstantPopup); if (!popupMenu) { popupMenu = new QMenu(extension); } extension->setMenu(popupMenu); } } void QToolBarLayout::checkUsePopupMenu() { QToolBar *tb = static_cast(parent()); QMainWindow *mw = qobject_cast(tb->parent()); Qt::Orientation o = tb->orientation(); setUsePopupMenu(!mw || tb->isFloating() || perp(o, expandedSize(mw->size())) >= perp(o, mw->size())); } void QToolBarLayout::addItem(QLayoutItem*) { qWarning() << "QToolBarLayout::addItem(): please use addAction() instead"; return; } QLayoutItem *QToolBarLayout::itemAt(int index) const { if (index < 0 || index >= items.count()) return 0; return items.at(index); } QLayoutItem *QToolBarLayout::takeAt(int index) { if (index < 0 || index >= items.count()) return 0; QToolBarItem *item = items.takeAt(index); if (popupMenu) popupMenu->removeAction(item->action); QWidgetAction *widgetAction = qobject_cast(item->action); if (widgetAction != 0 && item->customWidget) { widgetAction->releaseWidget(item->widget()); } else { // destroy the QToolButton/QToolBarSeparator item->widget()->hide(); item->widget()->deleteLater(); } invalidate(); return item; } void QToolBarLayout::insertAction(int index, QAction *action) { index = qMax(0, index); index = qMin(items.count(), index); QToolBarItem *item = createItem(action); if (item) { items.insert(index, item); invalidate(); } } int QToolBarLayout::indexOf(QAction *action) const { for (int i = 0; i < items.count(); ++i) { if (items.at(i)->action == action) return i; } return -1; } int QToolBarLayout::count() const { return items.count(); } bool QToolBarLayout::isEmpty() const { if (dirty) updateGeomArray(); return empty; } void QToolBarLayout::invalidate() { dirty = true; QLayout::invalidate(); } Qt::Orientations QToolBarLayout::expandingDirections() const { if (dirty) updateGeomArray(); QToolBar *tb = qobject_cast(parentWidget()); if (!tb) return Qt::Orientations(0); Qt::Orientation o = tb->orientation(); return expanding ? Qt::Orientations(o) : Qt::Orientations(0); } bool QToolBarLayout::movable() const { QToolBar *tb = qobject_cast(parentWidget()); if (!tb) return false; QMainWindow *win = qobject_cast(tb->parentWidget()); return tb->isMovable() && win != 0; } void QToolBarLayout::updateGeomArray() const { if (!dirty) return; QToolBarLayout *that = const_cast(this); QToolBar *tb = qobject_cast(parentWidget()); if (!tb) return; QStyle *style = tb->style(); QStyleOptionToolBar opt; tb->initStyleOption(&opt); const int handleExtent = movable() ? style->pixelMetric(QStyle::PM_ToolBarHandleExtent, &opt, tb) : 0; const int margin = this->margin(); const int spacing = this->spacing(); const int extensionExtent = style->pixelMetric(QStyle::PM_ToolBarExtensionExtent, &opt, tb); Qt::Orientation o = tb->orientation(); that->minSize = QSize(0, 0); that->hint = QSize(0, 0); rperp(o, that->minSize) = style->pixelMetric(QStyle::PM_ToolBarHandleExtent, &opt, tb); rperp(o, that->hint) = style->pixelMetric(QStyle::PM_ToolBarHandleExtent, &opt, tb); that->expanding = false; that->empty = false; QVector a(items.count() + 1); // + 1 for the stretch int count = 0; for (int i = 0; i < items.count(); ++i) { QToolBarItem *item = items.at(i); QSize max = item->maximumSize(); QSize min = item->minimumSize(); QSize hint = item->sizeHint(); Qt::Orientations exp = item->expandingDirections(); bool empty = item->isEmpty(); that->expanding = expanding || exp & o; if (item->widget()) { if ((item->widget()->sizePolicy().horizontalPolicy() & QSizePolicy::ExpandFlag)) { that->expandFlag = true; } } if (!empty) { if (count == 0) // the minimum size only displays one widget rpick(o, that->minSize) += pick(o, min); int s = perp(o, minSize); rperp(o, that->minSize) = qMax(s, perp(o, min)); //we only add spacing before item (ie never before the first one) rpick(o, that->hint) += (count == 0 ? 0 : spacing) + pick(o, hint); s = perp(o, that->hint); rperp(o, that->hint) = qMax(s, perp(o, hint)); ++count; } a[i].sizeHint = pick(o, hint); a[i].maximumSize = pick(o, max); a[i].minimumSize = pick(o, min); a[i].expansive = exp & o; if (o == Qt::Horizontal) a[i].stretch = item->widget()->sizePolicy().horizontalStretch(); else a[i].stretch = item->widget()->sizePolicy().verticalStretch(); a[i].empty = empty; } that->geomArray = a; that->empty = count == 0; rpick(o, that->minSize) += handleExtent; that->minSize += QSize(2*margin, 2*margin); if (items.count() > 1) rpick(o, that->minSize) += spacing + extensionExtent; rpick(o, that->hint) += handleExtent; that->hint += QSize(2*margin, 2*margin); that->dirty = false; #ifdef Q_WS_MAC if (QMainWindow *mw = qobject_cast(parentWidget()->parentWidget())) { if (mw->unifiedTitleAndToolBarOnMac() && mw->toolBarArea(static_cast(parentWidget())) == Qt::TopToolBarArea) { if (expandFlag) { tb->setMaximumSize(0xFFFFFF, 0xFFFFFF); } else { tb->setMaximumSize(hint); } } } #endif that->dirty = false; } static bool defaultWidgetAction(QToolBarItem *item) { QWidgetAction *a = qobject_cast(item->action); return a != 0 && a->defaultWidget() == item->widget(); } void QToolBarLayout::setGeometry(const QRect &rect) { QToolBar *tb = qobject_cast(parentWidget()); if (!tb) return; QStyle *style = tb->style(); QStyleOptionToolBar opt; tb->initStyleOption(&opt); const int margin = this->margin(); const int extensionExtent = style->pixelMetric(QStyle::PM_ToolBarExtensionExtent, &opt, tb); Qt::Orientation o = tb->orientation(); QLayout::setGeometry(rect); bool ranOutOfSpace = false; if (!animating) ranOutOfSpace = layoutActions(rect.size()); if (expanded || animating || ranOutOfSpace) { Qt::ToolBarArea area = Qt::TopToolBarArea; if (QMainWindow *win = qobject_cast(tb->parentWidget())) area = win->toolBarArea(tb); QSize hint = sizeHint(); QPoint pos; rpick(o, pos) = pick(o, rect.bottomRight()) - margin - extensionExtent + 2; if (area == Qt::LeftToolBarArea || area == Qt::TopToolBarArea) rperp(o, pos) = perp(o, rect.topLeft()) + margin; else rperp(o, pos) = perp(o, rect.bottomRight()) - margin - (perp(o, hint) - 2*margin) + 1; QSize size; rpick(o, size) = extensionExtent; rperp(o, size) = perp(o, hint) - 2*margin; QRect r(pos, size); if (o == Qt::Horizontal) r = QStyle::visualRect(parentWidget()->layoutDirection(), rect, r); extension->setGeometry(r); if (extension->isHidden()) extension->show(); } else { if (!extension->isHidden()) extension->hide(); } #ifdef Q_WS_MAC // Nothing to do for Carbon... probably # ifdef QT_MAC_USE_COCOA if (QMainWindow *win = qobject_cast(tb->parentWidget())) { Qt::ToolBarArea area = win->toolBarArea(tb); if (win->unifiedTitleAndToolBarOnMac() && area == Qt::TopToolBarArea) { qt_mainwindow_layout(win)->fixSizeInUnifiedToolbar(tb); } } # endif #endif } bool QToolBarLayout::layoutActions(const QSize &size) { if (dirty) updateGeomArray(); QRect rect(0, 0, size.width(), size.height()); QList showWidgets, hideWidgets; QToolBar *tb = qobject_cast(parentWidget()); if (!tb) return false; QStyle *style = tb->style(); QStyleOptionToolBar opt; tb->initStyleOption(&opt); const int handleExtent = movable() ? style->pixelMetric(QStyle::PM_ToolBarHandleExtent, &opt, tb) : 0; const int margin = this->margin(); const int spacing = this->spacing(); const int extensionExtent = style->pixelMetric(QStyle::PM_ToolBarExtensionExtent, &opt, tb); Qt::Orientation o = tb->orientation(); bool extensionMenuContainsOnlyWidgetActions = true; int space = pick(o, rect.size()) - 2*margin - handleExtent; if (space <= 0) return false; // nothing to do. if(popupMenu) popupMenu->clear(); bool ranOutOfSpace = false; int rows = 0; int rowPos = perp(o, rect.topLeft()) + margin; int i = 0; while (i < items.count()) { QVector a = geomArray; int start = i; int size = 0; int prev = -1; int rowHeight = 0; int count = 0; int maximumSize = 0; bool expansiveRow = false; for (; i < items.count(); ++i) { if (a[i].empty) continue; int newSize = size + (count == 0 ? 0 : spacing) + a[i].minimumSize; if (prev != -1 && newSize > space) { if (rows == 0) ranOutOfSpace = true; // do we have to move the previous item to the next line to make space for // the extension button? if (count > 1 && size + spacing + extensionExtent > space) i = prev; break; } if (expanded) rowHeight = qMax(rowHeight, perp(o, items.at(i)->sizeHint())); expansiveRow = expansiveRow || a[i].expansive; size = newSize; maximumSize += spacing + (a[i].expansive ? a[i].maximumSize : a[i].smartSizeHint()); prev = i; ++count; } // stretch at the end a[i].sizeHint = 0; a[i].maximumSize = QWIDGETSIZE_MAX; a[i].minimumSize = 0; a[i].expansive = true; a[i].stretch = 0; a[i].empty = true; if (expansiveRow && maximumSize < space) { expansiveRow = false; a[i].maximumSize = space - maximumSize; } qGeomCalc(a, start, i - start + (expansiveRow ? 0 : 1), 0, space - (ranOutOfSpace ? (extensionExtent + spacing) : 0), spacing); for (int j = start; j < i; ++j) { QToolBarItem *item = items.at(j); if (a[j].empty) { if (!item->widget()->isHidden()) hideWidgets << item->widget(); continue; } QPoint pos; rpick(o, pos) = margin + handleExtent + a[j].pos; rperp(o, pos) = rowPos; QSize size; rpick(o, size) = a[j].size; if (expanded) rperp(o, size) = rowHeight; else rperp(o, size) = perp(o, rect.size()) - 2*margin; QRect r(pos, size); if (o == Qt::Horizontal) r = QStyle::visualRect(parentWidget()->layoutDirection(), rect, r); item->setGeometry(r); if (item->widget()->isHidden()) showWidgets << item->widget(); } if (!expanded) { for (int j = i; j < items.count(); ++j) { QToolBarItem *item = items.at(j); if (!item->widget()->isHidden()) hideWidgets << item->widget(); if (popupMenu) { if (!defaultWidgetAction(item)) { popupMenu->addAction(item->action); extensionMenuContainsOnlyWidgetActions = false; } } } break; } rowPos += rowHeight + spacing; ++rows; } // if we are using a popup menu, not the expadning toolbar effect, we cannot move custom // widgets into the menu. If only custom widget actions are chopped off, the popup menu // is empty. So we show the little extension button to show something is chopped off, // but we make it disabled. extension->setEnabled(popupMenu == 0 || !extensionMenuContainsOnlyWidgetActions); // we have to do the show/hide here, because it triggers more calls to setGeometry :( for (int i = 0; i < showWidgets.count(); ++i) showWidgets.at(i)->show(); for (int i = 0; i < hideWidgets.count(); ++i) hideWidgets.at(i)->hide(); return ranOutOfSpace; } QSize QToolBarLayout::expandedSize(const QSize &size) const { if (dirty) updateGeomArray(); QToolBar *tb = qobject_cast(parentWidget()); if (!tb) return QSize(0, 0); QMainWindow *win = qobject_cast(tb->parentWidget()); Qt::Orientation o = tb->orientation(); QStyle *style = tb->style(); QStyleOptionToolBar opt; tb->initStyleOption(&opt); const int handleExtent = movable() ? style->pixelMetric(QStyle::PM_ToolBarHandleExtent, &opt, tb) : 0; const int margin = this->margin(); const int spacing = this->spacing(); const int extensionExtent = style->pixelMetric(QStyle::PM_ToolBarExtensionExtent, &opt, tb); int total_w = 0; int count = 0; for (int x = 0; x < items.count(); ++x) { if (!geomArray[x].empty) { total_w += (count == 0 ? 0 : spacing) + geomArray[x].minimumSize; ++count; } } if (count == 0) return QSize(0, 0); int min_w = pick(o, size); int rows = (int)qSqrt(qreal(count)); if (rows == 1) ++rows; // we want to expand to at least two rows int space = total_w/rows + spacing + extensionExtent; space = qMax(space, min_w - 2*margin - handleExtent); if (win != 0) space = qMin(space, pick(o, win->size()) - 2*margin - handleExtent); int w = 0; int h = 0; int i = 0; while (i < items.count()) { int count = 0; int size = 0; int prev = -1; int rowHeight = 0; for (; i < items.count(); ++i) { if (geomArray[i].empty) continue; int newSize = size + (count == 0 ? 0 : spacing) + geomArray[i].minimumSize; rowHeight = qMax(rowHeight, perp(o, items.at(i)->sizeHint())); if (prev != -1 && newSize > space) { if (count > 1 && size + spacing + extensionExtent > space) { size -= spacing + geomArray[prev].minimumSize; i = prev; } break; } size = newSize; prev = i; ++count; } w = qMax(size, w); h += rowHeight + spacing; } w += 2*margin + handleExtent + spacing + extensionExtent; w = qMax(w, min_w); if (win != 0) w = qMin(w, pick(o, win->size())); h += 2*margin - spacing; //there is no spacing before the first row QSize result; rpick(o, result) = w; rperp(o, result) = h; return result; } void QToolBarLayout::setExpanded(bool exp) { QWidget *tb = qobject_cast(parentWidget()); if (!tb) return; if (exp == expanded && !tb->isWindow()) return; expanded = exp; extension->setChecked(expanded); if (QMainWindow *win = qobject_cast(tb->parentWidget())) { #ifdef QT_NO_DOCKWIDGET animating = false; #else animating = !tb->isWindow() && win->isAnimated(); #endif QMainWindowLayout *layout = qt_mainwindow_layout(win); if (expanded) { tb->raise(); } else { QList path = layout->layoutState.indexOf(tb); if (!path.isEmpty()) { QRect rect = layout->layoutState.itemRect(path); layoutActions(rect.size()); } } layout->layoutState.toolBarAreaLayout.apply(animating); } } QSize QToolBarLayout::minimumSize() const { if (dirty) updateGeomArray(); return minSize; } QSize QToolBarLayout::sizeHint() const { if (dirty) updateGeomArray(); return hint; } QToolBarItem *QToolBarLayout::createItem(QAction *action) { bool customWidget = false; bool standardButtonWidget = false; QWidget *widget = 0; QToolBar *tb = qobject_cast(parentWidget()); if (!tb) return (QToolBarItem *)0; if (QWidgetAction *widgetAction = qobject_cast(action)) { widget = widgetAction->requestWidget(tb); if (widget != 0) { widget->setAttribute(Qt::WA_LayoutUsesWidgetRect); customWidget = true; } } else if (action->isSeparator()) { QToolBarSeparator *sep = new QToolBarSeparator(tb); connect(tb, SIGNAL(orientationChanged(Qt::Orientation)), sep, SLOT(setOrientation(Qt::Orientation))); widget = sep; } if (!widget) { QToolButton *button = new QToolButton(tb); button->setAutoRaise(true); button->setFocusPolicy(Qt::NoFocus); button->setIconSize(tb->iconSize()); button->setToolButtonStyle(tb->toolButtonStyle()); QObject::connect(tb, SIGNAL(iconSizeChanged(QSize)), button, SLOT(setIconSize(QSize))); QObject::connect(tb, SIGNAL(toolButtonStyleChanged(Qt::ToolButtonStyle)), button, SLOT(setToolButtonStyle(Qt::ToolButtonStyle))); button->setDefaultAction(action); QObject::connect(button, SIGNAL(triggered(QAction*)), tb, SIGNAL(actionTriggered(QAction*))); widget = button; standardButtonWidget = true; } widget->hide(); QToolBarItem *result = new QToolBarItem(widget); if (standardButtonWidget) result->setAlignment(Qt::AlignJustify); result->customWidget = customWidget; result->action = action; return result; } QT_END_NAMESPACE #endif // QT_NO_TOOLBAR