diff --git a/sublime/ideal.cpp b/sublime/ideal.cpp index 0d0fceca06..bba4765f01 100644 --- a/sublime/ideal.cpp +++ b/sublime/ideal.cpp @@ -1,1073 +1,1058 @@ /* Copyright 2007 Roberto Raggi Copyright 2007 Hamish Rodda Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "ideal.h" #include #include #include #include #include #include #include #include #include #include #include "area.h" #include "view.h" #include "document.h" #include "mainwindow.h" using namespace Sublime; IdealToolButton::IdealToolButton(Qt::DockWidgetArea area, QWidget *parent) : QToolButton(parent), _area(area) { setFocusPolicy(Qt::NoFocus); KAcceleratorManager::setNoAccel(this); setCheckable(true); setAutoRaise(true); setToolButtonStyle(Qt::ToolButtonTextBesideIcon); } Qt::Orientation IdealToolButton::orientation() const { if (_area == Qt::LeftDockWidgetArea || _area == Qt::RightDockWidgetArea) return Qt::Vertical; return Qt::Horizontal; } QSize IdealToolButton::sizeHint() const { ensurePolished(); QStyleOptionToolButton opt; initStyleOption(&opt); QFontMetrics fm = fontMetrics(); const int charWidth = fm.width(QLatin1Char('x')); QSize textSize = fm.size(Qt::TextShowMnemonic, opt.text); textSize.rwidth() += 2 * charWidth; const int spacing = 2; // ### FIXME int width = 4 + textSize.width() + opt.iconSize.width() + spacing; int height = 4 + qMax(textSize.height(), opt.iconSize.height()); if (orientation() == Qt::Vertical) return QSize(height, width); return QSize(width, height); } void IdealToolButton::paintEvent(QPaintEvent *event) { if (_area == Qt::TopDockWidgetArea || _area == Qt::BottomDockWidgetArea) { QToolButton::paintEvent(event); } else { QStyleOptionToolButton opt; initStyleOption(&opt); opt.rect.setSize(QSize(opt.rect.height(), opt.rect.width())); QPixmap pix(opt.rect.width(), opt.rect.height()); QStylePainter painter(&pix, this); painter.fillRect(pix.rect(), opt.palette.background()); painter.drawComplexControl(QStyle::CC_ToolButton, opt); painter.end(); QPainter p(this); if (_area == Qt::LeftDockWidgetArea) { p.translate(0, height()); p.rotate(-90); } else { p.translate(width(), 0); p.rotate(90); } p.drawPixmap(0, 0, pix); } } IdealButtonBarWidget::IdealButtonBarWidget(Qt::DockWidgetArea area, IdealMainWidget *parent) : QWidget(parent) , _area(area) , _actions(new QActionGroup(this)) { // TODO Only for now... _actions->setExclusive(true); (void) new IdealButtonBarLayout(orientation(), this); } KAction *IdealButtonBarWidget::addWidget(const QString& title, IdealDockWidget *dock, Area *area, View *view) { + kDebug(9504) << "adding widget"; KAction *action = new KAction(this); action->setCheckable(true); action->setText(title); action->setIcon(dock->widget()->windowIcon()); if (_area == Qt::BottomDockWidgetArea || _area == Qt::TopDockWidgetArea) dock->setFeatures( dock->features() | IdealDockWidget::DockWidgetVerticalTitleBar ); dock->setArea(area); dock->setView(view); dock->setDockWidgetArea(_area); connect(dock, SIGNAL(anchor(bool)), SLOT(anchor(bool))); connect(dock, SIGNAL(maximize(bool)), SLOT(maximize(bool))); _widgets[action] = dock; connect(action, SIGNAL(toggled(bool)), this, SLOT(showWidget(bool))); addAction(action); _actions->addAction(action); return action; } void IdealButtonBarWidget::removeAction(QAction * action) { _widgets.remove(action); delete _buttons.take(action); delete action; } Qt::Orientation IdealButtonBarWidget::orientation() const { if (_area == Qt::LeftDockWidgetArea || _area == Qt::RightDockWidgetArea) return Qt::Vertical; return Qt::Horizontal; } void IdealButtonBarWidget::showWidget(bool checked) { Q_ASSERT(parentWidget() != 0); - + QAction *action = qobject_cast(sender()); Q_ASSERT(action); IdealDockWidget *widget = _widgets.value(action); Q_ASSERT(widget); parentWidget()->showDockWidget(widget, checked); } -void IdealButtonBarWidget::resizeEvent(QResizeEvent *event) -{ - if (layout() != 0 && orientation() == Qt::Vertical) - static_cast(layout())->setHeight(event->size().height()); -} - void IdealButtonBarWidget::anchor(bool anchor) { parentWidget()->anchorDockWidget(anchor, this); } void IdealButtonBarWidget::maximize(bool maximized) { parentWidget()->maximizeDockWidget(maximized, this); } void IdealButtonBarWidget::actionEvent(QActionEvent *event) { QAction *action = qobject_cast(event->action()); if (! action) return; switch (event->type()) { case QEvent::ActionAdded: { if (! _buttons.contains(action)) { IdealToolButton *button = new IdealToolButton(_area); _buttons.insert(action, button); button->setText(action->text()); button->setIcon(action->icon()); button->setShortcut(QKeySequence()); button->setChecked(action->isChecked()); layout()->addWidget(button); connect(action, SIGNAL(toggled(bool)), SLOT(actionToggled(bool))); connect(button, SIGNAL(toggled(bool)), action, SLOT(setChecked(bool))); } } break; case QEvent::ActionRemoved: { if (IdealToolButton *button = _buttons.value(action)) { for (int index = 0; index < layout()->count(); ++index) { if (QLayoutItem *item = layout()->itemAt(index)) { if (item->widget() == button) { action->disconnect(this); delete layout()->takeAt(index); layout()->invalidate(); break; } } } } } break; case QEvent::ActionChanged: { if (IdealToolButton *button = _buttons.value(action)) { button->setText(action->text()); button->setIcon(action->icon()); button->setShortcut(QKeySequence()); Q_ASSERT(_widgets.contains(action)); _widgets[action]->setWindowTitle(action->text()); } } break; default: break; } } void IdealButtonBarWidget::actionToggled(bool state) { QAction* action = qobject_cast(sender()); Q_ASSERT(action); IdealToolButton* button = _buttons.value(action); Q_ASSERT(button); bool blocked = button->blockSignals(true); button->setChecked(state); button->blockSignals(blocked); } IdealDockWidget::IdealDockWidget(QWidget *parent) : QDockWidget(parent), m_area(0), m_view(0), m_docking_area(Qt::NoDockWidgetArea), m_maximized(false) { QAbstractButton *floatButton = qFindChild(this, QLatin1String("qt_dockwidget_floatbutton")); QAbstractButton *closeButton = qFindChild(this, QLatin1String("qt_dockwidget_closebutton")); if (floatButton && closeButton) { disconnect(floatButton, SIGNAL(clicked()), 0, 0); disconnect(closeButton, SIGNAL(clicked()), 0, 0); m_anchor = floatButton; m_anchor->setCheckable(true); m_anchor->setToolTip(i18n("Lock the tool")); m_anchor->setWhatsThis(i18n("Lock the tool

When a tool is unlocked, it " "will be automatically hidden when you click outside it. " "A locked tool will remain visible until you explicitly " "hide it, or switch to a different tool.")); connect(m_anchor, SIGNAL(toggled(bool)), SIGNAL(anchor(bool))); m_close = closeButton; m_close->setToolTip(i18n("Remove the tool")); m_close->setWhatsThis(i18n("Remove the tool

Removes this tool completely. " "You can add the tool again by using the " "View->Add Tool View command.")); connect(m_close, SIGNAL(clicked(bool)), this, SLOT(slotRemove())); } } IdealDockWidget::~IdealDockWidget() { } Area *IdealDockWidget::area() const { return m_area; } void IdealDockWidget::setArea(Area *area) { m_area = area; } View *IdealDockWidget::view() const { return m_view; } void IdealDockWidget::setView(View *view) { m_view = view; } Qt::DockWidgetArea IdealDockWidget::dockWidgetArea() const { return m_docking_area; } void IdealDockWidget::setDockWidgetArea(Qt::DockWidgetArea dockingArea) { m_docking_area = dockingArea; } void IdealDockWidget::mouseDoubleClickEvent(QMouseEvent *event) { event->accept(); setMaximized(!isMaximized()); slotMaximize(isMaximized()); } bool IdealDockWidget::isMaximized() const { return m_maximized; } void IdealDockWidget::setMaximized(bool maximized) { m_maximized = maximized; } bool IdealDockWidget::event(QEvent *e) { return QWidget::event(e); } bool IdealDockWidget::isAnchored() const { return m_anchor->isChecked(); } void IdealDockWidget::setAnchored(bool anchored, bool emitSignals) { bool blocked = false; if (!emitSignals) blocked = m_anchor->blockSignals(true); m_anchor->setChecked(anchored); if (!emitSignals) m_anchor->blockSignals(blocked); } void IdealDockWidget::slotMaximize(bool maximized) { #if 0 // ### fixme QStyle::StandardPixmap pix; if (maximized) pix = QStyle::SP_TitleBarNormalButton; else pix = QStyle::SP_TitleBarMaxButton; m_maximize->setIcon(style()->standardPixmap(pix)); #endif emit maximize(maximized); } void IdealDockWidget::slotRemove() { m_area->removeToolView(m_view); } void IdealDockWidget::contextMenuEvent(QContextMenuEvent *event) { KMenu menu; menu.addTitle(i18n("Position")); QActionGroup *g = new QActionGroup(this); QAction *left = new QAction(i18n("Left"), g); QAction *bottom = new QAction(i18n("Bottom"), g); QAction *right = new QAction(i18n("Right"), g); QAction *top = new QAction(i18n("Top"), g); QAction* actions[] = {left, bottom, right, top}; for (int i = 0; i < 4; ++i) { menu.addAction(actions[i]); actions[i]->setCheckable(true); } if (m_docking_area == Qt::TopDockWidgetArea) top->setChecked(true); else if (m_docking_area == Qt::BottomDockWidgetArea) bottom->setChecked(true); else if (m_docking_area == Qt::LeftDockWidgetArea) left->setChecked(true); else right->setChecked(true); QAction* triggered = menu.exec(event->globalPos()); if (triggered) { Sublime::Position pos; if (triggered == left) pos = Sublime::Left; else if (triggered == bottom) pos = Sublime::Bottom; else if (triggered == right) pos = Sublime::Right; else pos = Sublime::Top; Area *area = m_area; View *view = m_view; /* This call will delete *this, so we no longer can access member variables. */ m_area->moveToolView(m_view, pos); area->raiseToolView(view); } } IdealMainWidget::IdealMainWidget(MainWindow* parent, KActionCollection* ac) : QWidget(parent) , m_centralWidgetFocusing(false) { - leftBarWidget = new IdealButtonBarWidget(Qt::LeftDockWidgetArea); + leftBarWidget = new IdealButtonBarWidget(Qt::LeftDockWidgetArea, this); leftBarWidget->hide(); - rightBarWidget = new IdealButtonBarWidget(Qt::RightDockWidgetArea); + rightBarWidget = new IdealButtonBarWidget(Qt::RightDockWidgetArea, this); rightBarWidget->hide(); - bottomBarWidget = new IdealButtonBarWidget(Qt::BottomDockWidgetArea); + bottomBarWidget = new IdealButtonBarWidget(Qt::BottomDockWidgetArea, this); bottomBarWidget->hide(); - topBarWidget = new IdealButtonBarWidget(Qt::TopDockWidgetArea); + topBarWidget = new IdealButtonBarWidget(Qt::TopDockWidgetArea, this); topBarWidget->hide(); - mainWidget = new IdealCentralWidget(this); + m_mainLayout = new IdealMainLayout(this); + m_mainLayout->addButtonBar(leftBarWidget, IdealMainLayout::Left); + m_mainLayout->addButtonBar(rightBarWidget, IdealMainLayout::Right); + m_mainLayout->addButtonBar(topBarWidget, IdealMainLayout::Top); + m_mainLayout->addButtonBar(bottomBarWidget, IdealMainLayout::Bottom); - m_mainLayout = new IdealMainLayout(mainWidget); - mainWidget->setLayout(m_mainLayout); + setLayout(m_mainLayout); connect(parent, SIGNAL(settingsLoaded()), m_mainLayout, SLOT(loadSettings())); +#if 0 QGridLayout *grid = new QGridLayout(this); grid->setMargin(0); grid->setSpacing(0); +#if 0 grid->addWidget(leftBarWidget, 1, 0); +#endif grid->addWidget(mainWidget, 1, 1); +#if 0 grid->addWidget(rightBarWidget, 1, 2); grid->addWidget(bottomBarWidget, 2, 0, 1, 3); grid->addWidget(topBarWidget, 0, 0, 1, 3); +#endif setLayout(grid); +#endif KAction* action = m_showLeftDock = new KAction(i18n("Show Left Dock"), this); action->setCheckable(true); connect(action, SIGNAL(toggled(bool)), SLOT(showLeftDock(bool))); ac->addAction("show_left_dock", action); m_showRightDock = action = new KAction(i18n("Show Right Dock"), this); action->setCheckable(true); connect(action, SIGNAL(toggled(bool)), SLOT(showRightDock(bool))); ac->addAction("show_right_dock", action); m_showBottomDock = action = new KAction(i18n("Show Bottom Dock"), this); action->setCheckable(true); connect(action, SIGNAL(toggled(bool)), SLOT(showBottomDock(bool))); ac->addAction("show_bottom_dock", action); m_showTopDock = action = new KAction(i18n("Show Top Dock"), this); action->setCheckable(true); connect(action, SIGNAL(toggled(bool)), SLOT(showTopDock(bool))); ac->addAction("show_top_dock", action); action = new KAction(i18n("Hide All Docks"), this); connect(action, SIGNAL(triggered(bool)), SLOT(hideAllDocks())); ac->addAction("hide_all_docks", action); action = new KAction(i18n("Focus Editor"), this); connect(action, SIGNAL(triggered(bool)), SLOT(focusEditor())); ac->addAction("focus_editor", action); m_anchorCurrentDock = action = new KAction(i18n("Anchor Current Dock"), this); action->setCheckable(true); action->setEnabled(false); connect(action, SIGNAL(toggled(bool)), SLOT(anchorCurrentDock(bool))); ac->addAction("anchor_current_dock", action); m_maximizeCurrentDock = action = new KAction(i18n("Maximize Current Dock"), this); action->setCheckable(true); action->setEnabled(false); connect(action, SIGNAL(toggled(bool)), SLOT(maximizeCurrentDock(bool))); ac->addAction("maximize_current_dock", action); action = new KAction(i18n("Select Next Dock"), this); connect(action, SIGNAL(triggered(bool)), SLOT(selectNextDock())); ac->addAction("select_next_dock", action); action = new KAction(i18n("Select Previous Dock"), this); connect(action, SIGNAL(triggered(bool)), SLOT(selectPreviousDock())); ac->addAction("select_previous_dock", action); action = new KAction(i18n("Remove view"), this); connect(action, SIGNAL(triggered(bool)), SLOT(removeView())); ac->addAction("remove_view", action); action = m_docks = new KActionMenu(i18n("Docks"), this); ac->addAction("docks_submenu", action); } void IdealMainWidget::addView(Qt::DockWidgetArea area, View* view) { - IdealDockWidget *dock = new IdealDockWidget(mainWidget); + IdealDockWidget *dock = new IdealDockWidget(this); KAcceleratorManager::setNoAccel(dock); QWidget *w = view->widget(dock); if (w->parent() == 0) { /* Could happen when we're moving the widget from one IdealDockWidget to another. See moveView below. In this case, we need to reparent the widget. */ w->setParent(dock); } QList toolBarActions = view->toolBarActions(); if (toolBarActions.isEmpty()) dock->setWidget(w); else { QMainWindow *toolView = new QMainWindow(); KToolBar *toolBar = new KToolBar(toolView); int iconSize = style()->pixelMetric(QStyle::PM_SmallIconSize); toolBar->setIconSize(QSize(iconSize, iconSize)); toolBar->setToolButtonStyle(Qt::ToolButtonIconOnly); toolBar->setWindowTitle(i18n("%1 Tool Bar", w->windowTitle())); toolBar->setFloatable(false); toolBar->setMovable(true); foreach (QAction *action, toolBarActions) toolBar->addAction(action); toolView->setCentralWidget(w); toolView->addToolBar(toolBar); dock->setWidget(toolView); } dock->setWindowTitle(view->widget()->windowTitle()); dock->setAutoFillBackground(true); dock->setFocusProxy(dock->widget()); if (IdealButtonBarWidget* bar = barForRole(roleForArea(area))) { KAction* action = bar->addWidget( view->document()->title(), dock, static_cast(parent())->area(), view); m_dockwidget_to_action[dock] = m_view_to_action[view] = action; m_docks->addAction(action); bar->show(); } dock->hide(); docks[dock] = area; } KAction * Sublime::IdealMainWidget::actionForRole(IdealMainLayout::Role role) const { switch (role) { case IdealMainLayout::Left: default: return m_showLeftDock; case IdealMainLayout::Right: return m_showRightDock; case IdealMainLayout::Top: return m_showTopDock; case IdealMainLayout::Bottom: return m_showBottomDock; } } void IdealMainWidget::centralWidgetFocused() { m_centralWidgetFocusing = true; for (IdealMainLayout::Role role = IdealMainLayout::Left; role <= IdealMainLayout::Top; role = static_cast(role + 1)) if (!m_mainLayout->isAreaAnchored(role)) actionForRole(role)->setChecked(false); m_centralWidgetFocusing = false; } void IdealMainWidget::hideAllDocks() { for (IdealMainLayout::Role role = IdealMainLayout::Left; role <= IdealMainLayout::Top; role = static_cast(role + 1)) actionForRole(role)->setChecked(false); } void IdealMainWidget::raiseView(View * view) { QAction* action = m_view_to_action.value(view); Q_ASSERT(action); action->setChecked(true); } void IdealMainWidget::removeView(View* view, bool nondestructive) { Q_ASSERT(m_view_to_action.contains(view)); QAction* action = m_view_to_action.value(view); QWidget *viewParent = view->widget()->parentWidget(); IdealDockWidget *dock = qobject_cast(viewParent); for (; viewParent; viewParent = viewParent->parentWidget()) { if (0 != (dock = qobject_cast(viewParent))) break; } Q_ASSERT(dock); /* Hide the view, first. This is a workaround -- if we try to remove IdealDockWidget without this, then eventually a call to IdealMainLayout::takeAt will be made, which method asserts immediately. */ action->setChecked(false); if (IdealButtonBarWidget* bar = barForRole(roleForArea(docks.value(dock)))) bar->removeAction(action); m_view_to_action.remove(view); m_dockwidget_to_action.remove(dock); if (nondestructive) view->widget()->setParent(0); delete dock; } void IdealMainWidget::moveView(View *view, Qt::DockWidgetArea area) { removeView(view); addView(area, view); } void IdealMainWidget::setCentralWidget(QWidget * widget) { m_mainLayout->addWidget(widget, IdealMainLayout::Central); } void IdealMainWidget::anchorCurrentDock(bool anchor) { if (IdealDockWidget* dw = m_mainLayout->lastDockWidget()) { if (!dw->isVisible()) return setAnchorActionStatus(dw->isAnchored()); dw->setAnchored(anchor, true); } } void IdealMainWidget::maximizeCurrentDock(bool maximized) { if (IdealDockWidget* dw = m_mainLayout->lastDockWidget()) { if (!dw->isVisible()) return setMaximizeActionStatus(false); dw->setMaximized(maximized); } } void IdealMainWidget::anchorDockWidget(bool checked, IdealButtonBarWidget * bar) { m_mainLayout->anchorWidget(checked, roleForBar(bar)); } void IdealMainWidget::maximizeDockWidget(bool checked, IdealButtonBarWidget * bar) { IdealDockWidget* widget = 0; if (checked) { IdealMainLayout::Role role = roleForBar(bar); widget = mainLayout()->lastDockWidget(role); } m_mainLayout->maximizeWidget(widget); setMaximizeActionStatus(widget); } void IdealMainWidget::anchorDockWidget(IdealDockWidget * dock, bool anchor) { Q_ASSERT(docks.contains(dock)); m_mainLayout->anchorWidget(anchor, roleForArea(docks.value(dock))); } void IdealMainWidget::showDockWidget(IdealDockWidget * dock, bool show) { Q_ASSERT(docks.contains(dock)); IdealMainLayout::Role role = roleForArea(docks.value(dock)); dock->setAnchored(m_mainLayout->isAreaAnchored(role), false); if (show) { m_mainLayout->addWidget(dock, role); bool isMaximized = dock->isMaximized(); if (isMaximized) m_mainLayout->maximizeWidget(dock); } else { m_mainLayout->removeWidget(dock, role); setMaximizeActionStatus(false); } m_maximizeCurrentDock->setEnabled(show); m_anchorCurrentDock->setEnabled(show); setShowDockStatus(role, show); Sublime::Position pos; if (role == IdealMainLayout::Left) pos = Sublime::Left; else if (role == IdealMainLayout::Right) pos = Sublime::Right; else if (role == IdealMainLayout::Top) pos = Sublime::Top; else if (role == IdealMainLayout::Bottom) pos = Sublime::Bottom; else { Q_ASSERT (0 && "unexpect position"); } emit dockShown(dock->view(), pos, show); } -IdealCentralWidget::IdealCentralWidget(IdealMainWidget * parent) - : QWidget(parent) -{ -} - -IdealCentralWidget::~ IdealCentralWidget() -{ -} - -IdealMainLayout * IdealCentralWidget::idealLayout() const -{ - return static_cast(layout()); -} - IdealSplitterHandle::IdealSplitterHandle(Qt::Orientation orientation, QWidget* parent, IdealMainLayout::Role resizeRole) : QWidget(parent) , m_orientation(orientation) , m_hover(false) , m_resizeRole(resizeRole) { setCursor(orientation == Qt::Horizontal ? Qt::SplitVCursor : Qt::SplitHCursor); setMouseTracking(true); setAutoFillBackground(true); } void IdealSplitterHandle::paintEvent(QPaintEvent *) { QStylePainter painter(this); QStyleOption options; options.initFrom(this); if (m_orientation == Qt::Vertical) options.state |= QStyle::State_Horizontal; options.state |= QStyle::State_Enabled; painter.drawControl(QStyle::CE_Splitter, options); } void IdealSplitterHandle::mouseMoveEvent(QMouseEvent * event) { if (!(event->buttons() & Qt::LeftButton)) return; int thickness = convert(parentWidget()->mapFromGlobal(event->globalPos())) - m_dragStart; switch (m_resizeRole) { case IdealMainLayout::Right: thickness = parentWidget()->size().width() - thickness; break; case IdealMainLayout::Bottom: thickness = parentWidget()->size().height() - thickness; break; default: break; } emit resize(thickness, m_resizeRole); } void IdealSplitterHandle::mousePressEvent(QMouseEvent * event) { if (event->button() == Qt::LeftButton) m_dragStart = convert(event->pos()); } IdealMainWidget * IdealButtonBarWidget::parentWidget() const { return static_cast(QWidget::parentWidget()); } IdealMainLayout * IdealMainWidget::mainLayout() const { return m_mainLayout; } -IdealCentralWidget * IdealMainWidget::internalCentralWidget() const -{ - return mainWidget; -} - void IdealMainWidget::showDock(IdealMainLayout::Role role, bool show) { // If the dock is shown but not focused, first focus it, a second press of the shortcut will hide it if (!m_centralWidgetFocusing) { if (IdealDockWidget* widget = mainLayout()->lastDockWidget(role)) { if (widget->isVisible() && !widget->hasFocus()) { widget->setFocus(Qt::ShortcutFocusReason); // re-sync action state given we may have asked for the dock to be hidden KAction* action = actionForRole(role); if (!action->isChecked()) { action->blockSignals(true); action->setChecked(true); action->blockSignals(false); } return; } } } if (show) { if (IdealDockWidget* widget = m_mainLayout->lastDockWidget(role)) if (QAction *action = m_dockwidget_to_action.value(widget)) return action->setChecked(show); if (barForRole(role)->actions().count()) barForRole(role)->actions().first()->setChecked(show); } else { foreach (QAction* action, barForRole(role)->actions()) if (action->isChecked()) action->setChecked(false); } } void IdealMainWidget::showLeftDock(bool show) { showDock(IdealMainLayout::Left, show); } void IdealMainWidget::showBottomDock(bool show) { showDock(IdealMainLayout::Bottom, show); } void IdealMainWidget::showTopDock(bool show) { showDock(IdealMainLayout::Top, show); } void IdealMainWidget::showRightDock(bool show) { showDock(IdealMainLayout::Right, show); } QWidget * IdealMainWidget::firstWidget(IdealMainLayout::Role role) const { if (IdealButtonBarWidget* button = barForRole(role)) if (!button->actions().isEmpty()) return button->widgetForAction(button->actions().first()); return 0; } IdealButtonBarWidget* IdealMainWidget::barForRole(IdealMainLayout::Role role) const { switch (role) { case IdealMainLayout::Left: return leftBarWidget; case IdealMainLayout::Top: return topBarWidget; case IdealMainLayout::Right: return rightBarWidget; case IdealMainLayout::Bottom: return bottomBarWidget; default: Q_ASSERT(false); return 0; } } IdealMainLayout::Role IdealMainWidget::roleForBar(IdealButtonBarWidget* bar) const { if (bar == leftBarWidget) return IdealMainLayout::Left; else if (bar == topBarWidget) return IdealMainLayout::Top; else if (bar == rightBarWidget) return IdealMainLayout::Right; else if (bar == bottomBarWidget) return IdealMainLayout::Bottom; Q_ASSERT(false); return IdealMainLayout::Left; } QAction * IdealMainWidget::actionForView(View * view) const { return m_view_to_action.value(view); } void IdealMainWidget::setAnchorActionStatus(bool checked) { bool blocked = m_anchorCurrentDock->blockSignals(true); m_anchorCurrentDock->setChecked(checked); m_anchorCurrentDock->blockSignals(blocked); } void IdealMainWidget::setMaximizeActionStatus(bool checked) { bool blocked = m_maximizeCurrentDock->blockSignals(true); m_maximizeCurrentDock->setChecked(checked); m_maximizeCurrentDock->blockSignals(blocked); } IdealDockWidget * IdealButtonBarWidget::widgetForAction(QAction *action) const { return _widgets.value(action); } void IdealMainWidget::selectNextDock() { IdealDockWidget* dock = mainLayout()->lastDockWidget(); IdealMainLayout::Role role = mainLayout()->lastDockWidgetRole(); IdealButtonBarWidget* bar = barForRole(role); int index = bar->actions().indexOf(m_dockwidget_to_action.value(dock)); if (index == -1 || index == bar->actions().count() - 1) index = 0; else ++index; if (index < bar->actions().count()) { QAction* action = bar->actions().at(index); action->setChecked(true); } } void IdealMainWidget::selectPreviousDock() { IdealDockWidget* dock = mainLayout()->lastDockWidget(); IdealMainLayout::Role role = mainLayout()->lastDockWidgetRole(); IdealButtonBarWidget* bar = barForRole(role); int index = bar->actions().indexOf(m_dockwidget_to_action.value(dock)); if (index < 1) index = bar->actions().count() - 1; else --index; if (index < bar->actions().count()) { QAction* action = bar->actions().at(index); action->setChecked(true); } } void IdealMainWidget::removeView() { MainWindow *main = dynamic_cast(parent()); main->area()->removeToolView(main->activeToolView()); } void Sublime::IdealMainWidget::setShowDockStatus(IdealMainLayout::Role role, bool checked) { KAction* action = actionForRole(role); if (action->isChecked() != checked) { bool blocked = action->blockSignals(true); action->setChecked(checked); action->blockSignals(blocked); } } void Sublime::IdealMainWidget::focusEditor() { if (View* view = static_cast(parent())->activeView()) if (view->hasWidget()) view->widget()->setFocus(Qt::ShortcutFocusReason); } IdealDockWidgetButton::IdealDockWidgetButton(QWidget *parent) : QToolButton(parent) { setFocusPolicy(Qt::NoFocus); } IdealDockWidgetButton::~IdealDockWidgetButton() { } QSize IdealDockWidgetButton::sizeHint() const { ensurePolished(); int size = 0; if (! icon().isNull()) { const QPixmap pix = icon().pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize)); size += qMax(pix.width(), pix.height()); } const int titleBarButtonMargin = style()->pixelMetric(QStyle::PM_DockWidgetTitleBarButtonMargin); size += titleBarButtonMargin * 2; return QSize(size, size); } QSize IdealDockWidgetButton::minimumSizeHint() const { return sizeHint(); } void IdealDockWidgetButton::enterEvent(QEvent *event) { if (isEnabled()) update(); QToolButton::enterEvent(event); } void IdealDockWidgetButton::leaveEvent(QEvent *event) { if (isEnabled()) update(); QToolButton::leaveEvent(event); } void IdealDockWidgetButton::paintEvent(QPaintEvent *) { QStylePainter painter(this); QStyleOptionToolButton options; options.init(this); options.state |= QStyle::State_AutoRaise; if (isEnabled() && underMouse() && ! isChecked() && ! isDown()) options.state |= QStyle::State_Raised; if (isChecked()) options.state |= QStyle::State_On; if (isDown()) options.state |= QStyle::State_Sunken; options.subControls = QStyle::SC_None; options.activeSubControls = QStyle::SC_None; options.icon = icon(); options.arrowType = Qt::NoArrow; options.features = QStyleOptionToolButton::None; const int size = style()->pixelMetric(QStyle::PM_SmallIconSize); options.iconSize = QSize(size, size); painter.drawComplexControl(QStyle::CC_ToolButton, options); } #include "ideal.moc" diff --git a/sublime/ideal.h b/sublime/ideal.h index 94cfb0ce50..e6a4f8ac28 100644 --- a/sublime/ideal.h +++ b/sublime/ideal.h @@ -1,287 +1,272 @@ /* Copyright 2007 Roberto Raggi Copyright 2007 Hamish Rodda Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SUBLIME_IDEAL_H #define SUBLIME_IDEAL_H #include #include "ideallayout.h" #include "sublimedefs.h" class KAction; class KActionMenu; class KActionCollection; namespace Sublime { class Area; class View; class MainWindow; class IdealToolButton: public QToolButton { Q_OBJECT public: IdealToolButton(Qt::DockWidgetArea area, QWidget *parent = 0); Qt::Orientation orientation() const; virtual QSize sizeHint() const; protected: virtual void paintEvent(QPaintEvent *event); private: Qt::DockWidgetArea _area; }; class IdealDockWidgetButton: public QToolButton { Q_OBJECT public: IdealDockWidgetButton(QWidget *parent = 0); virtual ~IdealDockWidgetButton(); QSize sizeHint() const; QSize minimumSizeHint() const; protected: virtual void enterEvent(QEvent *event); virtual void leaveEvent(QEvent *event); virtual void paintEvent(QPaintEvent *event); }; class IdealButtonBarWidget: public QWidget { Q_OBJECT public: IdealButtonBarWidget(Qt::DockWidgetArea area, class IdealMainWidget *parent = 0); KAction *addWidget(const QString& title, IdealDockWidget *widget, Area* area, View *view); void showWidget(IdealDockWidget* widget); void removeAction(QAction* action); IdealMainWidget* parentWidget() const; Qt::Orientation orientation() const; IdealDockWidget* widgetForAction(QAction* action) const; private Q_SLOTS: void showWidget(bool checked); void anchor(bool anchor); void maximize(bool maximized); void actionToggled(bool state); protected: - virtual void resizeEvent(QResizeEvent *event); - virtual void actionEvent(QActionEvent *event); private: Qt::DockWidgetArea _area; QHash _buttons; QHash _widgets; QActionGroup* _actions; }; class IdealDockWidget : public QDockWidget { Q_OBJECT public: IdealDockWidget(QWidget *parent); virtual ~IdealDockWidget(); Area *area() const; void setArea(Area *area); View *view() const; void setView(View *view); Qt::DockWidgetArea dockWidgetArea() const; void setDockWidgetArea(Qt::DockWidgetArea dockingArea); bool isAnchored() const; void setAnchored(bool anchored, bool emitSignals); bool isMaximized() const; void setMaximized(bool maximized); virtual bool event(QEvent *event); protected: // QWidget overrides virtual void contextMenuEvent(QContextMenuEvent *); virtual void mouseDoubleClickEvent(QMouseEvent *event); Q_SIGNALS: void anchor(bool anchor); void maximize(bool maximize); void close(); private Q_SLOTS: void slotMaximize(bool maximized); void slotRemove(); private: Qt::Orientation m_orientation; QAbstractButton* m_anchor; QAbstractButton* m_close; Area *m_area; View *m_view; Qt::DockWidgetArea m_docking_area; bool m_maximized; }; -class IdealCentralWidget : public QWidget -{ - Q_OBJECT - -public: - IdealCentralWidget(IdealMainWidget* parent); - virtual ~IdealCentralWidget(); - - IdealMainLayout* idealLayout() const; -}; - class View; class IdealMainWidget : public QWidget { Q_OBJECT public: IdealMainWidget(MainWindow* parent, KActionCollection* ac); // Public api void setCentralWidget(QWidget* widget); QAction* actionForView(View* view) const; void addView(Qt::DockWidgetArea area, View* View); void raiseView(View* view); /** Remove view. If nondestructive true, view->widget() is not deleted, as is left with NULL parent. Otherwise, it's deleted. */ void removeView(View* view, bool nondestructive = false); void moveView(View *view, Qt::DockWidgetArea area); // Internal api // TODO can move the object filter here with judicious focusProxy? void centralWidgetFocused(); void showDockWidget(IdealDockWidget* widget, bool show); void showDock(IdealMainLayout::Role role, bool show); void anchorDockWidget(IdealDockWidget* widget, bool anchor); IdealMainLayout* mainLayout() const; - IdealCentralWidget* internalCentralWidget() const; void anchorDockWidget(bool checked, IdealButtonBarWidget* bar); void maximizeDockWidget(bool checked, IdealButtonBarWidget* bar); QWidget* firstWidget(IdealMainLayout::Role role) const; IdealButtonBarWidget* barForRole(IdealMainLayout::Role role) const; IdealMainLayout::Role roleForBar(IdealButtonBarWidget* bar) const; KAction* actionForRole(IdealMainLayout::Role role) const; void setAnchorActionStatus(bool checked); void setMaximizeActionStatus(bool checked); void setShowDockStatus(IdealMainLayout::Role role, bool checked); Q_SIGNALS: void dockShown(Sublime::View*, Sublime::Position pos, bool shown); public Q_SLOTS: void showLeftDock(bool show); void showRightDock(bool show); void showBottomDock(bool show); void showTopDock(bool show); void hideAllDocks(); void focusEditor(); void anchorCurrentDock(bool anchor); void maximizeCurrentDock(bool maximized); void selectNextDock(); void selectPreviousDock(); void removeView(); private: IdealButtonBarWidget *leftBarWidget; IdealButtonBarWidget *rightBarWidget; IdealButtonBarWidget *bottomBarWidget; IdealButtonBarWidget *topBarWidget; KAction* m_showLeftDock; KAction* m_showRightDock; KAction* m_showBottomDock; KAction* m_showTopDock; KAction* m_anchorCurrentDock; KAction* m_maximizeCurrentDock; KActionMenu* m_docks; - IdealCentralWidget* mainWidget; class IdealMainLayout* m_mainLayout; QMap docks; /** Map from View to an action that shows/hides the IdealDockWidget containing that view. */ QMap m_view_to_action; /** Map from IdealDockWidget to an action that shows/hides that IdealDockWidget. */ QMap m_dockwidget_to_action; bool m_centralWidgetFocusing; }; class IdealSplitterHandle : public QWidget { Q_OBJECT public: IdealSplitterHandle(Qt::Orientation orientation, QWidget* parent, IdealMainLayout::Role resizeRole); Q_SIGNALS: void resize(int thickness, IdealMainLayout::Role resizeRole); protected: virtual void paintEvent(QPaintEvent* event); virtual void mouseMoveEvent(QMouseEvent* event); virtual void mousePressEvent(QMouseEvent* event); private: inline int convert(const QPoint& pos) const { return m_orientation == Qt::Horizontal ? pos.y() : pos.x(); } Qt::Orientation m_orientation; bool m_hover; int m_dragStart; IdealMainLayout::Role m_resizeRole; }; } #endif diff --git a/sublime/ideallayout.cpp b/sublime/ideallayout.cpp index 7a5b07e53f..bcf381cee3 100644 --- a/sublime/ideallayout.cpp +++ b/sublime/ideallayout.cpp @@ -1,981 +1,1090 @@ /* Copyright 2007 Roberto Raggi Copyright 2007 Hamish Rodda + Copyright 2008 Vladimir Prus Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "ideallayout.h" #include "ideal.h" +#include "kdebug.h" #include #include #include using namespace Sublime; IdealMainLayout::Role Sublime ::roleForArea(Qt::DockWidgetArea area) { switch (area) { case Qt::LeftDockWidgetArea: return IdealMainLayout::Left; case Qt::RightDockWidgetArea: return IdealMainLayout::Right; case Qt::BottomDockWidgetArea: return IdealMainLayout::Bottom; case Qt::TopDockWidgetArea: return IdealMainLayout::Top; default: Q_ASSERT(false); return IdealMainLayout::Left; } } Qt::DockWidgetArea Sublime ::areaForRole(IdealMainLayout::Role role) { switch (role) { case IdealMainLayout::Left: return Qt::LeftDockWidgetArea; case IdealMainLayout::Right: return Qt::RightDockWidgetArea; case IdealMainLayout::Bottom: return Qt::BottomDockWidgetArea; case IdealMainLayout::Top: return Qt::TopDockWidgetArea; default: Q_ASSERT(false); return Qt::LeftDockWidgetArea; } } IdealButtonBarLayout::IdealButtonBarLayout(Qt::Orientation orientation, QWidget *parent) : QLayout(parent) , _orientation(orientation) , _height(0) { - setMargin(2); + if (orientation == Qt::Vertical) + setContentsMargins(2, 0, 2, 0); + else + setContentsMargins(0, 2, 0, 0); setSpacing(2); invalidate(); } void IdealButtonBarLayout::invalidate() { m_minSizeDirty = true; + m_sizeHintDirty = true; m_layoutDirty = true; QLayout::invalidate(); } IdealButtonBarLayout::~IdealButtonBarLayout() { qDeleteAll(_items); } void IdealButtonBarLayout::setHeight(int height) { Q_ASSERT(orientation() == Qt::Vertical); _height = height; (void) invalidate(); } Qt::Orientation IdealButtonBarLayout::orientation() const { return _orientation; } Qt::Orientations IdealButtonBarLayout::expandingDirections() const { return orientation(); } -bool IdealButtonBarLayout::hasHeightForWidth() const -{ - if (orientation() == Qt::Vertical) - return false; - - return true; -} - -int IdealButtonBarLayout::heightForWidth(int width) const -{ - Q_ASSERT(orientation() == Qt::Horizontal); - return doHorizontalLayout(QRect(0, 0, width, 0), false); -} - QSize IdealButtonBarLayout::minimumSize() const { + // The code below appears to be completely wrong -- + // it will return the maximum size of a single button, not any + // estimate as to how much space is necessary to draw all buttons + // in a minimally acceptable way. if (m_minSizeDirty) { if (orientation() == Qt::Vertical) { const int width = doVerticalLayout(QRect(0, 0, 0, _height), false); return QSize(width, 0); } m_min = QSize(0, 0); foreach (QLayoutItem *item, _items) m_min = m_min.expandedTo(item->minimumSize()); - m_min += QSize(2 * margin(), 2 * margin()); m_minSizeDirty = false; } - return m_min; } QSize IdealButtonBarLayout::sizeHint() const { - return minimumSize(); + if (m_sizeHintDirty) { + int orientationSize = 0; + int crossSize = 0; + + bool first = true; + foreach (QLayoutItem *item, _items) + { + QSize hint = item->sizeHint(); + int orientationSizeHere; + int crossSizeHere; + if (orientation() == Qt::Vertical) + { + orientationSizeHere = hint.height(); + crossSizeHere = hint.width(); + } + else + { + orientationSizeHere = hint.width(); + crossSizeHere = hint.height(); + } + + if (first) + { + crossSize = crossSizeHere; + } + else + { + orientationSize += spacing(); + } + orientationSize += orientationSizeHere; + first = false; + } + + if (orientation() == Qt::Vertical) + m_hint = QSize(crossSize, orientationSize); + else + m_hint = QSize(orientationSize, crossSize); + int l, t, r, b; + getContentsMargins(&l, &t, &r, &b); + m_hint += QSize(l+r, t+b); + + m_sizeHintDirty = false; + } + return m_hint; } void IdealButtonBarLayout::setGeometry(const QRect &rect) { if (m_layoutDirty || rect != geometry()) if (orientation() == Qt::Vertical) doVerticalLayout(rect); else doHorizontalLayout(rect); } void IdealButtonBarLayout::addItem(QLayoutItem *item) { _items.append(item); } QLayoutItem* IdealButtonBarLayout::itemAt(int index) const { return _items.value(index, 0); } QLayoutItem* IdealButtonBarLayout::takeAt(int index) { if (index >= 0 && index < _items.count()) return _items.takeAt(index); return 0; } int IdealButtonBarLayout::count() const { return _items.count(); } int IdealButtonBarLayout::doVerticalLayout(const QRect &rect, bool updateGeometry) const { - int x = rect.x() + margin(); - int y = rect.y() + margin(); + int l, t, r, b; + getContentsMargins(&l, &t, &r, &b); + int x = rect.x() + l; + int y = rect.y() + t; int currentLineWidth = 0; foreach (QLayoutItem *item, _items) { const QSize itemSizeHint = item->sizeHint(); - if (y + itemSizeHint.height() >= rect.height()) { - x += currentLineWidth + spacing(); - y = rect.y() + margin(); + if (y + itemSizeHint.height() + b > rect.height()) { + int newX = x + currentLineWidth + spacing(); + if (newX + itemSizeHint.width() + r <= rect.width()) + { + x += currentLineWidth + spacing(); + y = rect.y() + t; + } } if (updateGeometry) item->setGeometry(QRect(x, y, itemSizeHint.width(), itemSizeHint.height())); currentLineWidth = qMax(currentLineWidth, itemSizeHint.width()); y += itemSizeHint.height() + spacing(); } m_layoutDirty = updateGeometry; - return x + currentLineWidth + margin(); + return x + currentLineWidth + r; } int IdealButtonBarLayout::doHorizontalLayout(const QRect &rect, bool updateGeometry) const -{ - int x = rect.x() + margin(); - int y = rect.y() + margin(); +{ + int l, t, r, b; + getContentsMargins(&l, &t, &r, &b); + int x = rect.x() + l; + int y = rect.y() + t; int currentLineHeight = 0; foreach (QLayoutItem *item, _items) { - const QSize itemSizeHint = item->sizeHint(); - if (x + itemSizeHint.width() + margin() >= rect.width()) { - y += currentLineHeight + spacing(); - x = rect.x() + margin(); - currentLineHeight = 0; + QSize itemSizeHint = item->sizeHint(); + if (x + itemSizeHint.width() + r > rect.width()) { + // Run out of horizontal space. Try to move button to another + // row. + int newY = y + currentLineHeight + spacing(); + if (newY + itemSizeHint.height() + b <= rect.height()) + { + y = newY; + x = rect.x() + l; + currentLineHeight = 0; + } } if (updateGeometry) item->setGeometry(QRect(x, y, itemSizeHint.width(), itemSizeHint.height())); currentLineHeight = qMax(currentLineHeight, itemSizeHint.height()); x += itemSizeHint.width() + spacing(); } m_layoutDirty = updateGeometry; - return y + currentLineHeight + margin(); + return y + currentLineHeight + b; } IdealDockWidget* IdealMainLayout::lastDockWidget() const { return qobject_cast(m_lastDockWidget); } IdealDockWidget * IdealMainLayout::lastDockWidget(IdealMainLayout::Role role) const { return qobject_cast(m_items[role]->last); } IdealMainLayout::IdealMainLayout(QWidget * parent) : QLayout(parent) , m_layoutDirty(true) , m_sizeHintDirty(true) , m_minDirty(true) , m_lastDockWidgetRole(Left) , m_topOwnsTopLeft(0) , m_topOwnsTopRight(0) , m_bottomOwnsBottomLeft(0) , m_bottomOwnsBottomRight(0) , m_maximizedWidget(0) { createArea(Left); createArea(Right); createArea(Top); createArea(Bottom); createArea(Central); setMargin(0); m_splitterWidth = parent->style()->pixelMetric(QStyle::PM_SplitterWidth, 0, parentWidget()); loadSettings(); anchorWidget(true, Left); anchorWidget(true, Right); anchorWidget(true, Top); anchorWidget(true, Bottom); } IdealMainLayout::~ IdealMainLayout() { } QSize IdealMainLayout::minimumSize() const { if (m_minDirty) { if (m_maximizedWidget) { m_min = m_maximizedWidget->minimumSize(); m_minDirty = false; return m_min; } int minHeight = 0; int softMinHeight = 0; int minWidth = 0; int softMinWidth = 0; minimumSize(Left, minWidth, softMinWidth, minHeight, softMinHeight); minimumSize(Right, minWidth, softMinWidth, minHeight, softMinHeight); minimumSize(Top, minWidth, softMinWidth, minHeight, softMinHeight); minimumSize(Bottom, minWidth, softMinWidth, minHeight, softMinHeight); if (QLayoutItem* item = m_items[Central]->first()) { const QSize itemSizeHint = item->minimumSize(); minHeight += qMax(softMinHeight, itemSizeHint.height() + splitterWidth()); minWidth += qMax(softMinWidth, itemSizeHint.width() + splitterWidth()); } m_min = QSize(minHeight, minWidth); m_minDirty = true; } return m_min; } void IdealMainLayout::minimumSize(Role role, int& minWidth, int& softMinWidth, int& minHeight, int& softMinHeight) const { foreach (QLayoutItem* item, m_items[role]->items()) { const QSize itemSizeHint = item->minimumSize(); switch (role) { case Left: case Right: if (m_items[role]->anchored) minWidth += itemSizeHint.width() + splitterWidth(); softMinHeight = qMax(softMinHeight, itemSizeHint.height() + splitterWidth()); break; case Top: case Bottom: if (m_items[role]->anchored) minHeight += itemSizeHint.height() + splitterWidth(); softMinWidth = qMax(softMinWidth, itemSizeHint.width() + splitterWidth()); break; default: break; } } } QLayoutItem * IdealMainLayout::itemAt(int index) const { int at = 0; + if (m_buttonBars.contains(Left)) + { + if (index == 0) + return m_buttonBarItems[Left]; + else + --index; + } + if (m_buttonBars.contains(Top)) + { + if (index == 0) + return m_buttonBarItems[Top]; + else + --index; + } + if (m_buttonBars.contains(Right)) + { + if (index == 0) + return m_buttonBarItems[Right]; + else + --index; + } + if (m_buttonBars.contains(Bottom)) + { + if (index == 0) + return m_buttonBarItems[Bottom]; + else + --index; + } + + if (QLayoutItem* item = m_items[Left]->itemAt(index, at)) return item; index -= at; at = 0; if (QLayoutItem* item = m_items[Right]->itemAt(index, at)) return item; index -= at; at = 0; if (QLayoutItem* item = m_items[Top]->itemAt(index, at)) return item; index -= at; at = 0; if (QLayoutItem* item = m_items[Bottom]->itemAt(index, at)) return item; index -= at; at = 0; if (QLayoutItem* item = m_items[Central]->itemAt(index, at)) return item; return 0; } void IdealMainLayout::addItem(QLayoutItem * item) { Q_UNUSED(item) // Uh-oh...?? Q_ASSERT(false); } void IdealMainLayout::setGeometry(const QRect & rect) { if (m_layoutDirty || rect != geometry()) { - doLayout(rect); QLayout::setGeometry(rect); + doLayout(rect); } } QSize IdealMainLayout::sizeHint() const { if (m_sizeHintDirty) { if (m_maximizedWidget) { m_hint = m_maximizedWidget->sizeHint(); m_sizeHintDirty = false; return m_hint; } int minHeight = 0; int softMinHeight = 0; int minWidth = 0; int softMinWidth = 0; sizeHint(Left, minWidth, softMinWidth, minHeight, softMinHeight); sizeHint(Right, minWidth, softMinWidth, minHeight, softMinHeight); sizeHint(Top, minWidth, softMinWidth, minHeight, softMinHeight); sizeHint(Bottom, minWidth, softMinWidth, minHeight, softMinHeight); if (QLayoutItem* item = m_items[Central]->first()) { const QSize itemSizeHint = item->sizeHint(); minHeight += qMax(softMinHeight, itemSizeHint.height() + splitterWidth()); minWidth += qMax(softMinWidth, itemSizeHint.width() + splitterWidth()); } m_hint = QSize(minHeight, minWidth); m_sizeHintDirty = false; } return m_hint; } void IdealMainLayout::sizeHint(Role role, int& minWidth, int& softMinWidth, int& minHeight, int& softMinHeight) const { foreach (QLayoutItem* item, m_items[role]->items()) { const QSize itemSizeHint = item->sizeHint(); switch (role) { case Left: case Right: if (m_items[role]->anchored) minWidth += itemSizeHint.width() + splitterWidth(); softMinHeight = qMax(softMinHeight, itemSizeHint.height() + splitterWidth()); break; case Top: case Bottom: if (m_items[role]->anchored) minHeight += itemSizeHint.height() + splitterWidth(); softMinWidth = qMax(softMinWidth, itemSizeHint.width() + splitterWidth()); break; default: break; } } } QLayoutItem * IdealMainLayout::takeAt(int index) { Q_UNUSED(index) // Uh-oh...?? Q_ASSERT(false); return 0; } int IdealMainLayout::count() const { - return m_items[Left]->count() + m_items[Right]->count() + m_items[Top]->count() + m_items[Bottom]->count() + m_items[Central]->count(); + return m_buttonBars.count() + m_items[Left]->count() + m_items[Right]->count() + + m_items[Top]->count() + m_items[Bottom]->count() + m_items[Central]->count(); } void IdealMainLayout::doLayout(QRect rect) const { if (m_maximizedWidget) { m_maximizedWidget->setGeometry(rect); return; } if (m_topOwnsTopLeft) if (m_topOwnsTopRight) if (m_bottomOwnsBottomLeft) if (m_bottomOwnsBottomRight) layout(Top, Bottom, Left, Right, rect); else layout(Top, Right, Bottom, Left, rect); else if (m_bottomOwnsBottomRight) layout(Top, Left, Bottom, Right, rect); else layout(Top, Right, Left, Bottom, rect); else if (m_bottomOwnsBottomLeft) if (m_bottomOwnsBottomRight) layout(Bottom, Right, Top, Left, rect); else layout(Right, Top, Left, Bottom, rect); else if (m_bottomOwnsBottomRight) // TODO: this is not possible with current code layout(Top, Left, Bottom, Right, rect); else layout(Right, Top, Left, Bottom, rect); else if (m_topOwnsTopRight) if (m_bottomOwnsBottomLeft) if (m_bottomOwnsBottomRight) layout(Bottom, Left, Top, Right, rect); else layout(Left, Top, Right, Bottom, rect); else if (m_bottomOwnsBottomRight) layout(Left, Bottom, Top, Right, rect); else layout(Right, Left, Bottom, Top, rect); else if (m_bottomOwnsBottomLeft) if (m_bottomOwnsBottomRight) layout(Bottom, Right, Left, Top, rect); else layout(Right, Bottom, Left, Top, rect); else if (m_bottomOwnsBottomRight) // TODO: this is not possible with current code layout(Left, Bottom, Right, Top, rect); else layout(Right, Left, Bottom, Top, rect); if (QLayoutItem* item = m_items[Central]->first()) { QSize itemSizeHint = item->sizeHint(); if (itemSizeHint.height() > rect.height()) { itemSizeHint.setHeight(qMax(item->minimumSize().height(), rect.height())); if (itemSizeHint.height() > rect.height()) rect.setHeight(itemSizeHint.height()); } if (itemSizeHint.width() > rect.width()) { itemSizeHint.setWidth(qMax(item->minimumSize().width(), rect.width())); if (itemSizeHint.width() > rect.width()) rect.setWidth(itemSizeHint.width()); } item->setGeometry(rect); } m_layoutDirty = false; } void IdealMainLayout::layout(Role role1, Role role2, Role role3, Role role4, QRect & rect) const { layoutItem(role1, rect); layoutItem(role2, rect); layoutItem(role3, rect); layoutItem(role4, rect); } void IdealMainLayout::layoutItem(Role role, QRect& rect) const { + QWidget* buttonBar = m_buttonBars.value(role); + if (buttonBar) + { + const QSize hint = buttonBar->sizeHint(); + QRect geometry = rect; + if (role == Left) { + geometry.setWidth(hint.width()); + rect.setLeft(hint.width()); + } + else if (role == Bottom) { + geometry.setTop(rect.height() - hint.height()); + rect.setBottom(rect.height() - hint.height()); + } + else if (role == Right) + geometry.setLeft(rect.width() - hint.width()); + else if (role == Top) + geometry.setHeight(hint.height()); + + buttonBar->setGeometry(geometry); + } + DockArea* area = m_items[role]; foreach (QLayoutItem* item, area->items()) { int hintDimension = 0; if (m_items[role]->width != -1) { hintDimension = m_items[role]->width; } else { const QSize itemSize = item->sizeHint(); switch (role) { case Left: case Right: hintDimension = itemSize.width() + splitterWidth(); if (hintDimension + splitterWidth() > rect.width()) { hintDimension = item->minimumSize().width(); if (hintDimension + splitterWidth() > rect.width()) rect.setWidth(hintDimension + splitterWidth()); } break; case Top: case Bottom: hintDimension = itemSize.height(); if (hintDimension + splitterWidth() > rect.height()) { hintDimension = item->minimumSize().height(); if (hintDimension + splitterWidth() > rect.height()) rect.setHeight(hintDimension + splitterWidth()); } break; default: break; } } switch (role) { case Left: item->setGeometry(QRect(rect.x(), rect.y(), hintDimension, rect.height())); area->mainSplitter()->setGeometry(QRect(rect.x() + hintDimension, rect.y(), splitterWidth(), rect.height())); break; case Right: item->setGeometry(QRect(rect.x() + rect.width() - hintDimension, rect.y(), hintDimension, rect.height())); area->mainSplitter()->setGeometry(QRect(rect.x() + rect.width() - hintDimension - splitterWidth(), rect.y(), splitterWidth(), rect.height())); break; case Top: item->setGeometry(QRect(rect.x(), rect.y(), rect.width(), hintDimension)); area->mainSplitter()->setGeometry(QRect(rect.x(), rect.y() + hintDimension, rect.width(), splitterWidth())); break; case Bottom: item->setGeometry(QRect(rect.x(), rect.y() + rect.height() - hintDimension, rect.width(), hintDimension)); area->mainSplitter()->setGeometry(QRect(rect.x(), rect.y() + rect.height() - hintDimension - splitterWidth(), rect.width(), splitterWidth())); break; default: break; } if (m_items[role]->anchored) { switch (role) { case Left: rect.setX(rect.x() + hintDimension + splitterWidth()); break; case Right: rect.setWidth(rect.width() - hintDimension - splitterWidth()); break; case Top: rect.setY(rect.y() + hintDimension + splitterWidth()); break; case Bottom: rect.setHeight(rect.height() - hintDimension - splitterWidth()); break; default: break; } } } } IdealSplitterHandle* IdealMainLayout::createSplitter(Role role, bool reverse) { IdealSplitterHandle* splitter = 0; Qt::Orientation direction = ((role == Left || role == Right) ^ reverse) ? Qt::Vertical : Qt::Horizontal; splitter = new IdealSplitterHandle(direction, parentWidget(), role); addChildWidget(splitter); connect(splitter, SIGNAL(resize(int, IdealMainLayout::Role)), SLOT(resizeWidget(int, IdealMainLayout::Role))); splitter->hide(); return splitter; } void IdealMainLayout::createArea(Role role) { DockArea* area = new DockArea(this, role); m_items.insert(role, area); if (role != Central) area->setMainSplitter(createSplitter(role)); } void IdealMainLayout::addWidget(QWidget * widget, Role role) { if (m_maximizedWidget) maximizeWidget(0); if (IdealDockWidget* dock = qobject_cast(widget)) if (dock->isFloating()) dock->setFloating(false); if (widget->parent() != parentWidget()) { widget->setParent(parentWidget()); addChildWidget(widget); } DockArea* area = m_items[role]; area->addWidget(widget); area->setVisible(true, !m_maximizedWidget); if (role != Central) { m_lastDockWidget = widget; m_lastDockWidgetRole = role; m_items[role]->last = widget; mainWidget()->setAnchorActionStatus(isAreaAnchored(role)); } area->raise(); widget->setFocus(); } +void IdealMainLayout::addButtonBar(QWidget* widget, Role role) +{ + m_buttonBars[role] = widget; + m_buttonBarItems[role] = new QWidgetItem(widget); +} + void IdealMainLayout::removeWidgets(Role role) { if (m_maximizedWidget) // FIXME correct? maximizeWidget(0); DockArea* area = m_items[role]; area->setVisible(false); } void IdealMainLayout::removeWidget(QWidget * widget, Role role) { DockArea* area = m_items[role]; area->removeWidget(widget); if (area->items().isEmpty()) area->setVisible(false, false); } void IdealMainLayout::removeUnanchored() { if (!m_items[Left]->anchored) removeWidgets(Left); if (!m_items[Right]->anchored) removeWidgets(Right); if (!m_items[Top]->anchored) removeWidgets(Top); if (!m_items[Bottom]->anchored) removeWidgets(Bottom); } void IdealMainLayout::invalidate() { m_layoutDirty = true; m_sizeHintDirty = true; m_minDirty = true; QLayout::invalidate(); } int IdealMainLayout::splitterWidth() const { return m_splitterWidth; } void IdealMainLayout::resizeWidget(int thickness, IdealMainLayout::Role role) { m_items[role]->width = thickness; invalidate(); emit widgetResized(role, thickness); } void IdealMainLayout::anchorWidget(bool anchor, IdealMainLayout::Role role) { m_items[role]->anchored = anchor; invalidate(); } void IdealMainLayout::maximizeWidget(QWidget* widget) { m_maximizedWidget = widget; if (m_maximizedWidget) { for (Role role = Left; role <= Central; role = static_cast(role + 1)) m_items[role]->setVisible(false, false, m_maximizedWidget); } else { for (Role role = Left; role <= Central; role = static_cast(role + 1)) if (!m_items[role]->items().isEmpty()) m_items[role]->setVisible(true, role != Central, m_maximizedWidget); } invalidate(); } int IdealMainLayout::widthForRole(Role role) const { return m_items[role]->width; } void IdealMainLayout::setWidthForRole(Role role, int width) { m_items[role]->width = width; invalidate(); } bool IdealMainLayout::isAreaAnchored(Role role) const { return m_items[role]->anchored; } IdealMainWidget * IdealMainLayout::mainWidget() const { - return static_cast(parentWidget()->parent()); + return dynamic_cast(parentWidget()); } void IdealMainLayout::loadSettings() { KConfigGroup cg(KGlobal::config(), "UiSettings"); bool invalid = false; int topOwnsTopLeft = cg.readEntry("TopLeftCornerOwner", 0); if (m_topOwnsTopLeft != topOwnsTopLeft) { m_topOwnsTopLeft = topOwnsTopLeft; invalid = true; } int topOwnsTopRight = cg.readEntry("TopRightCornerOwner", 0); if (m_topOwnsTopRight != topOwnsTopRight) { m_topOwnsTopRight = topOwnsTopRight; invalid = true; } int bottomOwnsBottomLeft = cg.readEntry("BottomLeftCornerOwner", 0); if (m_bottomOwnsBottomLeft != bottomOwnsBottomLeft) { m_bottomOwnsBottomLeft = bottomOwnsBottomLeft; invalid = true; } int bottomOwnsBottomRight = cg.readEntry("BottomRightCornerOwner", 0); if (m_bottomOwnsBottomRight != bottomOwnsBottomRight) { m_bottomOwnsBottomRight = bottomOwnsBottomRight; invalid = true; } if (invalid) invalidate(); } IdealMainLayout::Role IdealMainLayout::lastDockWidgetRole() const { return m_lastDockWidgetRole; } QWidgetItem * IdealMainLayout::DockArea::mainSplitter() const { return m_mainSplitter; } void IdealMainLayout::DockArea::setMainSplitter(QWidget* widget) { m_mainSplitter = new QWidgetItem(widget); } IdealMainLayout::DockArea::DockArea(IdealMainLayout* layout, Role role) : width(250) , anchored(false) , m_layout(layout) , m_role(role) , m_mainSplitter(0) { } void IdealMainLayout::DockArea::removeMainSplitter() { if (m_mainSplitter) { delete m_mainSplitter->widget(); delete m_mainSplitter; m_mainSplitter = 0; } } void IdealMainLayout::DockArea::setVisible(bool visible, bool showMainSplitter, QWidget* maximizedWidget) { foreach (QLayoutItem* item, m_items) { bool itemVisible = visible || item->widget() == maximizedWidget; if (item->widget()->isVisible() != itemVisible) item->widget()->setVisible(itemVisible); } if (m_mainSplitter) { bool mainSplitterVisible = (visible && showMainSplitter && !maximizedWidget); if (m_mainSplitter->widget()->isVisible() != mainSplitterVisible) m_mainSplitter->widget()->setVisible(mainSplitterVisible); } bool subSplitterVisible = visible && !maximizedWidget; foreach (QWidgetItem* item, m_subSplitters) { if (item->widget()->isVisible() != subSplitterVisible) item->widget()->setVisible(subSplitterVisible); } } const QList< QWidgetItem * > IdealMainLayout::DockArea::items() const { return m_items; } QWidgetItem * IdealMainLayout::DockArea::first() const { Q_ASSERT(!m_items.isEmpty()); return m_items.first(); } void IdealMainLayout::DockArea::addWidget(QWidget * widget) { m_items.append(new QWidgetItem(widget)); m_heights.append(-1); } void IdealMainLayout::DockArea::removeWidget(QWidget * widget) { int index = 0; while (index < m_items.count()) { if (m_items.at(index)->widget() == widget) break; ++index; } Q_ASSERT(index < m_items.count()); Q_ASSERT(m_heights.count() == m_items.count()); QWidgetItem* item = m_items.takeAt(index); item->widget()->hide(); delete item; m_heights.removeAt(index); } IdealMainLayout::DockArea::~DockArea() { removeMainSplitter(); removeWidgets(); } void IdealMainLayout::DockArea::removeWidgets() { for (int i = m_items.count() - 1; i >= 0; --i) removeWidget(m_items.at(i)->widget()); } int Sublime::IdealMainLayout::DockArea::count() const { int count = 0; if (m_mainSplitter) ++count; count += m_items.count(); count += m_subSplitters.count(); return count; } QLayoutItem * Sublime::IdealMainLayout::DockArea::itemAt(int index, int& at) const { if (m_mainSplitter && index == 0) return m_mainSplitter; if (m_mainSplitter) ++at; if (index < m_items.count() + at) return m_items.at(index - at); at += m_items.count(); if (index < m_subSplitters.count()) return m_subSplitters.at(index - at); return 0; } void Sublime::IdealMainLayout::DockArea::raise() { foreach (QLayoutItem* item, m_items) item->widget()->raise(); foreach (QLayoutItem* item, m_subSplitters) item->widget()->raise(); if (m_mainSplitter) m_mainSplitter->widget()->raise(); } Sublime::Position Sublime::IdealMainLayout::positionForRole(Role role) { switch (role) { case Left: return Sublime::Left; case Right: return Sublime::Right; case Bottom: return Sublime::Bottom; case Top: return Sublime::Top; case Central: Q_ASSERT (false && "called with center role"); } } #include "ideallayout.moc" diff --git a/sublime/ideallayout.h b/sublime/ideallayout.h index 2738336853..bfa0749818 100644 --- a/sublime/ideallayout.h +++ b/sublime/ideallayout.h @@ -1,224 +1,225 @@ /* Copyright 2007 Roberto Raggi Copyright 2007 Hamish Rodda Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SUBLIME_IDEALLAYOUT_H #define SUBLIME_IDEALLAYOUT_H #include #include #include #include #include "sublimedefs.h" class KAction; class KActionCollection; namespace Sublime { class IdealDockWidget; class IdealButtonBarLayout: public QLayout { Q_OBJECT public: IdealButtonBarLayout(Qt::Orientation orientation, QWidget *parent = 0); virtual ~IdealButtonBarLayout(); void setHeight(int height); inline Qt::Orientation orientation() const; virtual Qt::Orientations expandingDirections() const; - virtual bool hasHeightForWidth() const; - - virtual int heightForWidth(int width) const; - virtual QSize minimumSize() const; virtual QSize sizeHint() const; virtual void setGeometry(const QRect &rect); virtual void addItem(QLayoutItem *item); virtual QLayoutItem* itemAt(int index) const; virtual QLayoutItem* takeAt(int index); virtual int count() const; virtual void invalidate(); protected: int doVerticalLayout(const QRect &rect, bool updateGeometry = true) const; int doHorizontalLayout(const QRect &rect, bool updateGeometry = true) const; private: QList _items; Qt::Orientation _orientation; int _height; mutable bool m_minSizeDirty : 1; + mutable bool m_sizeHintDirty : 1; mutable bool m_layoutDirty : 1; mutable QSize m_min; + mutable QSize m_hint; }; class IdealMainLayout : public QLayout { Q_OBJECT public: enum Role { Left /**< aligning left */, Right /**< aligning right */, Bottom /**< aligning bottom */, Top /**< aligning top */, Central /**< aligning central */ }; IdealMainLayout(QWidget *parent = 0); virtual ~IdealMainLayout(); void addWidget(QWidget* widget, Role role); + void addButtonBar(QWidget* widget, Role role); void removeWidgets(Role role); void removeWidget(QWidget* widget, Role role); void removeUnanchored(); int splitterWidth() const; int widthForRole(Role role) const; void setWidthForRole(Role role, int width); bool isAreaAnchored(Role role) const; /** * Maximize the given \a widget, or disable any maximized widget if \a widget is null. */ void maximizeWidget(QWidget* widget); virtual QSize minimumSize() const; virtual QSize sizeHint() const; virtual void setGeometry(const QRect &rect); virtual void addItem(QLayoutItem *item); virtual QLayoutItem* itemAt(int index) const; virtual QLayoutItem* takeAt(int index); virtual int count() const; virtual void invalidate(); IdealDockWidget* lastDockWidget() const; IdealDockWidget* lastDockWidget(Role role) const; Role lastDockWidgetRole() const; class IdealMainWidget* mainWidget() const; static Sublime::Position positionForRole(Role role); public Q_SLOTS: void resizeWidget(int thickness, IdealMainLayout::Role role); void anchorWidget(bool anchor, IdealMainLayout::Role role); void loadSettings(); Q_SIGNALS: /* Reports that a dock widget for role now occupies 'thickness' pixels from the corresponding window edge, if shown. */ void widgetResized(IdealMainLayout::Role role, int thickness); protected: using QLayout::layout; void doLayout(QRect rect) const; void layout(Role role1, Role role2, Role role3, Role role4, QRect& rect) const; void layoutItem(Role role, QRect& rect) const; void sizeHint(Role role, int& minWidth, int& softMinWidth, int& minHeight, int& softMinHeight) const; void minimumSize(Role role, int& minWidth, int& softMinWidth, int& minHeight, int& softMinHeight) const; private: void createArea(Role role); class IdealSplitterHandle* createSplitter(Role role, bool reverse = false); class DockArea { public: DockArea(IdealMainLayout* layout, Role role); ~DockArea(); int count() const; QLayoutItem* itemAt(int index, int& at) const; const QList items() const; QWidgetItem* first() const; void addWidget(QWidget* widget); void removeWidget(QWidget* widget); void removeWidgets(); QWidgetItem* mainSplitter() const; void setMainSplitter(QWidget* splitter); void setVisible(bool visible, bool showMainSplitter = false, QWidget* maximizedWidget = 0); void raise(); int width; bool anchored; QPointer last; private: void removeMainSplitter(); IdealMainLayout* m_layout; Role m_role; QList m_items; QList m_heights; QWidgetItem* m_mainSplitter; QList m_subSplitters; }; QMap m_items; + QMap m_buttonBars; + QMap m_buttonBarItems; mutable bool m_layoutDirty, m_sizeHintDirty, m_minDirty; mutable QSize m_min, m_hint; int m_splitterWidth; QPointer m_lastDockWidget; Role m_lastDockWidgetRole; int m_topOwnsTopLeft; int m_topOwnsTopRight; int m_bottomOwnsBottomLeft; int m_bottomOwnsBottomRight; Role m_maximizedRole; QWidget* m_maximizedWidget; }; IdealMainLayout::Role roleForArea(Qt::DockWidgetArea area); Qt::DockWidgetArea areaForRole(IdealMainLayout::Role role); } #endif