File indexing completed on 2024-05-19 08:29:17
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