File indexing completed on 2024-04-28 04:37:33

0001 /*
0002     SPDX-FileCopyrightText: 2006-2009 Alexander Dymo <adymo@kdevelop.org>
0003     SPDX-FileCopyrightText: 2012 Dominik Haumann <dhaumann@kde.org>
0004     SPDX-FileCopyrightText: 2020 Friedrich W. H. Kossebau <kossebau@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "mainwindow_p.h"
0010 
0011 #include <QLayout>
0012 #include <QSplitter>
0013 #include <QApplication>
0014 #include <QMenuBar>
0015 #include <QToolBar>
0016 #include <QKeyEvent>
0017 
0018 #include <KActionMenu>
0019 #include <KActionCollection>
0020 #include <KLocalizedString>
0021 
0022 #include "area.h"
0023 #include "view.h"
0024 #include "areaindex.h"
0025 #include "document.h"
0026 #include "container.h"
0027 #include "controller.h"
0028 #include "mainwindow.h"
0029 #include "viewbarcontainer.h"
0030 #include "idealcontroller.h"
0031 #include "holdupdates.h"
0032 #include "idealbuttonbarwidget.h"
0033 #include "message.h"
0034 #include "messagewidget.h"
0035 #include <debug.h>
0036 
0037 class IdealToolBar : public QToolBar
0038 {
0039     Q_OBJECT
0040     public:
0041         explicit IdealToolBar(const QString& title, bool hideWhenEmpty, Sublime::IdealButtonBarWidget* buttons, QMainWindow* parent)
0042             : QToolBar(title, parent)
0043             , m_buttons(buttons)
0044             , m_hideWhenEmpty(hideWhenEmpty)
0045         {
0046             setMovable(false);
0047             setFloatable(false);
0048             setObjectName(title);
0049             layout()->setContentsMargins(0, 0, 0, 0);
0050 
0051             addWidget(m_buttons);
0052 
0053             if (m_hideWhenEmpty) {
0054                 connect(m_buttons, &Sublime::IdealButtonBarWidget::emptyChanged,
0055                         this, &IdealToolBar::updateVisibilty);
0056             }
0057         }
0058 
0059     private Q_SLOTS:
0060         void updateVisibilty()
0061         {
0062             setVisible(!m_buttons->isEmpty());
0063         }
0064 
0065     private:
0066         Sublime::IdealButtonBarWidget* const m_buttons;
0067         const bool m_hideWhenEmpty;
0068 };
0069 
0070 namespace Sublime {
0071 
0072 MainWindowPrivate::MainWindowPrivate(MainWindow *w, Controller* controller)
0073 :controller(controller), area(nullptr), activeView(nullptr), activeToolView(nullptr), bgCentralWidget(nullptr),
0074  ignoreDockShown(false), autoAreaSettingsSave(false), m_mainWindow(w)
0075 {
0076     KActionCollection *ac = m_mainWindow->actionCollection();
0077 
0078     m_concentrationModeAction = new QAction(i18nc("@option:check", "Concentration Mode"), this);
0079     m_concentrationModeAction->setIcon(QIcon::fromTheme(QStringLiteral("page-zoom")));
0080     m_concentrationModeAction->setToolTip(i18nc("@info:tooltip", "Remove most of the controls so you can focus on what matters"));
0081     m_concentrationModeAction->setCheckable(true);
0082     m_concentrationModeAction->setChecked(false);
0083     ac->setDefaultShortcut(m_concentrationModeAction, Qt::META | Qt::Key_C);
0084     connect(m_concentrationModeAction, &QAction::toggled, this, &MainWindowPrivate::restoreConcentrationMode);
0085     ac->addAction(QStringLiteral("toggle_concentration_mode"), m_concentrationModeAction);
0086 
0087     auto* action = new QAction(i18nc("@option:check", "Show Left Dock"), this);
0088     action->setCheckable(true);
0089     ac->setDefaultShortcut(action, Qt::META | Qt::CTRL | Qt::Key_Left);
0090     connect(action, &QAction::toggled, this, &MainWindowPrivate::showLeftDock);
0091 
0092     ac->addAction(QStringLiteral("show_left_dock"), action);
0093 
0094     action = new QAction(i18nc("@option:check", "Show Right Dock"), this);
0095     action->setCheckable(true);
0096     ac->setDefaultShortcut(action, Qt::META | Qt::CTRL | Qt::Key_Right);
0097     connect(action, &QAction::toggled, this, &MainWindowPrivate::showRightDock);
0098     ac->addAction(QStringLiteral("show_right_dock"), action);
0099 
0100     action = new QAction(i18nc("@option:check", "Show Bottom Dock"), this);
0101     action->setCheckable(true);
0102     ac->setDefaultShortcut(action, Qt::META | Qt::CTRL | Qt::Key_Down);
0103     connect(action, &QAction::toggled, this, &MainWindowPrivate::showBottomDock);
0104     ac->addAction(QStringLiteral("show_bottom_dock"), action);
0105 
0106     action = new QAction(i18nc("@action", "Focus Editor"), this);
0107     ac->setDefaultShortcut(action, Qt::META | Qt::CTRL | Qt::Key_E);
0108     connect(action, &QAction::triggered, this, &MainWindowPrivate::focusEditor);
0109     ac->addAction(QStringLiteral("focus_editor"), action);
0110 
0111     action = new QAction(i18nc("@action", "Hide/Restore Docks"), this);
0112     ac->setDefaultShortcut(action, Qt::META | Qt::CTRL | Qt::Key_Up);
0113     connect(action, &QAction::triggered, this, &MainWindowPrivate::toggleDocksShown);
0114     ac->addAction(QStringLiteral("hide_all_docks"), action);
0115 
0116     action = new QAction(i18nc("@action", "Next Tool View"), this);
0117     ac->setDefaultShortcut(action, Qt::META | Qt::CTRL | Qt::Key_N);
0118     action->setIcon(QIcon::fromTheme(QStringLiteral("go-next")));
0119     connect(action, &QAction::triggered, this, &MainWindowPrivate::selectNextDock);
0120     ac->addAction(QStringLiteral("select_next_dock"), action);
0121 
0122     action = new QAction(i18nc("@action", "Previous Tool View"), this);
0123     ac->setDefaultShortcut(action, Qt::META | Qt::CTRL | Qt::Key_P);
0124     action->setIcon(QIcon::fromTheme(QStringLiteral("go-previous")));
0125     connect(action, &QAction::triggered, this, &MainWindowPrivate::selectPreviousDock);
0126     ac->addAction(QStringLiteral("select_previous_dock"), action);
0127 
0128     auto* const toolViewsMenu = new KActionMenu(i18nc("@title:menu", "Tool Views"), this);
0129     toolViewsMenu->setPopupMode(QToolButton::InstantPopup);
0130     ac->addAction(QStringLiteral("docks_submenu"), toolViewsMenu);
0131 
0132     idealController = new IdealController(m_mainWindow);
0133 
0134     m_leftToolBar = new IdealToolBar(i18n("Left Button Bar"), true, idealController->leftBarWidget, m_mainWindow);
0135     m_mainWindow->addToolBar(Qt::LeftToolBarArea, m_leftToolBar);
0136 
0137     m_rightToolBar = new IdealToolBar(i18n("Right Button Bar"), true, idealController->rightBarWidget, m_mainWindow);
0138     m_mainWindow->addToolBar(Qt::RightToolBarArea, m_rightToolBar);
0139 
0140     m_bottomToolBar = new IdealToolBar(i18n("Bottom Button Bar"), false, idealController->bottomBarWidget, m_mainWindow);
0141     m_mainWindow->addToolBar(Qt::BottomToolBarArea, m_bottomToolBar);
0142 
0143     // adymo: intentionally do not add a toolbar for top buttonbar
0144     // this doesn't work well with toolbars added via xmlgui
0145 
0146     centralWidget = new QWidget;
0147     centralWidget->setObjectName(QStringLiteral("centralWidget"));
0148     auto* layout = new QVBoxLayout(centralWidget);
0149     layout->setContentsMargins(0, 0, 0, 0);
0150     centralWidget->setLayout(layout);
0151 
0152     messageWidget = new MessageWidget();
0153     layout->addWidget(messageWidget);
0154 
0155     splitterCentralWidget = new QSplitter(centralWidget);
0156     // take as much space as possible
0157     splitterCentralWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
0158     layout->addWidget(splitterCentralWidget, 2);
0159 
0160     // this view bar container is used for the ktexteditor integration to show
0161     // all view bars at a central place, esp. for split view configurations
0162     viewBarContainer = new ViewBarContainer;
0163     viewBarContainer->setObjectName(QStringLiteral("viewBarContainer"));
0164     // hide by default
0165     viewBarContainer->setVisible(false);
0166     // only take as much as needed
0167     viewBarContainer->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
0168     layout->addWidget(viewBarContainer);
0169 
0170     m_mainWindow->setCentralWidget(centralWidget);
0171 
0172     connect(idealController,
0173             &IdealController::dockShown,
0174             this,
0175             &MainWindowPrivate::slotDockShown);
0176 
0177     connect(idealController, &IdealController::dockBarContextMenuRequested,
0178             m_mainWindow, &MainWindow::dockBarContextMenuRequested);
0179 }
0180 
0181 
0182 MainWindowPrivate::~MainWindowPrivate()
0183 {
0184     // create working copy as messages are auto-removing themselves from the hash on destruction
0185     const auto messages = m_messageHash.keys();
0186     qDeleteAll(messages);
0187 
0188     delete m_leftTabbarCornerWidget.data();
0189     m_leftTabbarCornerWidget.clear();
0190 }
0191 
0192 void MainWindowPrivate::disableConcentrationMode()
0193 {
0194     m_concentrationModeAction->setChecked(false);
0195     restoreConcentrationMode();
0196 }
0197 
0198 void MainWindowPrivate::restoreConcentrationMode()
0199 {
0200     const bool concentrationModeOn = m_concentrationModeAction->isChecked();
0201     QWidget* cornerWidget = nullptr;
0202     if (m_concentrateToolBar) {
0203         QLayout* l = m_concentrateToolBar->layout();
0204         QLayoutItem* li = l->takeAt(1); //ensure the cornerWidget isn't destroyed with the toolbar
0205         if (li) {
0206             cornerWidget = li->widget();
0207             delete li;
0208         }
0209 
0210         m_concentrateToolBar->deleteLater();
0211     }
0212 
0213     m_mainWindow->menuBar()->setVisible(!concentrationModeOn);
0214     m_bottomToolBar->setVisible(!concentrationModeOn);
0215     m_leftToolBar->setVisible(!concentrationModeOn);
0216     m_rightToolBar->setVisible(!concentrationModeOn);
0217 
0218     if (concentrationModeOn) {
0219         m_concentrateToolBar = new QToolBar(m_mainWindow);
0220         m_concentrateToolBar->setObjectName(QStringLiteral("concentrateToolBar"));
0221         m_concentrateToolBar->addAction(m_concentrationModeAction);
0222         m_concentrateToolBar->toggleViewAction()->setVisible(false);
0223         auto *action = new QWidgetAction(this);
0224 
0225         action->setDefaultWidget(m_mainWindow->menuBar()->cornerWidget(Qt::TopRightCorner));
0226         m_concentrateToolBar->addAction(action);
0227         m_concentrateToolBar->setMovable(false);
0228 
0229         m_mainWindow->addToolBar(Qt::TopToolBarArea, m_concentrateToolBar);
0230         m_mainWindow->menuBar()->setCornerWidget(nullptr, Qt::TopRightCorner);
0231     } else if (cornerWidget) {
0232         m_mainWindow->menuBar()->setCornerWidget(cornerWidget, Qt::TopRightCorner);
0233         cornerWidget->show();
0234     }
0235 
0236     if (concentrationModeOn) {
0237         m_mainWindow->installEventFilter(this);
0238     } else {
0239         m_mainWindow->removeEventFilter(this);
0240     }
0241 }
0242 
0243 bool MainWindowPrivate::eventFilter(QObject* obj, QEvent* event)
0244 {
0245     Q_ASSERT(m_mainWindow == obj);
0246     if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
0247         const auto ev = static_cast<QKeyEvent *>(event);
0248         Qt::KeyboardModifiers modifiers = ev->modifiers();
0249 
0250         //QLineEdit banned mostly so that alt navigation can be used from QuickOpen
0251         const bool visible = modifiers == Qt::AltModifier && ev->type() == QEvent::KeyPress && !qApp->focusWidget()->inherits("QLineEdit");
0252         m_mainWindow->menuBar()->setVisible(visible);
0253     }
0254 
0255     return false;
0256 }
0257 
0258 void MainWindowPrivate::showLeftDock(bool b)
0259 {
0260     idealController->showLeftDock(b);
0261 }
0262 
0263 void MainWindowPrivate::showBottomDock(bool b)
0264 {
0265     idealController->showBottomDock(b);
0266 }
0267 
0268 void MainWindowPrivate::showRightDock(bool b)
0269 {
0270     idealController->showRightDock(b);
0271 }
0272 
0273 void MainWindowPrivate::setBackgroundCentralWidget(QWidget* w)
0274 {
0275     delete bgCentralWidget;
0276 
0277     bgCentralWidget = w;
0278 
0279     if (bgCentralWidget) {
0280         auto* l = static_cast<QVBoxLayout*>(centralWidget->layout());
0281         l->addWidget(bgCentralWidget, 2);
0282         setBackgroundVisible(area->views().isEmpty());
0283     }
0284 }
0285 
0286 void MainWindowPrivate::setBackgroundVisible(bool v)
0287 {
0288     if(!bgCentralWidget)
0289         return;
0290 
0291     bgCentralWidget->setVisible(v);
0292     splitterCentralWidget->setVisible(!v);
0293 }
0294 
0295 void MainWindowPrivate::focusEditor()
0296 {
0297     if (View* view = m_mainWindow->activeView())
0298         if (view->hasWidget())
0299             view->widget()->setFocus(Qt::ShortcutFocusReason);
0300 }
0301 
0302 void MainWindowPrivate::toggleDocksShown()
0303 {
0304     idealController->toggleDocksShown();
0305 }
0306 
0307 void MainWindowPrivate::selectNextDock()
0308 {
0309     idealController->goPrevNextDock(IdealController::NextDock);
0310 }
0311 
0312 void MainWindowPrivate::selectPreviousDock()
0313 {
0314     idealController->goPrevNextDock(IdealController::PrevDock);
0315 }
0316 
0317 Area::WalkerMode MainWindowPrivate::IdealToolViewCreator::operator() (View *view, Sublime::Position position)
0318 {
0319     if (!d->docks.contains(view))
0320     {
0321         d->docks << view;
0322 
0323         //add view
0324         d->idealController->addView(d->positionToDockArea(position), view);
0325     }
0326     return Area::ContinueWalker;
0327 }
0328 
0329 Area::WalkerMode MainWindowPrivate::ViewCreator::operator() (AreaIndex *index)
0330 {
0331     QSplitter *splitter = d->m_indexSplitters.value(index);
0332     if (!splitter)
0333     {
0334         //no splitter - we shall create it and populate with views
0335         if (!index->parent())
0336         {
0337             qCDebug(SUBLIME) << "reconstructing root area";
0338             //this is root area
0339             splitter = d->splitterCentralWidget;
0340             d->m_indexSplitters[index] = splitter;
0341         }
0342         else
0343         {
0344             if (!d->m_indexSplitters.value(index->parent())) {
0345                 // can happen in working set code, as that adds a view to a child index first
0346                 // hence, recursively reconstruct the parent indices first
0347                 operator()(index->parent());
0348             }
0349             QSplitter *parent = d->m_indexSplitters.value(index->parent());
0350             splitter = new QSplitter(parent);
0351             d->m_indexSplitters[index] = splitter;
0352 
0353             if(index == index->parent()->first())
0354                 parent->insertWidget(0, splitter);
0355             else
0356                 parent->addWidget(splitter);
0357         }
0358         Q_ASSERT(splitter);
0359     }
0360 
0361     if (index->isSplit()) //this is a visible splitter
0362         splitter->setOrientation(index->orientation());
0363     else
0364     {
0365         Container *container = nullptr;
0366 
0367         while(splitter->count() && qobject_cast<QSplitter*>(splitter->widget(0)))
0368         {
0369             // After unsplitting, we might have to remove old splitters
0370             QWidget* widget = splitter->widget(0);
0371             qCDebug(SUBLIME) << "deleting" << widget;
0372             widget->setParent(nullptr);
0373             delete widget;
0374         }
0375 
0376         if (!splitter->widget(0))
0377         {
0378             //we need to create view container
0379             container = new Container(splitter);
0380             connect(container, &Container::activateView,
0381                     d->m_mainWindow, &MainWindow::activateViewAndFocus);
0382             connect(container, &Container::tabDoubleClicked,
0383                     d->m_mainWindow, &MainWindow::tabDoubleClicked);
0384             connect(container, &Container::tabContextMenuRequested,
0385                     d->m_mainWindow, &MainWindow::tabContextMenuRequested);
0386             connect(container, &Container::tabToolTipRequested,
0387                     d->m_mainWindow, &MainWindow::tabToolTipRequested);
0388             connect(container, QOverload<QWidget*>::of(&Container::requestClose),
0389                     d, &MainWindowPrivate::widgetCloseRequest, Qt::QueuedConnection);
0390             connect(container, &Container::newTabRequested,
0391                     d->m_mainWindow, &MainWindow::newTabRequested);
0392             splitter->addWidget(container);
0393         }
0394         else
0395             container = qobject_cast<Container*>(splitter->widget(0));
0396         container->show();
0397 
0398         int position = 0;
0399         bool hadActiveView = false;
0400         Sublime::View* activeView = d->activeView;
0401 
0402         for (View* view : qAsConst(index->views())) {
0403             QWidget *widget = view->widget(container);
0404 
0405             if (widget)
0406             {
0407                 if(!container->hasWidget(widget))
0408                 {
0409                     container->addWidget(view, position);
0410                     d->viewContainers[view] = container;
0411                     d->widgetToView[widget] = view;
0412                 }
0413                 if(activeView == view)
0414                 {
0415                     hadActiveView = true;
0416                     container->setCurrentWidget(widget);
0417                 }else if(topViews.contains(view) && !hadActiveView)
0418                     container->setCurrentWidget(widget);
0419             }
0420             position++;
0421         }
0422     }
0423     return Area::ContinueWalker;
0424 }
0425 
0426 void MainWindowPrivate::reconstructViews(const QList<View*>& topViews)
0427 {
0428     ViewCreator viewCreator(this, topViews);
0429     area->walkViews(viewCreator, area->rootIndex());
0430     setBackgroundVisible(area->views().isEmpty());
0431 }
0432 
0433 void MainWindowPrivate::reconstruct()
0434 {
0435     if(m_leftTabbarCornerWidget) {
0436         m_leftTabbarCornerWidget->hide();
0437         m_leftTabbarCornerWidget->setParent(nullptr);
0438     }
0439 
0440     IdealToolViewCreator toolViewCreator(this);
0441     area->walkToolViews(toolViewCreator, Sublime::AllPositions);
0442 
0443     reconstructViews();
0444 
0445     {
0446         QSignalBlocker blocker(m_mainWindow);
0447         qCDebug(SUBLIME) << "RECONSTRUCT" << area << area->shownToolViews(Sublime::Left);
0448         for (View* view : qAsConst(area->toolViews())) {
0449             QString id = view->document()->documentSpecifier();
0450             if (!id.isEmpty())
0451             {
0452                 Sublime::Position pos = area->toolViewPosition(view);
0453                 if (area->shownToolViews(pos).contains(id))
0454                     idealController->raiseView(view, IdealController::GroupWithOtherViews);
0455             }
0456         }
0457     }
0458 
0459     setTabBarLeftCornerWidget(m_leftTabbarCornerWidget.data());
0460 }
0461 
0462 void MainWindowPrivate::clearArea()
0463 {
0464     if(m_leftTabbarCornerWidget)
0465         m_leftTabbarCornerWidget->setParent(nullptr);
0466 
0467     //reparent tool view widgets to nullptr to prevent their deletion together with dockwidgets
0468     for (View* view : qAsConst(area->toolViews())) {
0469         // FIXME should we really delete here??
0470         bool nonDestructive = true;
0471         idealController->removeView(view, nonDestructive);
0472 
0473         if (view->hasWidget())
0474             view->widget()->setParent(nullptr);
0475     }
0476 
0477     docks.clear();
0478 
0479     //reparent all view widgets to 0 to prevent their deletion together with central
0480     //widget. this reparenting is necessary when switching areas inside the same mainwindow
0481     const auto views = area->views();
0482     for (View* view : views) {
0483         if (view->hasWidget())
0484             view->widget()->setParent(nullptr);
0485     }
0486     cleanCentralWidget();
0487     m_mainWindow->setActiveView(nullptr);
0488     m_indexSplitters.clear();
0489     area = nullptr;
0490     viewContainers.clear();
0491 
0492     setTabBarLeftCornerWidget(m_leftTabbarCornerWidget.data());
0493 }
0494 
0495 void MainWindowPrivate::cleanCentralWidget()
0496 {
0497     while(splitterCentralWidget->count())
0498         delete splitterCentralWidget->widget(0);
0499 
0500     setBackgroundVisible(true);
0501 }
0502 
0503 struct ShownToolViewFinder {
0504     ShownToolViewFinder() {}
0505     Area::WalkerMode operator()(View *v, Sublime::Position /*position*/)
0506     {
0507         if (v->hasWidget() && v->widget()->isVisible())
0508             views << v;
0509         return Area::ContinueWalker;
0510     }
0511     QList<View *> views;
0512 };
0513 
0514 void MainWindowPrivate::slotDockShown(Sublime::View* /*view*/, Sublime::Position pos, bool /*shown*/)
0515 {
0516     if (ignoreDockShown)
0517         return;
0518 
0519     ShownToolViewFinder finder;
0520     m_mainWindow->area()->walkToolViews(finder, pos);
0521 
0522     QStringList ids;
0523     ids.reserve(finder.views.size());
0524     for (View* v : qAsConst(finder.views)) {
0525         ids << v->document()->documentSpecifier();
0526     }
0527     area->setShownToolViews(pos, ids);
0528 }
0529 
0530 void MainWindowPrivate::viewRemovedInternal(AreaIndex* index, View* view)
0531 {
0532     Q_UNUSED(index);
0533     Q_UNUSED(view);
0534     setBackgroundVisible(area->views().isEmpty());
0535 }
0536 
0537 void MainWindowPrivate::viewAdded(Sublime::AreaIndex *index, Sublime::View *view)
0538 {
0539     if(m_leftTabbarCornerWidget) {
0540         m_leftTabbarCornerWidget->hide();
0541         m_leftTabbarCornerWidget->setParent(nullptr);
0542     }
0543 
0544     // Remove container objects in the hierarchy from the parents,
0545     // because they are not needed anymore, and might lead to broken splitter hierarchy and crashes.
0546     for(Sublime::AreaIndex* current = index; current; current = current->parent())
0547     {
0548         QSplitter *splitter = m_indexSplitters[current];
0549         if (current->isSplit() && splitter)
0550         {
0551             // Also update the orientation
0552             splitter->setOrientation(current->orientation());
0553 
0554             for(int w = 0; w < splitter->count(); ++w)
0555             {
0556                 auto *container = qobject_cast<Sublime::Container*>(splitter->widget(w));
0557                 //we need to remove extra container before reconstruction
0558                 //first reparent widgets in container so that they are not deleted
0559                 if(container)
0560                 {
0561                     while (container->count())
0562                     {
0563                         container->widget(0)->setParent(nullptr);
0564                     }
0565                     //and then delete the container
0566                     delete container;
0567                 }
0568             }
0569         }
0570     }
0571 
0572     ViewCreator viewCreator(this);
0573     area->walkViews(viewCreator, index);
0574     emit m_mainWindow->viewAdded( view );
0575 
0576     setTabBarLeftCornerWidget(m_leftTabbarCornerWidget.data());
0577 
0578     setBackgroundVisible(false);
0579 }
0580 
0581 void Sublime::MainWindowPrivate::raiseToolView(Sublime::View * view)
0582 {
0583     idealController->raiseView(view);
0584 }
0585 
0586 void MainWindowPrivate::aboutToRemoveView(Sublime::AreaIndex *index, Sublime::View *view)
0587 {
0588     QSplitter *splitter = m_indexSplitters[index];
0589     if (!splitter)
0590         return;
0591 
0592     qCDebug(SUBLIME) << "index " << index << " root " << area->rootIndex();
0593     qCDebug(SUBLIME) << "splitter " << splitter << " container " << splitter->widget(0);
0594     qCDebug(SUBLIME) << "structure: " << index->print() << " whole structure: " << area->rootIndex()->print();
0595     //find the container for the view and remove the widget
0596     auto *container = qobject_cast<Container*>(splitter->widget(0));
0597     if (!container) {
0598         qCWarning(SUBLIME) << "Splitter does not have a left widget!";
0599         return;
0600     }
0601 
0602     emit m_mainWindow->aboutToRemoveView( view );
0603 
0604     if (view->widget())
0605         widgetToView.remove(view->widget());
0606     viewContainers.remove(view);
0607 
0608     const bool wasActive = m_mainWindow->activeView() == view;
0609     if (container->count() > 1)
0610     {
0611         //container is not empty or this is a root index
0612         //just remove a widget
0613         if( view->widget() ) {
0614             container->removeWidget(view->widget());
0615             view->widget()->setParent(nullptr);
0616             //activate what is visible currently in the container if the removed view was active
0617             if (wasActive) {
0618                 m_mainWindow->setActiveView(container->viewForWidget(container->currentWidget()));
0619                 return;
0620             }
0621         }
0622     }
0623     else
0624     {
0625         if(m_leftTabbarCornerWidget) {
0626             m_leftTabbarCornerWidget->hide();
0627             m_leftTabbarCornerWidget->setParent(nullptr);
0628         }
0629 
0630         // We've about to remove the last view of this container.  It will
0631         // be empty, so have to delete it, as well.
0632 
0633         // If we have a container, then it should be the only child of
0634         // the splitter.
0635         Q_ASSERT(splitter->count() == 1);
0636         container->removeWidget(view->widget());
0637 
0638         if (view->widget())
0639             view->widget()->setParent(nullptr);
0640         else
0641             qCWarning(SUBLIME) << "View does not have a widget!";
0642 
0643         Q_ASSERT(container->count() == 0);
0644         // We can be called from signal handler of container
0645         // (which is tab widget), so defer deleting it.
0646         container->deleteLater();
0647         container->setParent(nullptr);
0648 
0649         /* If we're not at the top level, we get to collapse split views.  */
0650         if (index->parent())
0651         {
0652             /* The splitter used to have container as the only child, now it's
0653                time to get rid of it.  Make sure deleting splitter does not
0654                delete container -- per above comment, we'll delete it later.  */
0655             container->setParent(nullptr);
0656             m_indexSplitters.remove(index);
0657             delete splitter;
0658 
0659             AreaIndex *parent = index->parent();
0660             QSplitter *parentSplitter = m_indexSplitters[parent];
0661 
0662             AreaIndex *sibling = parent->first() == index ? parent->second() : parent->first();
0663             QSplitter *siblingSplitter = m_indexSplitters[sibling];
0664 
0665             if(siblingSplitter)
0666             {
0667                 HoldUpdates du(parentSplitter);
0668                 //save sizes and orientation of the sibling splitter
0669                 parentSplitter->setOrientation(siblingSplitter->orientation());
0670                 QList<int> sizes = siblingSplitter->sizes();
0671 
0672                 /* Parent has two children -- 'index' that we've deleted and
0673                 'sibling'.  We move all children of 'sibling' into parent,
0674                 and delete 'sibling'.  sibling either contains a single
0675                 Container instance, or a bunch of further QSplitters.  */
0676                 while (siblingSplitter->count() > 0)
0677                 {
0678                     //reparent contents into parent splitter
0679                     QWidget *siblingWidget = siblingSplitter->widget(0);
0680                     siblingWidget->setParent(parentSplitter);
0681                     parentSplitter->addWidget(siblingWidget);
0682                 }
0683 
0684                 m_indexSplitters.remove(sibling);
0685                 delete siblingSplitter;
0686                 parentSplitter->setSizes(sizes);
0687             }
0688 
0689             qCDebug(SUBLIME) << "after deletion " << parent << " has "
0690                          << parentSplitter->count() << " elements";
0691 
0692 
0693             //find the container somewhere to activate
0694             auto *containerToActivate = parentSplitter->findChild<Sublime::Container*>();
0695             //activate the current view there
0696             if (containerToActivate) {
0697                 m_mainWindow->setActiveView(containerToActivate->viewForWidget(containerToActivate->currentWidget()));
0698                 setTabBarLeftCornerWidget(m_leftTabbarCornerWidget.data());
0699                 return;
0700             }
0701         }
0702     }
0703 
0704     setTabBarLeftCornerWidget(m_leftTabbarCornerWidget.data());
0705     if ( wasActive ) {
0706         m_mainWindow->setActiveView(nullptr);
0707     }
0708 }
0709 
0710 void MainWindowPrivate::toolViewAdded(Sublime::View* /*toolView*/, Sublime::Position position)
0711 {
0712     IdealToolViewCreator toolViewCreator(this);
0713     area->walkToolViews(toolViewCreator, position);
0714 }
0715 
0716 void MainWindowPrivate::aboutToRemoveToolView(Sublime::View *toolView, Sublime::Position /*position*/)
0717 {
0718     if (!docks.contains(toolView))
0719         return;
0720 
0721     idealController->removeView(toolView);
0722     // TODO are Views unique?
0723     docks.removeAll(toolView);
0724 }
0725 
0726 void MainWindowPrivate::toolViewMoved(
0727     Sublime::View *toolView, Sublime::Position position)
0728 {
0729     if (!docks.contains(toolView))
0730         return;
0731 
0732     idealController->moveView(toolView, positionToDockArea(position));
0733 }
0734 
0735 Qt::DockWidgetArea MainWindowPrivate::positionToDockArea(Position position)
0736 {
0737     switch (position)
0738     {
0739         case Sublime::Left: return Qt::LeftDockWidgetArea;
0740         case Sublime::Right: return Qt::RightDockWidgetArea;
0741         case Sublime::Bottom: return Qt::BottomDockWidgetArea;
0742         case Sublime::Top: return Qt::TopDockWidgetArea;
0743         default: return Qt::LeftDockWidgetArea;
0744     }
0745 }
0746 
0747 void MainWindowPrivate::updateAreaSwitcher(Sublime::Area *area)
0748 {
0749     QAction* action = m_areaActions.value(area);
0750     if (action)
0751         action->setChecked(true);
0752 }
0753 
0754 void MainWindowPrivate::activateFirstVisibleView()
0755 {
0756     QList<Sublime::View*> views = area->views();
0757     if (views.count() > 0)
0758         m_mainWindow->activateView(views.first());
0759 }
0760 
0761 void MainWindowPrivate::widgetCloseRequest(QWidget* widget)
0762 {
0763     if (View *view = widgetToView.value(widget))
0764     {
0765         area->closeView(view);
0766     }
0767 }
0768 
0769 
0770 void MainWindowPrivate::setTabBarLeftCornerWidget(QWidget* widget)
0771 {
0772     if(widget != m_leftTabbarCornerWidget.data()) {
0773         delete m_leftTabbarCornerWidget.data();
0774         m_leftTabbarCornerWidget.clear();
0775     }
0776     m_leftTabbarCornerWidget = widget;
0777 
0778     if(!widget || !area || viewContainers.isEmpty())
0779         return;
0780 
0781     AreaIndex* putToIndex = area->rootIndex();
0782     QSplitter* splitter = m_indexSplitters[putToIndex];
0783     while(putToIndex->isSplit()) {
0784         putToIndex = putToIndex->first();
0785         splitter = m_indexSplitters[putToIndex];
0786     }
0787 
0788 //     Q_ASSERT(splitter || putToIndex == area->rootIndex());
0789 
0790     Container* c = nullptr;
0791     if(splitter) {
0792         c = qobject_cast<Container*>(splitter->widget(0));
0793     }else{
0794         c = *viewContainers.constBegin();
0795     }
0796     Q_ASSERT(c);
0797 
0798     c->setLeftCornerWidget(widget);
0799 }
0800 
0801 void MainWindowPrivate::postMessage(Message* message)
0802 {
0803     if (!message) {
0804         return;
0805     }
0806 
0807     message->setParent(this);
0808 
0809     // if there are no actions, add a close action by default if widget does not auto-hide
0810     if (message->actions().isEmpty() && message->autoHide() < 0) {
0811         auto* closeAction = new QAction(QIcon::fromTheme(QStringLiteral("window-close")),
0812                                         i18nc("@action", "Close"));
0813         closeAction->setToolTip(i18nc("@info:tooltip", "Close message"));
0814         message->addAction(closeAction);
0815     }
0816 
0817     // reparent actions, as we want full control over when they are deleted
0818     QVector<QSharedPointer<QAction>> managedMessageActions;
0819     const auto messageActions = message->actions();
0820     managedMessageActions.reserve(messageActions.size());
0821     for (QAction* action : messageActions) {
0822         action->setParent(nullptr);
0823         managedMessageActions.append(QSharedPointer<QAction>(action));
0824     }
0825     m_messageHash.insert(message, managedMessageActions);
0826 
0827     // also catch if the user manually calls delete message
0828     connect(message, &Message::closed, this, &MainWindowPrivate::messageDestroyed);
0829 
0830     messageWidget->postMessage(message, managedMessageActions);
0831 }
0832 
0833 void MainWindowPrivate::messageDestroyed(Message* message)
0834 {
0835     // Message is already in destructor
0836     Q_ASSERT(m_messageHash.contains(message));
0837     m_messageHash.remove(message);
0838 }
0839 
0840 }
0841 
0842 #include "mainwindow_p.moc"
0843 #include "moc_mainwindow_p.cpp"
0844