diff --git a/sublime/area.cpp b/sublime/area.cpp index a96d9ce18..c588cbd27 100644 --- a/sublime/area.cpp +++ b/sublime/area.cpp @@ -1,505 +1,484 @@ /*************************************************************************** * Copyright 2006-2007 Alexander Dymo * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "area.h" #include #include #include #include #include #include "view.h" #include "document.h" #include "areaindex.h" #include "controller.h" #include "sublimedebug.h" namespace Sublime { // struct AreaPrivate struct AreaPrivate { AreaPrivate() : rootIndex(new RootAreaIndex) , currentIndex(rootIndex.data()) , controller(nullptr) { } AreaPrivate(const AreaPrivate &p) : title(p.title) , rootIndex(new RootAreaIndex(*(p.rootIndex))) , currentIndex(rootIndex.data()) , controller(p.controller) , toolViewPositions() , desiredToolViews(p.desiredToolViews) , shownToolViews(p.shownToolViews) , iconName(p.iconName) , workingSet(p.workingSet) , m_actions(p.m_actions) { } ~AreaPrivate() { } struct ViewFinder { ViewFinder(View *_view): view(_view), index(0) {} Area::WalkerMode operator() (AreaIndex *idx) { if (idx->hasView(view)) { index = idx; return Area::StopWalker; } return Area::ContinueWalker; } View *view; AreaIndex *index; }; struct ViewLister { Area::WalkerMode operator()(AreaIndex *idx) { views += idx->views(); return Area::ContinueWalker; } QList views; }; QString title; QScopedPointer rootIndex; AreaIndex *currentIndex; Controller *controller; QList toolViews; QMap toolViewPositions; QMap desiredToolViews; QMap shownToolViews; - QMap thickness; QString iconName; QString workingSet; QPointer activeView; QList m_actions; }; // class Area Area::Area(Controller *controller, const QString &name, const QString &title) :QObject(controller), d( new AreaPrivate() ) { // FIXME: using objectName seems fishy. Introduce areaType method, // or some such. setObjectName(name); d->title = title; d->controller = controller; d->iconName = QStringLiteral("kdevelop"); d->workingSet.clear(); qCDebug(SUBLIME) << "initial working-set:" << d->workingSet; initialize(); } Area::Area(const Area &area) : QObject(area.controller()), d( new AreaPrivate( *(area.d) ) ) { setObjectName(area.objectName()); //clone toolviews d->toolViews.clear(); foreach (View *view, area.toolViews()) addToolView(view->document()->createView(), area.toolViewPosition(view)); initialize(); } void Area::initialize() { connect(this, &Area::viewAdded, d->controller, &Controller::notifyViewAdded); connect(this, &Area::aboutToRemoveView, d->controller, &Controller::notifyViewRemoved); connect(this, &Area::toolViewAdded, d->controller, &Controller::notifyToolViewAdded); connect(this, &Area::aboutToRemoveToolView, d->controller, &Controller::notifyToolViewRemoved); connect(this, &Area::toolViewMoved, d->controller, &Controller::toolViewMoved); /* In theory, ownership is passed to us, so should not bother detecting deletion outside. */ // Functor will be called after destructor has run -> capture controller pointer by value // otherwise we crash because we access the already freed pointer this->d auto controller = d->controller; connect(this, &Area::destroyed, controller, [controller] (QObject* obj) { controller->removeArea(static_cast(obj)); }); } Area::~Area() { delete d; } View* Area::activeView() { return d->activeView.data(); } void Area::setActiveView(View* view) { d->activeView = view; } void Area::addView(View *view, AreaIndex *index, View *after) { //View *after = 0; if (!after && controller()->openAfterCurrent()) { after = activeView(); } index->add(view, after); connect(view, &View::positionChanged, this, &Area::positionChanged); qCDebug(SUBLIME) << "view added in" << this; connect(this, &Area::destroyed, view, &View::deleteLater); emit viewAdded(index, view); } void Area::addView(View *view, View *after) { AreaIndex *index = d->currentIndex; if (after) { AreaIndex *i = indexOf(after); if (i) index = i; } addView(view, index); } void Area::addView(View *view, View *viewToSplit, Qt::Orientation orientation) { AreaIndex *indexToSplit = indexOf(viewToSplit); addView(view, indexToSplit, orientation); } void Area::addView(View* view, AreaIndex* indexToSplit, Qt::Orientation orientation) { indexToSplit->split(view, orientation); emit viewAdded(indexToSplit, view); connect(this, &Area::destroyed, view, &View::deleteLater); } View* Area::removeView(View *view) { AreaIndex *index = indexOf(view); Q_ASSERT(index); emit aboutToRemoveView(index, view); index->remove(view); emit viewRemoved(index, view); return view; } AreaIndex *Area::indexOf(View *view) { AreaPrivate::ViewFinder f(view); walkViews(f, d->rootIndex.data()); return f.index; } RootAreaIndex *Area::rootIndex() const { return d->rootIndex.data(); } void Area::addToolView(View *view, Position defaultPosition) { d->toolViews.append(view); const QString id = view->document()->documentSpecifier(); const Position position = d->desiredToolViews.value(id, defaultPosition); d->desiredToolViews[id] = position; d->toolViewPositions[view] = position; emit toolViewAdded(view, position); } void Sublime::Area::raiseToolView(View * toolView) { emit requestToolViewRaise(toolView); } View* Area::removeToolView(View *view) { if (!d->toolViews.contains(view)) return 0; emit aboutToRemoveToolView(view, d->toolViewPositions[view]); QString id = view->document()->documentSpecifier(); qCDebug(SUBLIME) << this << "removed tool view " << id; d->desiredToolViews.remove(id); d->toolViews.removeAll(view); d->toolViewPositions.remove(view); return view; } void Area::moveToolView(View *toolView, Position newPosition) { if (!d->toolViews.contains(toolView)) return; QString id = toolView->document()->documentSpecifier(); d->desiredToolViews[id] = newPosition; d->toolViewPositions[toolView] = newPosition; emit toolViewMoved(toolView, newPosition); } QList &Area::toolViews() const { return d->toolViews; } Position Area::toolViewPosition(View *toolView) const { return d->toolViewPositions[toolView]; } Controller *Area::controller() const { return d->controller; } QList Sublime::Area::views() { AreaPrivate::ViewLister lister; walkViews(lister, d->rootIndex.data()); return lister.views; } QString Area::title() const { return d->title; } void Area::setTitle(const QString &title) { d->title = title; } void Area::save(KConfigGroup& group) const { QStringList desired; QMap::iterator i, e; for (i = d->desiredToolViews.begin(), e = d->desiredToolViews.end(); i != e; ++i) { desired << i.key() + ':' + QString::number(static_cast(i.value())); } group.writeEntry("desired views", desired); qCDebug(SUBLIME) << "save " << this << "wrote" << group.readEntry("desired views", ""); group.writeEntry("view on left", shownToolViews(Sublime::Left)); group.writeEntry("view on right", shownToolViews(Sublime::Right)); group.writeEntry("view on top", shownToolViews(Sublime::Top)); group.writeEntry("view on bottom", shownToolViews(Sublime::Bottom)); - group.writeEntry("thickness left", thickness(Sublime::Left)); - group.writeEntry("thickness right", thickness(Sublime::Right)); - group.writeEntry("thickness bottom", thickness(Sublime::Bottom)); - group.writeEntry("thickness top", thickness(Sublime::Top)); group.writeEntry("working set", d->workingSet); } void Area::load(const KConfigGroup& group) { qCDebug(SUBLIME) << "loading areas config"; d->desiredToolViews.clear(); QStringList desired = group.readEntry("desired views", QStringList()); foreach (const QString &s, desired) { int i = s.indexOf(':'); if (i != -1) { QString id = s.left(i); int pos_i = s.midRef(i+1).toInt(); Sublime::Position pos = static_cast(pos_i); if (pos != Sublime::Left && pos != Sublime::Right && pos != Sublime::Top && pos != Sublime::Bottom) { pos = Sublime::Bottom; } d->desiredToolViews[id] = pos; } } setShownToolViews(Sublime::Left, group.readEntry("view on left", QStringList())); setShownToolViews(Sublime::Right, group.readEntry("view on right", QStringList())); setShownToolViews(Sublime::Top, group.readEntry("view on top", QStringList())); setShownToolViews(Sublime::Bottom, group.readEntry("view on bottom", QStringList())); - setThickness(Sublime::Left, group.readEntry("thickness left", -1)); - setThickness(Sublime::Right, group.readEntry("thickness right", -1)); - setThickness(Sublime::Bottom, group.readEntry("thickness bottom", -1)); - setThickness(Sublime::Top, group.readEntry("thickness top", -1)); setWorkingSet(group.readEntry("working set", d->workingSet)); } bool Area::wantToolView(const QString& id) { return (d->desiredToolViews.contains(id)); } void Area::setShownToolViews(Sublime::Position pos, const QStringList& ids) { d->shownToolViews[pos] = ids; } QStringList Area::shownToolViews(Sublime::Position pos) const { if (pos == Sublime::AllPositions) { QStringList allIds; std::for_each(d->shownToolViews.constBegin(), d->shownToolViews.constEnd(), [&](const QStringList& ids) { allIds << ids; }); return allIds; } return d->shownToolViews[pos]; } void Area::setDesiredToolViews( const QMap& desiredToolViews) { d->desiredToolViews = desiredToolViews; } -void Area::setThickness(Sublime::Position pos, int thickness) -{ - d->thickness[pos] = thickness; -} - -int Area::thickness(Sublime::Position pos) const -{ - if (!d->thickness.contains(pos)) - return -1; - return (d->thickness)[pos]; -} - QString Area::iconName() const { return d->iconName; } void Area::setIconName(const QString& iconName) { d->iconName = iconName; } void Area::positionChanged(View *view, int newPos) { qCDebug(SUBLIME) << view << newPos; AreaIndex *index = indexOf(view); index->views().move(index->views().indexOf(view), newPos); } QString Area::workingSet() const { return d->workingSet; } void Area::setWorkingSet(QString name) { if(name != d->workingSet) { qCDebug(SUBLIME) << this << "setting new working-set" << name; QString oldName = d->workingSet; emit changingWorkingSet(this, oldName, name); d->workingSet = name; emit changedWorkingSet(this, oldName, name); } } bool Area::closeView(View* view, bool silent) { QPointer doc = view->document(); // We don't just delete the view, because if silent is false, we might need to ask the user. if(doc && !silent) { // Do some counting to check whether we need to ask the user for feedback qCDebug(SUBLIME) << "Closing view for" << view->document()->documentSpecifier() << "views" << view->document()->views().size() << "in area" << this; int viewsInCurrentArea = 0; // Number of views for the same document in the current area int viewsInOtherAreas = 0; // Number of views for the same document in other areas int viewsInOtherWorkingSets = 0; // Number of views for the same document in areas with different working-set foreach(View* otherView, doc.data()->views()) { Area* area = controller()->areaForView(otherView); if(area == this) viewsInCurrentArea += 1; if(!area || (area != this)) viewsInOtherAreas += 1; if(area && area != this && area->workingSet() != workingSet()) viewsInOtherWorkingSets += 1; } if(viewsInCurrentArea == 1 && (viewsInOtherAreas == 0 || viewsInOtherWorkingSets == 0)) { // Time to ask the user for feedback, because the document will be completely closed // due to working-set synchronization if( !doc.data()->askForCloseFeedback() ) return false; } } // otherwise we can silently close the view, // the document will still have an opened view somewhere delete removeView(view); return true; } void Area::clearViews(bool silent) { foreach(Sublime::View* view, views()) { closeView(view, silent); } } void Area::clearDocuments() { if (views().isEmpty()) emit clearWorkingSet(this); else clearViews(true); } QList Area::actions() const { return d->m_actions; } void Area::addAction(QAction* action) { Q_ASSERT(!d->m_actions.contains(action)); connect(action, &QAction::destroyed, this, &Area::actionDestroyed); d->m_actions.append(action); } void Area::actionDestroyed(QObject* action) { d->m_actions.removeAll(qobject_cast(action)); } } diff --git a/sublime/area.h b/sublime/area.h index 4b5965693..2544c356d 100644 --- a/sublime/area.h +++ b/sublime/area.h @@ -1,290 +1,287 @@ /*************************************************************************** * Copyright 2006-2007 Alexander Dymo * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef KDEVPLATFORM_SUBLIMEAREA_H #define KDEVPLATFORM_SUBLIMEAREA_H #include #include "sublimeexport.h" #include "areaindex.h" #include "sublimedefs.h" #include class QAction; namespace Sublime { class AreaIndex; class RootAreaIndex; class Controller; class View; /** @short Area - the universal view container Area contains views and toolviews, knows about their positions and provides functionality to add new (tool)views and remove existing. Area takes care of all placement/configuration details so that in order for @ref MainWindow to show the area it just needs to reconstruct itself according to the area's rules. Usual way of creating an area is: @code Controller *controller = new Controller(); ... //document creation code here Area *area = new Area(controller, "My Area"); area->addView(document->createView()); MainWindow *mw = new MainWindow(controller); controller->show(area, mw); @endcode */ class KDEVPLATFORMSUBLIME_EXPORT Area: public QObject { Q_OBJECT public: /**Creates and area with given @p name and adds it to the @p controller. @param controller is the controller in which this area will be available. @param name should identify this area and be unique for all areas in the controller. @ref QObject::objectName shall be used to get this name after area creation. @param title is the user-visible (translatable) title for the area. Use @ref title and @ref setTitle to operate on the title. This parameter can be omitted and then name will be used as title.*/ Area(Controller *controller, const QString &name, const QString &title = {}); Area(const Area &area); ~Area() override; QString title() const; void setTitle(const QString &title); QString iconName() const; void setIconName(const QString &iconName); /**Adds the @p view to the list of views at the given area index, after the given view @p after. If @p after == 0 && controller()->openAfterCurrent(): @p view is inserted after current view If @p after == 0 && !controller()->openAfterCurrent(): @p view is inserted at the last position. */ void addView(View *view, AreaIndex *index, View *after = nullptr); /**Adds the @p view to the list of views at the current area index.*/ void addView(View *view, View *after = nullptr); /**Adds the @p view to the area splitting the @p viewToSplit using given @p orientation. @p view Will be in the second child index of the area-index containing the view. */ void addView(View *view, View *viewToSplit, Qt::Orientation orientation); /**Adds the @p view to the area splitting the area index @p indexToSplit using given @p orientation. @p view Will be in the second child index of the area-index containing the view. */ void addView(View *view, AreaIndex *indexToSplit, Qt::Orientation orientation); /**Removes the @p view from the area. Does not delete it. */ View* removeView(View *view); /**@return the list of all views in this area in no particular order. To process the views in ordered manner (tree walk) use @ref walkViews method. This method is added only for convenience.*/ QList views(); /** Removes all views from this area and deletes them. * If an open document has changes, and it is the last view of that document, * the user may push 'Cancel', and the view will stay active. * @param silent If this is true, the user is never asked for feedback. */ void clearViews(bool silent = false); /** * Returns the view that was last stored through setActiveView(view), or zero * if the view was deleted or it was never set. */ View* activeView(); /** * Allows marking a view as active that can later be retrieved through activeView() */ void setActiveView(View* view); /** Closes and deletes the view, asking the user for feedback if needed. * Closes the document if it is the last view. * Does allow breaking the closing process. * If it is the last view of the document that has changes, and the user pushed 'Cancel', * false will be returned, and the view will not be closed. * @param silent If this is false, the user will be asked for feedback. Otherwise he won't. */ bool closeView(View* view, bool silent = false); /**@return the index of view or 0 if it can not be found.*/ AreaIndex *indexOf(View *view); /**@return the root index of the area. Root index always exists so this method will never return 0.*/ RootAreaIndex *rootIndex() const; /**Adds the toolview to the area. Area will use its configuration and restore the proper position for the toolview when necessary. If it has no configuration for this view, it will use @p defaultPosition.*/ void addToolView(View *toolView, Position defaultPosition); /**Removes the toolview from the area.*/ View* removeToolView(View *toolView); /**Moves the toolview to a different position. */ void moveToolView(View *toolView, Position newPosition); /**Raise tool view.*/ void raiseToolView(View *toolView); /**@return the list of toolviews in the area. No particular sort order is guaranteed.*/ QList &toolViews() const; /**@return the current position of @p toolView in the area.*/ Position toolViewPosition(View *toolView) const; /* Returns true if this area actively desires to show a tool view with id of 'id'. The area, of course, will show any tool view added with 'addToolView', however, this method can be used to guess a set of tool views that make most sense to be added. */ bool wantToolView(const QString& id); void setShownToolViews(Sublime::Position pos, const QStringList& ids); QStringList shownToolViews(Sublime::Position pos) const; void setDesiredToolViews( const QMap& desiredToolViews); - void setThickness(Sublime::Position pos, int thickness); - int thickness(Sublime::Position pos) const; - void save(KConfigGroup& group) const; void load(const KConfigGroup& group); /**@return the controller for this area.*/ Controller *controller() const; ///Returns the currently set working-set for this area. The working-set is persistent QString workingSet() const; ///Sets the working-set for this area. The working-set is just a marker, and does nothing ///within Area. ///The actual view management has to be implemented in the entity that knows more ///about possible views, documents, etc. (kdevplatform/shell) ///@warning (KDevelop): Before calling this, make sure that all views are saved! (see IDocumentController::saveAllDocumentsForWindow) void setWorkingSet(QString name); /**Walker mode to determine the behavior of area walkers.*/ enum WalkerMode { StopWalker, /**< Stop after processing this area index or toolview */ ContinueWalker /**< Continue walking */ }; /**Walks the tree of area indices and executes the operator. It will always walk the tree of views from top to bottom from left to right. Operator should be the class with WalkerResult operator()(AreaIndex *index) method. That method should return Area::StopWalker if the walker has to stop at current index or Area::ContinueWalker to continue. Example (operator to print the indices, assumes hypothetical operator <<()): @code struct MyOperator { WalkerMode operator()(AreaIndex *index) { std::cerr << index << std::endl; return Area::ContinueWalker; } }; ... MyOperator op; walkViews(op, rootIndex()) @endcode*/ template void walkViews(Operator &op, AreaIndex *index); /**Walks the list of toolviews. The order in which toolviews are walked is not specified. Operator should be the class with bool operator()(View *view, Sublime::Position position) method. That method should return Area::StopWalker if the walker has to stop at current index or Area::ContinueWalker to continue. Example (operator to print the list of views): @code struct MyOperator { WalkerMode operator()(View *view, Sublime::Position position) { std::cerr << view << " at position " << position << std::endl; return Area::ContinueWalker; } }; ... MyOperator op; walkToolViews(op, Sublime::AllPositions) @endcode */ template void walkToolViews(Operator &op, Positions positions); /** Adds an action to the area. They will be made available from different places, like the Area Display*/ void addAction(QAction* action); /** @returns the actions related to the area */ QList actions() const; /** * Closes all the views and requests the working set to be cleared. * Works even though the area isn't opened yet */ void clearDocuments(); Q_SIGNALS: /**Emitted when a new view is added to the area.*/ void viewAdded(Sublime::AreaIndex*, Sublime::View*); /**Emitted when a view is going to be removed from the area.*/ void aboutToRemoveView(Sublime::AreaIndex*, Sublime::View*); /**Emitted when a view was removed from the area.*/ void viewRemoved(Sublime::AreaIndex*, Sublime::View*); /**Emitted when a new toolview is added to the area.*/ void toolViewAdded(Sublime::View*, Sublime::Position); /**Emitted when a toolview is requesting to be raised.*/ void requestToolViewRaise(Sublime::View*); /**Emitted when a toolview is going to be removed from the area.*/ void aboutToRemoveToolView(Sublime::View*, Sublime::Position); /**Emitted when a toolview is moved to a different position.*/ void toolViewMoved(Sublime::View*, Sublime::Position); /**Emitted before the working-set is changed.*/ void changingWorkingSet(Sublime::Area* area, QString from, QString to); /**Emitted after the working-set was changed.*/ void changedWorkingSet(Sublime::Area* area, QString from, QString to); /** notifies the working set that it should clear */ void clearWorkingSet(Sublime::Area* area); private Q_SLOTS: void positionChanged(Sublime::View*, int); void actionDestroyed(QObject* action); private: template WalkerMode walkViewsInternal(Operator &op, AreaIndex *index); void initialize(); struct AreaPrivate *const d; }; } #include "areawalkers.h" #endif diff --git a/sublime/idealcontroller.cpp b/sublime/idealcontroller.cpp index f14b9a034..72e8b3860 100644 --- a/sublime/idealcontroller.cpp +++ b/sublime/idealcontroller.cpp @@ -1,511 +1,506 @@ /* Copyright 2007 Roberto Raggi Copyright 2007 Hamish Rodda Copyright 2011 Alexander Dymo 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 "idealcontroller.h" #include #include #include #include #include #include #include "area.h" #include "view.h" #include "document.h" #include "mainwindow.h" #include "ideallayout.h" #include "idealtoolbutton.h" #include "idealdockwidget.h" #include "idealbuttonbarwidget.h" using namespace Sublime; IdealController::IdealController(Sublime::MainWindow* mainWindow): QObject(mainWindow), m_mainWindow(mainWindow) { leftBarWidget = new IdealButtonBarWidget(Qt::LeftDockWidgetArea, this, m_mainWindow); connect(leftBarWidget, &IdealButtonBarWidget::customContextMenuRequested, this, &IdealController::slotDockBarContextMenuRequested); rightBarWidget = new IdealButtonBarWidget(Qt::RightDockWidgetArea, this, m_mainWindow); connect(rightBarWidget, &IdealButtonBarWidget::customContextMenuRequested, this, &IdealController::slotDockBarContextMenuRequested); bottomBarWidget = new IdealButtonBarWidget(Qt::BottomDockWidgetArea, this, m_mainWindow); bottomStatusBarLocation = bottomBarWidget->corner(); connect(bottomBarWidget, &IdealButtonBarWidget::customContextMenuRequested, this, &IdealController::slotDockBarContextMenuRequested); topBarWidget = new IdealButtonBarWidget(Qt::TopDockWidgetArea, this, m_mainWindow); connect(topBarWidget, &IdealButtonBarWidget::customContextMenuRequested, this, &IdealController::slotDockBarContextMenuRequested); m_docks = qobject_cast(mainWindow->action("docks_submenu")); m_showLeftDock = qobject_cast(m_mainWindow->action("show_left_dock")); m_showRightDock = qobject_cast(m_mainWindow->action("show_right_dock")); m_showBottomDock = qobject_cast(m_mainWindow->action("show_bottom_dock")); m_showTopDock = qobject_cast(m_mainWindow->action("show_top_dock")); connect(m_mainWindow, &MainWindow::settingsLoaded, this, &IdealController::loadSettings); } void IdealController::addView(Qt::DockWidgetArea area, View* view) { IdealDockWidget *dock = new IdealDockWidget(this, m_mainWindow); // dock object name is used to store toolview settings QString dockObjectName = view->document()->title(); // support different configuration for same docks opened in different areas if (m_mainWindow->area()) dockObjectName += '_' + m_mainWindow->area()->objectName(); dock->setObjectName(dockObjectName); 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(); QToolBar *toolBar = new QToolBar(toolView); int iconSize = m_mainWindow->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(false); toolBar->addActions(toolBarActions); toolView->setCentralWidget(w); toolView->addToolBar(toolBar); dock->setWidget(toolView); } dock->setWindowTitle(view->widget()->windowTitle()); dock->setWindowIcon(view->widget()->windowIcon()); dock->setFocusProxy(dock->widget()); if (IdealButtonBarWidget* bar = barForDockArea(area)) { QAction* 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); connect(dock, &IdealDockWidget::closeRequested, action, &QAction::toggle); } connect(dock, &IdealDockWidget::dockLocationChanged, this, &IdealController::dockLocationChanged); dock->hide(); docks.insert(dock); } void IdealController::dockLocationChanged(Qt::DockWidgetArea area) { IdealDockWidget *dock = qobject_cast(sender()); View *view = dock->view(); QAction* action = m_view_to_action.value(view); if (dock->dockWidgetArea() == area) { // this event can happen even when dock changes its location within the same area // usecases: // 1) user drags to the same area // 2) user rearranges toolviews inside the same area // 3) state restoration shows the dock widget // in 3rd case we need to show dock if we don't want it to be shown // TODO: adymo: invent a better solution for the restoration problem if (!action->isChecked() && dock->isVisible()) { dock->hide(); } return; } if (IdealButtonBarWidget* bar = barForDockArea(dock->dockWidgetArea())) bar->removeAction(action); docks.insert(dock); if (IdealButtonBarWidget* bar = barForDockArea(area)) { QAction* action = bar->addWidget( view->document()->title(), dock, static_cast(parent())->area(), view); m_dockwidget_to_action[dock] = m_view_to_action[view] = action; // at this point the dockwidget is visible (user dragged it) // properly set up UI state bar->showWidget(action, true); // the dock should now be the "last" opened in a new area, not in the old area for (auto it = lastDockWidget.begin(); it != lastDockWidget.end(); ++it) { if (it->data() == dock) it->clear(); } lastDockWidget[area] = dock; // after drag, the toolview loses focus, so focus it again dock->setFocus(Qt::ShortcutFocusReason); m_docks->addAction(action); } if (area == Qt::BottomDockWidgetArea || area == Qt::TopDockWidgetArea) dock->setFeatures( QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable | IdealDockWidget::DockWidgetVerticalTitleBar | QDockWidget::DockWidgetMovable); else dock->setFeatures( QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetMovable ); } IdealButtonBarWidget* IdealController::barForDockArea(Qt::DockWidgetArea area) const { switch (area) { case Qt::LeftDockWidgetArea: return leftBarWidget; case Qt::TopDockWidgetArea: return topBarWidget; case Qt::RightDockWidgetArea: return rightBarWidget; case Qt::BottomDockWidgetArea: return bottomBarWidget; default: Q_ASSERT(false); return 0; } } void IdealController::slotDockBarContextMenuRequested(QPoint position) { IdealButtonBarWidget* bar = qobject_cast(sender()); Q_ASSERT(bar); emit dockBarContextMenuRequested(bar->area(), bar->mapToGlobal(position)); } void IdealController::raiseView(View* view, RaiseMode mode) { /// @todo GroupWithOtherViews is disabled for now by forcing "mode = HideOtherViews". /// for the release of KDevelop 4.3. /// Reason: Inherent bugs which need significant changes to be fixed. /// Example: Open two equal toolviews (for example 2x konsole), /// activate one, switch area, switch back, -> Both are active instead of one. /// The problem is that views are identified purely by their factory-id, which is equal /// for toolviews of the same type. mode = HideOtherViews; QAction* action = m_view_to_action.value(view); Q_ASSERT(action); QWidget *focusWidget = m_mainWindow->focusWidget(); action->setProperty("raise", mode); action->setChecked(true); // TODO: adymo: hack: focus needs to stay inside the previously // focused widget (setChecked will focus the toolview) if (focusWidget) focusWidget->setFocus(Qt::ShortcutFocusReason); } QList< IdealDockWidget* > IdealController::allDockWidgets() { return docks.toList(); } void IdealController::showDockWidget(IdealDockWidget* dock, bool show) { Q_ASSERT(docks.contains(dock)); Qt::DockWidgetArea area = dock->dockWidgetArea(); if (show) { m_mainWindow->addDockWidget(area, dock); dock->show(); } else { m_mainWindow->removeDockWidget(dock); } setShowDockStatus(area, show); emit dockShown(dock->view(), Sublime::dockAreaToPosition(area), show); if (!show) // Put the focus back on the editor if a dock was hidden focusEditor(); else { // focus the dock dock->setFocus(Qt::ShortcutFocusReason); } } void IdealController::focusEditor() { if (View* view = m_mainWindow->activeView()) if (view->hasWidget()) view->widget()->setFocus(Qt::ShortcutFocusReason); } QWidget* IdealController::statusBarLocation() const { return bottomStatusBarLocation; } QAction* IdealController::actionForView(View* view) const { return m_view_to_action.value(view); } void IdealController::setShowDockStatus(Qt::DockWidgetArea area, bool checked) { QAction* action = actionForArea(area); if (action->isChecked() != checked) { bool blocked = action->blockSignals(true); action->setChecked(checked); action->blockSignals(blocked); } } QAction* IdealController::actionForArea(Qt::DockWidgetArea area) const { switch (area) { case Qt::LeftDockWidgetArea: default: return m_showLeftDock; case Qt::RightDockWidgetArea: return m_showRightDock; case Qt::TopDockWidgetArea: return m_showTopDock; case Qt::BottomDockWidgetArea: return m_showBottomDock; } } void IdealController::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); if (!dock) { // toolviews with a toolbar live in a QMainWindow which lives in a Dock Q_ASSERT(qobject_cast(viewParent)); viewParent = viewParent->parentWidget(); dock = qobject_cast(viewParent); } 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 = barForDockArea(dock->dockWidgetArea())) bar->removeAction(action); m_view_to_action.remove(view); m_dockwidget_to_action.remove(dock); if (nondestructive) view->widget()->setParent(0); delete dock; } void IdealController::moveView(View *view, Qt::DockWidgetArea area) { removeView(view); addView(area, view); } void IdealController::showBottomDock(bool show) { showDock(Qt::BottomDockWidgetArea, show); } void IdealController::showLeftDock(bool show) { showDock(Qt::LeftDockWidgetArea, show); } void IdealController::showRightDock(bool show) { showDock(Qt::RightDockWidgetArea, show); } void IdealController::hideDocks(IdealButtonBarWidget *bar) { foreach (QAction *action, bar->actions()) { if (action->isChecked()) action->setChecked(false); } focusEditor(); } void IdealController::showDock(Qt::DockWidgetArea area, bool show) { IdealButtonBarWidget *bar = barForDockArea(area); if (!bar) return; IdealDockWidget *lastDock = lastDockWidget[area].data(); if (lastDock && lastDock->isVisible() && !lastDock->hasFocus()) { lastDock->setFocus(Qt::ShortcutFocusReason); // re-sync action state given we may have asked for the dock to be hidden QAction* action = actionForArea(area); if (!action->isChecked()) { action->blockSignals(true); action->setChecked(true); action->blockSignals(false); } return; } if (!show) { hideDocks(bar); } else { // open the last opened toolview (or the first one) and focus it if (lastDock) { if (QAction *action = m_dockwidget_to_action.value(lastDock)) action->setChecked(show); lastDock->setFocus(Qt::ShortcutFocusReason); return; } if (!barForDockArea(area)->actions().isEmpty()) barForDockArea(area)->actions().first()->setChecked(show); } } // returns currently focused dock widget (if any) IdealDockWidget* IdealController::currentDockWidget() { QWidget *w = m_mainWindow->focusWidget(); while (true) { if (!w) break; IdealDockWidget *dockCandidate = qobject_cast(w); if (dockCandidate) return dockCandidate; w = w->parentWidget(); } return 0; } void IdealController::goPrevNextDock(IdealController::Direction direction) { IdealDockWidget *currentDock = currentDockWidget(); if (!currentDock) return; IdealButtonBarWidget *bar = barForDockArea(currentDock->dockWidgetArea()); int index = bar->actions().indexOf(m_dockwidget_to_action.value(currentDock)); if (direction == NextDock) { if (index < 1) index = bar->actions().count() - 1; else --index; } else { 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 IdealController::toggleDocksShown() { QList allActions; allActions += leftBarWidget->actions(); allActions += bottomBarWidget->actions(); allActions += rightBarWidget->actions(); bool show = true; foreach (QAction *action, allActions) { if (action->isChecked()) { show = false; break; } } toggleDocksShown(leftBarWidget, show); toggleDocksShown(bottomBarWidget, show); toggleDocksShown(rightBarWidget, show); } void IdealController::toggleDocksShown(IdealButtonBarWidget* bar, bool show) { if (!show) { hideDocks(bar); } else { IdealDockWidget *lastDock = lastDockWidget[bar->area()].data(); if (lastDock) m_dockwidget_to_action[lastDock]->setChecked(true); } } void IdealController::loadSettings() { KConfigGroup cg(KSharedConfig::openConfig(), "UiSettings"); int bottomOwnsBottomLeft = cg.readEntry("BottomLeftCornerOwner", 0); if (bottomOwnsBottomLeft) m_mainWindow->setCorner(Qt::BottomLeftCorner, Qt::BottomDockWidgetArea); else m_mainWindow->setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea); int bottomOwnsBottomRight = cg.readEntry("BottomRightCornerOwner", 0); if (bottomOwnsBottomRight) m_mainWindow->setCorner(Qt::BottomRightCorner, Qt::BottomDockWidgetArea); else m_mainWindow->setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea); } -void IdealController::setWidthForArea(Qt::DockWidgetArea area, int width) -{ - m_widthsForAreas[area] = width; -} - void IdealController::emitWidgetResized(Qt::DockWidgetArea dockArea, int thickness) { emit widgetResized(dockArea, thickness); } diff --git a/sublime/idealcontroller.h b/sublime/idealcontroller.h index 7ffdf1a70..92ebd9d0a 100644 --- a/sublime/idealcontroller.h +++ b/sublime/idealcontroller.h @@ -1,136 +1,133 @@ /* Copyright 2007 Roberto Raggi Copyright 2007 Hamish Rodda Copyright 2011 Alexander Dymo 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 #include #include #include #include #include "sublimedefs.h" class QAction; class KActionMenu; namespace Sublime { class Area; class View; class MainWindow; class IdealButtonBarWidget; class IdealDockWidget; class View; class IdealController: public QObject { Q_OBJECT public: explicit IdealController(Sublime::MainWindow *mainWindow); void addView(Qt::DockWidgetArea area, View* view); enum RaiseMode { HideOtherViews, GroupWithOtherViews }; void raiseView(View* view, RaiseMode mode = HideOtherViews); void showDockWidget(IdealDockWidget* dock, bool show); void focusEditor(); QWidget *statusBarLocation() const; QAction* actionForView(View* view) const; void setShowDockStatus(Qt::DockWidgetArea area, bool checked); /** 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); void showLeftDock(bool show); void showRightDock(bool show); void showBottomDock(bool show); void toggleDocksShown(); IdealButtonBarWidget* barForDockArea(Qt::DockWidgetArea area) const; QAction* actionForArea(Qt::DockWidgetArea area) const; enum Direction { NextDock, PrevDock }; void goPrevNextDock(IdealController::Direction direction); - void setWidthForArea(Qt::DockWidgetArea, int); - IdealButtonBarWidget *leftBarWidget; IdealButtonBarWidget *rightBarWidget; IdealButtonBarWidget *bottomBarWidget; IdealButtonBarWidget *topBarWidget; QWidget *bottomStatusBarLocation; IdealDockWidget* currentDockWidget(); QMap > lastDockWidget; void emitWidgetResized(Qt::DockWidgetArea dockArea, int thickness); QList allDockWidgets(); Q_SIGNALS: /// Emitted, when a context menu is requested on one of the dock bars. /// When no actions gets associated to the QMenu, it won't be shown. void dockBarContextMenuRequested(Qt::DockWidgetArea area, const QPoint& position); void dockShown(Sublime::View*, Sublime::Position pos, bool shown); void widgetResized(Qt::DockWidgetArea dockArea, int thickness); private Q_SLOTS: void slotDockBarContextMenuRequested(QPoint position); void dockLocationChanged(Qt::DockWidgetArea); void loadSettings(); private: void hideDocks(IdealButtonBarWidget *bar); void showDock(Qt::DockWidgetArea area, bool show); void toggleDocksShown(IdealButtonBarWidget *bar, bool show); Sublime::MainWindow *m_mainWindow; QSet 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; - QMap m_widthsForAreas; KActionMenu* m_docks; QAction* m_showLeftDock; QAction* m_showRightDock; QAction* m_showBottomDock; QAction* m_showTopDock; }; } #endif diff --git a/sublime/mainwindow_p.cpp b/sublime/mainwindow_p.cpp index 74ef494fb..49494e9d4 100644 --- a/sublime/mainwindow_p.cpp +++ b/sublime/mainwindow_p.cpp @@ -1,819 +1,815 @@ /*************************************************************************** * Copyright 2006-2009 Alexander Dymo * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "mainwindow_p.h" #include #include #include #include #include #include #include #include "area.h" #include "view.h" #include "areaindex.h" #include "document.h" #include "container.h" #include "controller.h" #include "mainwindow.h" #include "idealcontroller.h" #include "holdupdates.h" #include "idealbuttonbarwidget.h" #include "sublimedebug.h" class IdealToolBar : public QToolBar { Q_OBJECT public: explicit IdealToolBar(const QString& title, bool hideWhenEmpty, Sublime::IdealButtonBarWidget* buttons, QMainWindow* parent) : QToolBar(title, parent) , m_timer(nullptr) , m_buttons(buttons) , m_hideWhenEmpty(hideWhenEmpty) , m_requestedVisibility(true) { setMovable(false); setFloatable(false); setObjectName(title); layout()->setMargin(0); addWidget(m_buttons); if (m_hideWhenEmpty) { m_timer = new QTimer(this); m_timer->setInterval(100); m_timer->setSingleShot(true); connect(m_timer, &QTimer::timeout, this, &IdealToolBar::refresh); connect(this, &IdealToolBar::visibilityChanged, m_timer, static_cast(&QTimer::start)); connect(m_buttons, &Sublime::IdealButtonBarWidget::emptyChanged, m_timer, static_cast(&QTimer::start)); } } void setVisible(bool visible) override { QToolBar::setVisible(visible); m_requestedVisibility = visible; if (m_hideWhenEmpty && visible) { m_timer->start(); } } private slots: void refresh() { setVisible(m_requestedVisibility && !m_buttons->isEmpty()); } private: QTimer* m_timer; Sublime::IdealButtonBarWidget* m_buttons; const bool m_hideWhenEmpty; bool m_requestedVisibility; }; namespace Sublime { MainWindowPrivate::MainWindowPrivate(MainWindow *w, Controller* controller) :controller(controller), area(0), activeView(0), activeToolView(0), bgCentralWidget(0), ignoreDockShown(false), autoAreaSettingsSave(false), m_mainWindow(w) { KActionCollection *ac = m_mainWindow->actionCollection(); m_concentrationModeAction = new QAction(i18n("Concentration Mode"), this); m_concentrationModeAction->setIcon(QIcon::fromTheme(QStringLiteral("page-zoom"))); m_concentrationModeAction->setToolTip(i18n("Removes most of the controls so you can focus on what matters.")); m_concentrationModeAction->setCheckable(true); m_concentrationModeAction->setChecked(false); ac->setDefaultShortcut(m_concentrationModeAction, Qt::META | Qt::Key_C); connect(m_concentrationModeAction, &QAction::toggled, this, &MainWindowPrivate::restoreConcentrationMode); ac->addAction(QStringLiteral("toggle_concentration_mode"), m_concentrationModeAction); QAction* action = new QAction(i18n("Show Left Dock"), this); action->setCheckable(true); ac->setDefaultShortcut(action, Qt::META | Qt::CTRL | Qt::Key_Left); connect(action, &QAction::toggled, this, &MainWindowPrivate::showLeftDock); ac->addAction(QStringLiteral("show_left_dock"), action); action = new QAction(i18n("Show Right Dock"), this); action->setCheckable(true); ac->setDefaultShortcut(action, Qt::META | Qt::CTRL | Qt::Key_Right); connect(action, &QAction::toggled, this, &MainWindowPrivate::showRightDock); ac->addAction(QStringLiteral("show_right_dock"), action); action = new QAction(i18n("Show Bottom Dock"), this); action->setCheckable(true); ac->setDefaultShortcut(action, Qt::META | Qt::CTRL | Qt::Key_Down); connect(action, &QAction::toggled, this, &MainWindowPrivate::showBottomDock); ac->addAction(QStringLiteral("show_bottom_dock"), action); action = new QAction(i18nc("@action", "Focus Editor"), this); ac->setDefaultShortcut(action, Qt::META | Qt::CTRL | Qt::Key_E); connect(action, &QAction::triggered, this, &MainWindowPrivate::focusEditor); ac->addAction(QStringLiteral("focus_editor"), action); action = new QAction(i18n("Hide/Restore Docks"), this); ac->setDefaultShortcut(action, Qt::META | Qt::CTRL | Qt::Key_Up); connect(action, &QAction::triggered, this, &MainWindowPrivate::toggleDocksShown); ac->addAction(QStringLiteral("hide_all_docks"), action); action = new QAction(i18n("Next Tool View"), this); ac->setDefaultShortcut(action, Qt::META | Qt::CTRL | Qt::Key_N); action->setIcon(QIcon::fromTheme(QStringLiteral("go-next"))); connect(action, &QAction::triggered, this, &MainWindowPrivate::selectNextDock); ac->addAction(QStringLiteral("select_next_dock"), action); action = new QAction(i18n("Previous Tool View"), this); ac->setDefaultShortcut(action, Qt::META | Qt::CTRL | Qt::Key_P); action->setIcon(QIcon::fromTheme(QStringLiteral("go-previous"))); connect(action, &QAction::triggered, this, &MainWindowPrivate::selectPreviousDock); ac->addAction(QStringLiteral("select_previous_dock"), action); action = new KActionMenu(i18n("Tool Views"), this); ac->addAction(QStringLiteral("docks_submenu"), action); idealController = new IdealController(m_mainWindow); m_leftToolBar = new IdealToolBar(i18n("Left Button Bar"), true, idealController->leftBarWidget, m_mainWindow); m_mainWindow->addToolBar(Qt::LeftToolBarArea, m_leftToolBar); m_rightToolBar = new IdealToolBar(i18n("Right Button Bar"), true, idealController->rightBarWidget, m_mainWindow); m_mainWindow->addToolBar(Qt::RightToolBarArea, m_rightToolBar); m_bottomToolBar = new IdealToolBar(i18n("Bottom Button Bar"), false, idealController->bottomBarWidget, m_mainWindow); m_mainWindow->addToolBar(Qt::BottomToolBarArea, m_bottomToolBar); // adymo: intentionally do not add a toolbar for top buttonbar // this doesn't work well with toolbars added via xmlgui centralWidget = new QWidget; QVBoxLayout* layout = new QVBoxLayout(centralWidget); centralWidget->setLayout(layout); layout->setMargin(0); splitterCentralWidget = new QSplitter(centralWidget); layout->addWidget(splitterCentralWidget); m_mainWindow->setCentralWidget(centralWidget); connect(idealController, &IdealController::dockShown, this, &MainWindowPrivate::slotDockShown); connect(idealController, &IdealController::widgetResized, this, &MainWindowPrivate::widgetResized); connect(idealController, &IdealController::dockBarContextMenuRequested, m_mainWindow, &MainWindow::dockBarContextMenuRequested); } MainWindowPrivate::~MainWindowPrivate() { delete m_leftTabbarCornerWidget.data(); m_leftTabbarCornerWidget.clear(); } void MainWindowPrivate::disableConcentrationMode() { m_concentrationModeAction->setChecked(false); restoreConcentrationMode(); } void MainWindowPrivate::restoreConcentrationMode() { const bool concentrationModeOn = m_concentrationModeAction->isChecked(); QWidget* cornerWidget = nullptr; if (m_concentrateToolBar) { QLayout* l = m_concentrateToolBar->layout(); QLayoutItem* li = l->takeAt(1); //ensure the cornerWidget isn't destroyed with the toolbar if (li) { cornerWidget = li->widget(); delete li; } m_concentrateToolBar->deleteLater(); } m_mainWindow->menuBar()->setVisible(!concentrationModeOn); m_bottomToolBar->setVisible(!concentrationModeOn); m_leftToolBar->setVisible(!concentrationModeOn); m_rightToolBar->setVisible(!concentrationModeOn); if (concentrationModeOn) { m_concentrateToolBar = new QToolBar(m_mainWindow); m_concentrateToolBar->setObjectName(QStringLiteral("concentrateToolBar")); m_concentrateToolBar->addAction(m_concentrationModeAction); QWidgetAction *action = new QWidgetAction(this); action->setDefaultWidget(m_mainWindow->menuBar()->cornerWidget(Qt::TopRightCorner)); m_concentrateToolBar->addAction(action); m_concentrateToolBar->setMovable(false); m_mainWindow->addToolBar(Qt::TopToolBarArea, m_concentrateToolBar); m_mainWindow->menuBar()->setCornerWidget(0, Qt::TopRightCorner); } else if (cornerWidget) { m_mainWindow->menuBar()->setCornerWidget(cornerWidget, Qt::TopRightCorner); cornerWidget->show(); } if (concentrationModeOn) { m_mainWindow->installEventFilter(this); } else { m_mainWindow->removeEventFilter(this); } } bool MainWindowPrivate::eventFilter(QObject* obj, QEvent* event) { Q_ASSERT(m_mainWindow == obj); if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) { Qt::KeyboardModifiers modifiers = static_cast(event)->modifiers(); m_mainWindow->menuBar()->setVisible(modifiers == Qt::AltModifier && event->type() == QEvent::KeyPress); } return false; } void MainWindowPrivate::showLeftDock(bool b) { idealController->showLeftDock(b); } void MainWindowPrivate::showBottomDock(bool b) { idealController->showBottomDock(b); } void MainWindowPrivate::showRightDock(bool b) { idealController->showRightDock(b); } void MainWindowPrivate::setBackgroundCentralWidget(QWidget* w) { delete bgCentralWidget; QLayout* l=m_mainWindow->centralWidget()->layout(); l->addWidget(w); bgCentralWidget=w; setBackgroundVisible(area->views().isEmpty()); } void MainWindowPrivate::setBackgroundVisible(bool v) { if(!bgCentralWidget) return; bgCentralWidget->setVisible(v); splitterCentralWidget->setVisible(!v); } void MainWindowPrivate::focusEditor() { if (View* view = m_mainWindow->activeView()) if (view->hasWidget()) view->widget()->setFocus(Qt::ShortcutFocusReason); } void MainWindowPrivate::toggleDocksShown() { idealController->toggleDocksShown(); } void MainWindowPrivate::selectNextDock() { idealController->goPrevNextDock(IdealController::NextDock); } void MainWindowPrivate::selectPreviousDock() { idealController->goPrevNextDock(IdealController::PrevDock); } Area::WalkerMode MainWindowPrivate::IdealToolViewCreator::operator() (View *view, Sublime::Position position) { if (!d->docks.contains(view)) { d->docks << view; //add view d->idealController->addView(d->positionToDockArea(position), view); } return Area::ContinueWalker; } Area::WalkerMode MainWindowPrivate::ViewCreator::operator() (AreaIndex *index) { QSplitter *splitter = d->m_indexSplitters.value(index); if (!splitter) { //no splitter - we shall create it and populate with views if (!index->parent()) { qCDebug(SUBLIME) << "reconstructing root area"; //this is root area splitter = d->splitterCentralWidget; d->m_indexSplitters[index] = splitter; d->centralWidget->layout()->addWidget(splitter); } else { if (!d->m_indexSplitters.value(index->parent())) { // can happen in working set code, as that adds a view to a child index first // hence, recursively reconstruct the parent indizes first operator()(index->parent()); } QSplitter *parent = d->m_indexSplitters.value(index->parent()); splitter = new QSplitter(parent); d->m_indexSplitters[index] = splitter; if(index == index->parent()->first()) parent->insertWidget(0, splitter); else parent->addWidget(splitter); } Q_ASSERT(splitter); } if (index->isSplit()) //this is a visible splitter splitter->setOrientation(index->orientation()); else { Container *container = 0; while(splitter->count() && qobject_cast(splitter->widget(0))) { // After unsplitting, we might have to remove old splitters QWidget* widget = splitter->widget(0); qCDebug(SUBLIME) << "deleting" << widget; widget->setParent(0); delete widget; } if (!splitter->widget(0)) { //we need to create view container container = new Container(splitter); connect(container, &Container::activateView, d->m_mainWindow, &MainWindow::activateViewAndFocus); connect(container, &Container::tabDoubleClicked, d->m_mainWindow, &MainWindow::tabDoubleClicked); connect(container, &Container::tabContextMenuRequested, d->m_mainWindow, &MainWindow::tabContextMenuRequested); connect(container, &Container::tabToolTipRequested, d->m_mainWindow, &MainWindow::tabToolTipRequested); connect(container, static_cast(&Container::requestClose), d, &MainWindowPrivate::widgetCloseRequest, Qt::QueuedConnection); connect(container, &Container::newTabRequested, d->m_mainWindow, &MainWindow::newTabRequested); splitter->addWidget(container); } else container = qobject_cast(splitter->widget(0)); container->show(); int position = 0; bool hadActiveView = false; Sublime::View* activeView = d->activeView; foreach (View *view, index->views()) { QWidget *widget = view->widget(container); if (widget) { if(!container->hasWidget(widget)) { container->addWidget(view, position); d->viewContainers[view] = container; d->widgetToView[widget] = view; } if(activeView == view) { hadActiveView = true; container->setCurrentWidget(widget); }else if(topViews.contains(view) && !hadActiveView) container->setCurrentWidget(widget); } position++; } } return Area::ContinueWalker; } void MainWindowPrivate::reconstructViews(QList topViews) { ViewCreator viewCreator(this, topViews); area->walkViews(viewCreator, area->rootIndex()); setBackgroundVisible(area->views().isEmpty()); } void MainWindowPrivate::reconstruct() { if(m_leftTabbarCornerWidget) { m_leftTabbarCornerWidget->hide(); m_leftTabbarCornerWidget->setParent(0); } - idealController->setWidthForArea(Qt::LeftDockWidgetArea, area->thickness(Sublime::Left)); - idealController->setWidthForArea(Qt::BottomDockWidgetArea, area->thickness(Sublime::Bottom)); - idealController->setWidthForArea(Qt::RightDockWidgetArea, area->thickness(Sublime::Right)); - IdealToolViewCreator toolViewCreator(this); area->walkToolViews(toolViewCreator, Sublime::AllPositions); reconstructViews(); m_mainWindow->blockSignals(true); qCDebug(SUBLIME) << "RECONSTRUCT" << area << area->shownToolViews(Sublime::Left); foreach (View *view, area->toolViews()) { QString id = view->document()->documentSpecifier(); if (!id.isEmpty()) { Sublime::Position pos = area->toolViewPosition(view); if (area->shownToolViews(pos).contains(id)) idealController->raiseView(view, IdealController::GroupWithOtherViews); } } m_mainWindow->blockSignals(false); setTabBarLeftCornerWidget(m_leftTabbarCornerWidget.data()); } void MainWindowPrivate::clearArea() { if(m_leftTabbarCornerWidget) m_leftTabbarCornerWidget->setParent(0); //reparent toolview widgets to 0 to prevent their deletion together with dockwidgets foreach (View *view, area->toolViews()) { // FIXME should we really delete here?? bool nonDestructive = true; idealController->removeView(view, nonDestructive); if (view->hasWidget()) view->widget()->setParent(0); } docks.clear(); //reparent all view widgets to 0 to prevent their deletion together with central //widget. this reparenting is necessary when switching areas inside the same mainwindow foreach (View *view, area->views()) { if (view->hasWidget()) view->widget()->setParent(0); } cleanCentralWidget(); m_mainWindow->setActiveView(0); m_indexSplitters.clear(); area = 0; viewContainers.clear(); setTabBarLeftCornerWidget(m_leftTabbarCornerWidget.data()); } void MainWindowPrivate::cleanCentralWidget() { while(splitterCentralWidget->count()) delete splitterCentralWidget->widget(0); setBackgroundVisible(true); } struct ShownToolViewFinder { ShownToolViewFinder() {} Area::WalkerMode operator()(View *v, Sublime::Position /*position*/) { if (v->hasWidget() && v->widget()->isVisible()) views << v; return Area::ContinueWalker; } QList views; }; void MainWindowPrivate::slotDockShown(Sublime::View* /*view*/, Sublime::Position pos, bool /*shown*/) { if (ignoreDockShown) return; ShownToolViewFinder finder; m_mainWindow->area()->walkToolViews(finder, pos); QStringList ids; foreach (View *v, finder.views) { ids << v->document()->documentSpecifier(); } area->setShownToolViews(pos, ids); } void MainWindowPrivate::viewRemovedInternal(AreaIndex* index, View* view) { Q_UNUSED(index); Q_UNUSED(view); setBackgroundVisible(area->views().isEmpty()); } void MainWindowPrivate::viewAdded(Sublime::AreaIndex *index, Sublime::View *view) { if(m_leftTabbarCornerWidget) { m_leftTabbarCornerWidget->hide(); m_leftTabbarCornerWidget->setParent(0); } // Remove container objects in the hierarchy from the parents, // because they are not needed anymore, and might lead to broken splitter hierarchy and crashes. for(Sublime::AreaIndex* current = index; current; current = current->parent()) { QSplitter *splitter = m_indexSplitters[current]; if (current->isSplit() && splitter) { // Also update the orientation splitter->setOrientation(current->orientation()); for(int w = 0; w < splitter->count(); ++w) { Container *container = qobject_cast(splitter->widget(w)); //we need to remove extra container before reconstruction //first reparent widgets in container so that they are not deleted if(container) { while (container->count()) { container->widget(0)->setParent(0); } //and then delete the container delete container; } } } } ViewCreator viewCreator(this); area->walkViews(viewCreator, index); emit m_mainWindow->viewAdded( view ); setTabBarLeftCornerWidget(m_leftTabbarCornerWidget.data()); setBackgroundVisible(false); } void Sublime::MainWindowPrivate::raiseToolView(Sublime::View * view) { idealController->raiseView(view); } void MainWindowPrivate::aboutToRemoveView(Sublime::AreaIndex *index, Sublime::View *view) { QSplitter *splitter = m_indexSplitters[index]; if (!splitter) return; qCDebug(SUBLIME) << "index " << index << " root " << area->rootIndex(); qCDebug(SUBLIME) << "splitter " << splitter << " container " << splitter->widget(0); qCDebug(SUBLIME) << "structure: " << index->print() << " whole structure: " << area->rootIndex()->print(); //find the container for the view and remove the widget Container *container = qobject_cast(splitter->widget(0)); if (!container) { qWarning() << "Splitter does not have a left widget!"; return; } emit m_mainWindow->aboutToRemoveView( view ); if (view->widget()) widgetToView.remove(view->widget()); viewContainers.remove(view); const bool wasActive = m_mainWindow->activeView() == view; if (container->count() > 1) { //container is not empty or this is a root index //just remove a widget if( view->widget() ) { container->removeWidget(view->widget()); view->widget()->setParent(0); //activate what is visible currently in the container if the removed view was active if (wasActive) return m_mainWindow->setActiveView(container->viewForWidget(container->currentWidget())); } } else { if(m_leftTabbarCornerWidget) { m_leftTabbarCornerWidget->hide(); m_leftTabbarCornerWidget->setParent(0); } // We've about to remove the last view of this container. It will // be empty, so have to delete it, as well. // If we have a container, then it should be the only child of // the splitter. Q_ASSERT(splitter->count() == 1); container->removeWidget(view->widget()); if (view->widget()) view->widget()->setParent(0); else qWarning() << "View does not have a widget!"; Q_ASSERT(container->count() == 0); // We can be called from signal handler of container // (which is tab widget), so defer deleting it. container->deleteLater(); container->setParent(0); /* If we're not at the top level, we get to collapse split views. */ if (index->parent()) { /* The splitter used to have container as the only child, now it's time to get rid of it. Make sure deleting splitter does not delete container -- per above comment, we'll delete it later. */ container->setParent(0); m_indexSplitters.remove(index); delete splitter; AreaIndex *parent = index->parent(); QSplitter *parentSplitter = m_indexSplitters[parent]; AreaIndex *sibling = parent->first() == index ? parent->second() : parent->first(); QSplitter *siblingSplitter = m_indexSplitters[sibling]; if(siblingSplitter) { HoldUpdates du(parentSplitter); //save sizes and orientation of the sibling splitter parentSplitter->setOrientation(siblingSplitter->orientation()); QList sizes = siblingSplitter->sizes(); /* Parent has two children -- 'index' that we've deleted and 'sibling'. We move all children of 'sibling' into parent, and delete 'sibling'. sibling either contains a single Container instance, or a bunch of further QSplitters. */ while (siblingSplitter->count() > 0) { //reparent contents into parent splitter QWidget *siblingWidget = siblingSplitter->widget(0); siblingWidget->setParent(parentSplitter); parentSplitter->addWidget(siblingWidget); } m_indexSplitters.remove(sibling); delete siblingSplitter; parentSplitter->setSizes(sizes); } qCDebug(SUBLIME) << "after deleation " << parent << " has " << parentSplitter->count() << " elements"; //find the container somewhere to activate Container *containerToActivate = parentSplitter->findChild(); //activate the current view there if (containerToActivate) { m_mainWindow->setActiveView(containerToActivate->viewForWidget(containerToActivate->currentWidget())); setTabBarLeftCornerWidget(m_leftTabbarCornerWidget.data()); return; } } } setTabBarLeftCornerWidget(m_leftTabbarCornerWidget.data()); if ( wasActive ) { m_mainWindow->setActiveView(0L); } } void MainWindowPrivate::toolViewAdded(Sublime::View* /*toolView*/, Sublime::Position position) { IdealToolViewCreator toolViewCreator(this); area->walkToolViews(toolViewCreator, position); } void MainWindowPrivate::aboutToRemoveToolView(Sublime::View *toolView, Sublime::Position /*position*/) { if (!docks.contains(toolView)) return; idealController->removeView(toolView); // TODO are Views unique? docks.removeAll(toolView); } void MainWindowPrivate::toolViewMoved( Sublime::View *toolView, Sublime::Position position) { if (!docks.contains(toolView)) return; idealController->moveView(toolView, positionToDockArea(position)); } Qt::DockWidgetArea MainWindowPrivate::positionToDockArea(Position position) { switch (position) { case Sublime::Left: return Qt::LeftDockWidgetArea; case Sublime::Right: return Qt::RightDockWidgetArea; case Sublime::Bottom: return Qt::BottomDockWidgetArea; case Sublime::Top: return Qt::TopDockWidgetArea; default: return Qt::LeftDockWidgetArea; } } void MainWindowPrivate::switchToArea(QAction *action) { qCDebug(SUBLIME) << "for" << action; controller->showArea(m_actionAreas.value(action), m_mainWindow); } void MainWindowPrivate::updateAreaSwitcher(Sublime::Area *area) { QAction* action = m_areaActions.value(area); if (action) action->setChecked(true); } void MainWindowPrivate::activateFirstVisibleView() { QList views = area->views(); if (views.count() > 0) m_mainWindow->activateView(views.first()); } void MainWindowPrivate::widgetResized(Qt::DockWidgetArea /*dockArea*/, int /*thickness*/) { //TODO: adymo: remove all thickness business } void MainWindowPrivate::widgetCloseRequest(QWidget* widget) { if (View *view = widgetToView.value(widget)) { area->closeView(view); } } void MainWindowPrivate::setTabBarLeftCornerWidget(QWidget* widget) { if(widget != m_leftTabbarCornerWidget.data()) { delete m_leftTabbarCornerWidget.data(); m_leftTabbarCornerWidget.clear(); } m_leftTabbarCornerWidget = widget; if(!widget || !area || viewContainers.isEmpty()) return; AreaIndex* putToIndex = area->rootIndex(); QSplitter* splitter = m_indexSplitters[putToIndex]; while(putToIndex->isSplit()) { putToIndex = putToIndex->first(); splitter = m_indexSplitters[putToIndex]; } // Q_ASSERT(splitter || putToIndex == area->rootIndex()); Container* c = 0; if(splitter) { c = qobject_cast(splitter->widget(0)); }else{ c = *viewContainers.constBegin(); } Q_ASSERT(c); c->setLeftCornerWidget(widget); } } #include "mainwindow_p.moc" #include "moc_mainwindow_p.cpp"