File indexing completed on 2024-04-28 05:49:31

0001 /*
0002     SPDX-FileCopyrightText: 2005 Christoph Cullmann <cullmann@kde.org>
0003     SPDX-FileCopyrightText: 2002, 2003 Joseph Wenninger <jowenn@kde.org>
0004 
0005     GUIClient partly based on ktoolbarhandler.cpp: SPDX-FileCopyrightText: 2002 Simon Hausmann <hausmann@kde.org>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include "katemdi.h"
0011 #include "kateapp.h"
0012 
0013 #include <KAcceleratorManager>
0014 #include <KActionCollection>
0015 #include <KActionMenu>
0016 #include <KConfigGroup>
0017 #include <KLocalizedString>
0018 #include <KMessageBox>
0019 #include <KSharedConfig>
0020 #include <KToolBar>
0021 #include <KWindowConfig>
0022 #include <KXMLGUIFactory>
0023 
0024 #include <QApplication>
0025 #include <QContextMenuEvent>
0026 #include <QDomDocument>
0027 #include <QDrag>
0028 #include <QHBoxLayout>
0029 #include <QLabel>
0030 #include <QMenu>
0031 #include <QMimeData>
0032 #include <QRubberBand>
0033 #include <QSizePolicy>
0034 #include <QStackedWidget>
0035 #include <QStyle>
0036 #include <QTimer>
0037 #include <QVBoxLayout>
0038 
0039 namespace KateMDI
0040 {
0041 // BEGIN TOGGLETOOLVIEWACTION
0042 //
0043 ToggleToolViewAction::ToggleToolViewAction(const QString &text, ToolView *tv, QObject *parent)
0044     : KToggleAction(text, parent)
0045     , m_tv(tv)
0046 {
0047     connect(this, &ToggleToolViewAction::toggled, this, &ToggleToolViewAction::slotToggled);
0048     connect(m_tv, &ToolView::toolVisibleChanged, this, &ToggleToolViewAction::toolVisibleChanged);
0049 
0050     setChecked(m_tv->toolVisible());
0051 }
0052 
0053 void ToggleToolViewAction::toolVisibleChanged(bool v)
0054 {
0055     if (isChecked() != v) {
0056         setChecked(v);
0057     }
0058 }
0059 
0060 void ToggleToolViewAction::slotToggled(bool t)
0061 {
0062     if (m_tv->toolVisible() == t) {
0063         return;
0064     }
0065 
0066     if (t) {
0067         m_tv->mainWindow()->showToolView(m_tv);
0068         m_tv->setFocus();
0069     } else {
0070         m_tv->mainWindow()->hideToolView(m_tv);
0071     }
0072 }
0073 
0074 // END TOGGLETOOLVIEWACTION
0075 
0076 // BEGIN GUICLIENT
0077 
0078 static const QString actionListName = QStringLiteral("kate_mdi_view_actions");
0079 
0080 GUIClient::GUIClient(MainWindow *mw)
0081     : QObject(mw)
0082     , KXMLGUIClient(mw)
0083     , m_mw(mw)
0084 {
0085     setComponentName(QStringLiteral("toolviewmanager"), i18n("Toolview Manager"));
0086     connect(m_mw->guiFactory(), &KXMLGUIFactory::clientAdded, this, &GUIClient::clientAdded);
0087     const QString guiDescription = QStringLiteral(
0088         ""
0089         "<!DOCTYPE gui><gui name=\"kate_mdi_view_actions\">"
0090         "<MenuBar>"
0091         "    <Menu name=\"view\">"
0092         "        <ActionList name=\"%1\" />"
0093         "    </Menu>"
0094         "</MenuBar>"
0095         "</gui>");
0096 
0097     if (domDocument().documentElement().isNull()) {
0098         QString completeDescription = guiDescription.arg(actionListName);
0099 
0100         setXML(completeDescription, false /*merge*/);
0101     }
0102 
0103     m_sidebarButtonsMenu = new KActionMenu(i18n("Sidebar Buttons"), this);
0104     actionCollection()->addAction(QStringLiteral("kate_mdi_show_sidebar_buttons"), m_sidebarButtonsMenu);
0105 
0106     m_focusToolviewMenu = new KActionMenu(i18n("Focus Toolview"), this);
0107     actionCollection()->addAction(QStringLiteral("kate_mdi_focus_toolview"), m_focusToolviewMenu);
0108 
0109     m_toolMenu = new KActionMenu(i18n("Tool &Views"), this);
0110     actionCollection()->addAction(QStringLiteral("kate_mdi_toolview_menu"), m_toolMenu);
0111     m_showSidebarsAction = new KToggleAction(i18n("Show Side&bars"), this);
0112     actionCollection()->addAction(QStringLiteral("kate_mdi_sidebar_visibility"), m_showSidebarsAction);
0113     actionCollection()->setDefaultShortcut(m_showSidebarsAction, Qt::CTRL | Qt::ALT | Qt::SHIFT | Qt::Key_F);
0114 
0115     m_showSidebarsAction->setChecked(m_mw->sidebarsVisible());
0116     connect(m_showSidebarsAction, &KToggleAction::toggled, m_mw, &MainWindow::setSidebarsVisible);
0117 
0118     m_hideToolViews = actionCollection()->addAction(QStringLiteral("kate_mdi_hide_toolviews"), m_mw, &MainWindow::hideToolViews);
0119     m_hideToolViews->setText(i18n("Hide All Tool Views"));
0120 
0121     m_toolMenu->addAction(m_showSidebarsAction);
0122     m_toolMenu->addAction(m_hideToolViews);
0123     QAction *sep_act = new QAction(this);
0124     sep_act->setSeparator(true);
0125     m_toolMenu->addAction(sep_act);
0126 
0127     // read shortcuts
0128     actionCollection()->setConfigGroup(QStringLiteral("Shortcuts"));
0129     actionCollection()->readSettings();
0130 
0131     actionCollection()->addAssociatedWidget(m_mw);
0132     const auto actions = actionCollection()->actions();
0133     for (QAction *action : actions) {
0134         action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
0135     }
0136 
0137     // hide tool views menu for KWrite mode
0138     if (KateApp::isKWrite()) {
0139         m_sidebarButtonsMenu->setVisible(false);
0140         m_focusToolviewMenu->setVisible(false);
0141         m_toolMenu->setVisible(false);
0142     }
0143 }
0144 
0145 void GUIClient::updateSidebarsVisibleAction()
0146 {
0147     m_showSidebarsAction->setChecked(m_mw->sidebarsVisible());
0148 }
0149 
0150 void GUIClient::registerToolView(ToolView *tv)
0151 {
0152     QString aname = QLatin1String("kate_mdi_toolview_") + tv->id;
0153 
0154     // try to read the action shortcut
0155 
0156     auto shortcutsForActionName = [](const QString &aname) {
0157         QList<QKeySequence> shortcuts;
0158         KSharedConfigPtr cfg = KSharedConfig::openConfig();
0159         const QString shortcutString = cfg->group(QStringLiteral("Shortcuts")).readEntry(aname, QString());
0160         const auto shortcutStrings = shortcutString.split(QLatin1Char(';'));
0161         for (const QString &shortcut : shortcutStrings) {
0162             shortcuts << QKeySequence::fromString(shortcut);
0163         }
0164         return shortcuts;
0165     };
0166 
0167     /** Show ToolView Action **/
0168     KToggleAction *a = new ToggleToolViewAction(i18n("Show %1", tv->text), tv, this);
0169     actionCollection()->setDefaultShortcuts(a, shortcutsForActionName(aname));
0170     actionCollection()->addAction(aname, a);
0171 
0172     m_toolMenu->addAction(a);
0173 
0174     auto &actionsForTool = m_toolToActions[tv];
0175     actionsForTool.push_back(a);
0176 
0177     /** Show Tab button in sidebar action **/
0178     aname = QStringLiteral("kate_mdi_show_toolview_button_") + tv->id;
0179     a = new KToggleAction(i18n("Show %1 Button", tv->text), this);
0180     a->setChecked(true);
0181     actionCollection()->setDefaultShortcuts(a, shortcutsForActionName(aname));
0182     actionCollection()->addAction(aname, a);
0183     connect(a, &KToggleAction::toggled, this, [toolview = QPointer<ToolView>(tv)](bool checked) {
0184         if (toolview) {
0185             const QSignalBlocker b(toolview);
0186             toolview->sidebar()->showToolviewTab(toolview, checked);
0187         }
0188     });
0189     connect(tv, &ToolView::tabButtonVisibleChanged, a, &QAction::setChecked);
0190 
0191     m_sidebarButtonsMenu->addAction(a);
0192     actionsForTool.push_back(a);
0193 
0194     aname = QStringLiteral("kate_mdi_focus_toolview_") + tv->id;
0195     QAction *act = new QAction(i18n("Focus %1", tv->text), this);
0196     actionCollection()->setDefaultShortcuts(act, shortcutsForActionName(aname));
0197     actionCollection()->addAction(aname, act);
0198     connect(act, &QAction::triggered, tv, [tv = QPointer(tv)] {
0199         if (tv && tv->mainWindow()) {
0200             if (!tv->isVisible()) {
0201                 tv->mainWindow()->showToolView(tv);
0202             }
0203             tv->setFocus();
0204         }
0205     });
0206     m_focusToolviewMenu->addAction(act);
0207     actionsForTool.push_back(act);
0208 
0209     updateActions();
0210 }
0211 
0212 void GUIClient::unregisterToolView(ToolView *tv)
0213 {
0214     auto &actionsForTool = m_toolToActions[tv];
0215     if (actionsForTool.empty())
0216         return;
0217 
0218     for (auto *a : actionsForTool) {
0219         delete a;
0220     }
0221     m_toolToActions.erase(tv);
0222 
0223     updateActions();
0224 }
0225 
0226 void GUIClient::clientAdded(KXMLGUIClient *client)
0227 {
0228     if (client == this) {
0229         updateActions();
0230     }
0231 }
0232 
0233 void GUIClient::updateActions()
0234 {
0235     if (!factory()) {
0236         return;
0237     }
0238 
0239     unplugActionList(actionListName);
0240 
0241     QList<QAction *> addList;
0242     addList.append(m_toolMenu);
0243     addList.append(m_sidebarButtonsMenu);
0244     addList.append(m_focusToolviewMenu);
0245 
0246     plugActionList(actionListName, addList);
0247 }
0248 
0249 // END GUICLIENT
0250 
0251 // BEGIN TOOLVIEW
0252 
0253 ToolView::ToolView(MainWindow *mainwin, Sidebar *sidebar, QWidget *parent, const QString &identifier)
0254     : QFrame(parent)
0255     , m_mainWin(mainwin)
0256     , m_sidebar(sidebar)
0257     , m_toolbar(nullptr)
0258     , id(identifier)
0259     , m_toolVisible(false)
0260 {
0261     // try to fix resize policy
0262     QSizePolicy policy(QSizePolicy::Preferred, QSizePolicy::Preferred);
0263     policy.setRetainSizeWhenHidden(true);
0264     setSizePolicy(policy);
0265 
0266     // per default vbox layout
0267     QVBoxLayout *layout = new QVBoxLayout(this);
0268     layout->setContentsMargins(0, 0, 0, 0);
0269     layout->setSpacing(0);
0270     setLayout(layout);
0271 
0272     // toolbar to collect actions
0273     m_toolbar = new KToolBar(this);
0274     m_toolbar->setVisible(false);
0275     m_toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly);
0276 
0277     // ensure reasonable icons sizes, like e.g. the quick-open and co. icons
0278     // the normal toolbar sizes are TOO large, e.g. for scaled stuff even more!
0279     const int iconSize = style()->pixelMetric(QStyle::PM_ButtonIconSize, nullptr, this);
0280     m_toolbar->setIconSize(QSize(iconSize, iconSize));
0281 }
0282 
0283 QSize ToolView::sizeHint() const
0284 {
0285     return size();
0286 }
0287 
0288 QSize ToolView::minimumSizeHint() const
0289 {
0290     return QSize(160, 160);
0291 }
0292 
0293 bool ToolView::tabButtonVisible() const
0294 {
0295     return isTabButtonVisible;
0296 }
0297 
0298 void ToolView::setTabButtonVisible(bool visible)
0299 {
0300     isTabButtonVisible = visible;
0301 }
0302 
0303 ToolView::~ToolView()
0304 {
0305     m_mainWin->toolViewDeleted(this);
0306 }
0307 
0308 void ToolView::setToolVisible(bool vis)
0309 {
0310     if (m_toolVisible == vis) {
0311         return;
0312     }
0313 
0314     m_toolVisible = vis;
0315     Q_EMIT toolVisibleChanged(m_toolVisible);
0316 }
0317 
0318 bool ToolView::toolVisible() const
0319 {
0320     return m_toolVisible;
0321 }
0322 
0323 void ToolView::childEvent(QChildEvent *ev)
0324 {
0325     // set the widget to be focus proxy if possible
0326     if (ev->type() == QEvent::ChildAdded) {
0327         if (QWidget *widget = qobject_cast<QWidget *>(ev->child())) {
0328             setFocusProxy(widget);
0329             layout()->addWidget(widget);
0330         }
0331     }
0332 
0333     QFrame::childEvent(ev);
0334 }
0335 
0336 void ToolView::actionEvent(QActionEvent *event)
0337 {
0338     QFrame::actionEvent(event);
0339     if (event->type() == QEvent::ActionAdded) {
0340         m_toolbar->addAction(event->action());
0341     } else if (event->type() == QEvent::ActionRemoved) {
0342         m_toolbar->removeAction(event->action());
0343     }
0344     m_toolbar->setVisible(!m_toolbar->actions().isEmpty());
0345 }
0346 
0347 // END TOOLVIEW
0348 
0349 // BEGIN SIDEBAR
0350 
0351 MultiTabBar::MultiTabBar(KMultiTabBar::KMultiTabBarPosition pos, Sidebar *sb, int idx)
0352     : m_sb(sb)
0353     , m_stack(new QStackedWidget())
0354     , m_multiTabBar(new KMultiTabBar(pos, this))
0355 {
0356     setProperty("is-multi-tabbar", true);
0357     QVBoxLayout *layout = new QVBoxLayout(this);
0358     layout->setContentsMargins(0, 0, 0, 0);
0359     layout->setSpacing(0);
0360     layout->addWidget(m_multiTabBar);
0361 
0362     m_sb->m_ownSplit->insertWidget(idx, m_stack);
0363     m_stack->hide();
0364 }
0365 
0366 MultiTabBar::~MultiTabBar()
0367 {
0368     // Don't forget to remove our stack from the splitter
0369     m_stack->deleteLater();
0370 }
0371 
0372 KMultiTabBarTab *MultiTabBar::addTab(int id, ToolView *tv)
0373 {
0374     m_stack->addWidget(tv);
0375     KMultiTabBarTab *newTab;
0376 
0377     if (std::find(m_tabList.begin(), m_tabList.end(), id) != m_tabList.end()) {
0378         // We are in session restore
0379         newTab = m_multiTabBar->tab(id);
0380         newTab->setIcon(tv->icon);
0381         newTab->setText(tv->text);
0382     } else {
0383         m_tabList.push_back(id);
0384         m_multiTabBar->appendTab(tv->icon, id, tv->text);
0385         newTab = m_multiTabBar->tab(id);
0386     }
0387 
0388     connect(newTab, &KMultiTabBarTab::clicked, this, &MultiTabBar::tabClicked);
0389 
0390     m_sb->m_widgetToTabBar.emplace(tv, this);
0391 
0392     return newTab;
0393 }
0394 
0395 int MultiTabBar::addBlankTab()
0396 {
0397     int id = m_sb->nextId();
0398     m_tabList.push_back(id);
0399     m_multiTabBar->appendTab(QIcon(), id, QStringLiteral("placeholder"));
0400     return id;
0401 }
0402 
0403 void MultiTabBar::tabClicked(int id)
0404 {
0405     if (m_multiTabBar->isTabRaised(id) || m_sb->isCollapsed()) {
0406         showToolView(id);
0407     } else {
0408         hideToolView(id);
0409     }
0410 
0411     m_sb->updateSidebar();
0412 }
0413 
0414 void MultiTabBar::removeBlankTab(int id)
0415 {
0416     m_tabList.erase(std::remove(m_tabList.begin(), m_tabList.end(), id), m_tabList.end());
0417     m_multiTabBar->removeTab(id);
0418     if (tabCount() == 0) {
0419         m_activeTab = 0;
0420         Q_EMIT lastTabRemoved(this);
0421     }
0422 }
0423 
0424 void MultiTabBar::removeTab(int id)
0425 {
0426     m_tabList.erase(std::remove(m_tabList.begin(), m_tabList.end(), id), m_tabList.end());
0427     m_multiTabBar->removeTab(id);
0428     ToolView *tv = m_sb->m_idToWidget.at(id);
0429     m_sb->m_widgetToTabBar.erase(tv);
0430 
0431     bool hideView = (m_stack->currentWidget() == tv);
0432     m_stack->removeWidget(tv);
0433 
0434     if (tabCount() == 0) {
0435         m_activeTab = 0; // Without any tab left, there is no one active
0436         Q_EMIT lastTabRemoved(this);
0437         return;
0438     }
0439 
0440     if (!hideView) {
0441         // Some other than the active is removed, no more to do
0442         return;
0443     }
0444 
0445     m_activeTab = 0; // Ensure we are up to date, reporting nonsense is dangerous
0446     tv = static_cast<ToolView *>(m_stack->currentWidget());
0447     if (tv) {
0448         hideToolView(m_sb->m_widgetToId.at(tv));
0449     }
0450 }
0451 
0452 void MultiTabBar::reorderTab(int id, KMultiTabBarTab *before)
0453 {
0454     // can't find source id?
0455     auto it = std::find(m_tabList.begin(), m_tabList.end(), id);
0456     if (it == m_tabList.end()) {
0457         return;
0458     }
0459     int idIdx = std::distance(m_tabList.begin(), it);
0460     it = before ? std::find(m_tabList.begin(), m_tabList.end(), before->id()) : m_tabList.end();
0461     if (before && it == m_tabList.end()) {
0462         // can't find before id?
0463         return;
0464     }
0465     // before == null, idx will be the last idx
0466     int beforeIdx = it == m_tabList.end() ? m_tabList.size() - 1 : std::distance(m_tabList.begin(), it);
0467     if (idIdx == beforeIdx) {
0468         return;
0469     }
0470 
0471     int start = std::min(idIdx, beforeIdx);
0472 
0473     // copy because after removeTab `before` will be invalid
0474     const int beforeId = before ? before->id() : -1;
0475 
0476     // Remove the actual tabs
0477     for (size_t i = start; i < m_tabList.size(); ++i) {
0478         auto oldTab = m_multiTabBar->tab(m_tabList[i]);
0479         oldTab->removeEventFilter(m_sb);
0480         m_multiTabBar->removeTab(m_tabList[i]);
0481     }
0482 
0483     // reorder the tab list
0484     // erase old position
0485     m_tabList.erase(std::remove(m_tabList.begin(), m_tabList.end(), id), m_tabList.end());
0486     // find new position and insert
0487     it = before ? std::find(m_tabList.begin(), m_tabList.end(), beforeId) : m_tabList.end();
0488     m_tabList.insert(it, id);
0489 
0490     // re-add tabs
0491     for (size_t i = start; i < m_tabList.size(); ++i) {
0492         auto tabId = m_tabList[i];
0493         ToolView *tv = m_sb->m_idToWidget.at(tabId);
0494         m_multiTabBar->appendTab(tv->icon, tabId, tv->text);
0495         m_sb->appendStyledTab(tabId, this, tv);
0496     }
0497 }
0498 
0499 void MultiTabBar::showToolView(int id)
0500 {
0501     setTabActive(id, true);
0502     expandToolView();
0503 }
0504 
0505 void MultiTabBar::hideToolView(int id)
0506 {
0507     setTabActive(id, false);
0508     collapseToolView();
0509 }
0510 
0511 void MultiTabBar::setTabActive(int id, bool state)
0512 {
0513     // Only change m_activeTab when state is true
0514 
0515     if (m_activeTab == id) {
0516         // Well, normally should be state always==false, but who knows...
0517         m_multiTabBar->setTab(id, state);
0518         m_sb->m_idToWidget.at(id)->setToolVisible(state);
0519         m_activeTab = state ? id : 0;
0520         return;
0521     }
0522 
0523     if (m_activeTab && state) {
0524         // Obviously the active tool is changed, disable the old one
0525         m_multiTabBar->setTab(m_activeTab, false);
0526         m_sb->m_idToWidget.at(m_activeTab)->setToolVisible(false);
0527     }
0528 
0529     m_multiTabBar->setTab(id, state);
0530     m_sb->m_idToWidget.at(id)->setToolVisible(state);
0531     m_activeTab = state ? id : m_activeTab;
0532 }
0533 
0534 bool MultiTabBar::isToolActive() const
0535 {
0536     return m_activeTab > 0;
0537 }
0538 
0539 void MultiTabBar::collapseToolView() const
0540 {
0541     m_stack->hide();
0542 
0543     if (m_stack->count() < 1) {
0544         return;
0545     }
0546 
0547     static_cast<ToolView *>(m_stack->currentWidget())->setToolVisible(false);
0548 }
0549 
0550 bool MultiTabBar::expandToolView() const
0551 {
0552     if (!m_activeTab) {
0553         return false;
0554     }
0555 
0556     if (m_stack->count() < 1) {
0557         return false;
0558     }
0559 
0560     ToolView *tv = m_sb->m_idToWidget.at(m_activeTab);
0561     tv->setToolVisible(true);
0562     tv->setFocus(); // This is for some tools nice, for some other not
0563     m_stack->setCurrentWidget(tv);
0564     m_stack->show();
0565 
0566     return true;
0567 }
0568 
0569 Sidebar::Sidebar(KMultiTabBar::KMultiTabBarPosition pos, QSplitter *sp, MainWindow *mainwin, QWidget *parent)
0570     : QSplitter(parent)
0571     , m_mainWin(mainwin)
0572     , m_tabBarPosition(pos)
0573     , m_splitter(sp)
0574     , m_ownSplit(new QSplitter(sp))
0575     , m_ownSplitIndex(sp->indexOf(m_ownSplit))
0576     , m_lastSize(200) // Default used when no session to restore is around
0577     , m_dropIndicator(new QRubberBand(QRubberBand::Rectangle, mainwin))
0578     , m_internalDropIndicator(new QRubberBand(QRubberBand::Rectangle, mainwin))
0579 {
0580     setChildrenCollapsible(false);
0581     setAcceptDrops(true);
0582     connect(this, &Sidebar::destroyed, m_dropIndicator, &QObject::deleteLater);
0583 
0584     if (isVertical()) {
0585         m_ownSplit->setOrientation(Qt::Vertical);
0586         setOrientation(Qt::Vertical);
0587         setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
0588     } else {
0589         m_ownSplit->setOrientation(Qt::Horizontal);
0590         setOrientation(Qt::Horizontal);
0591         setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
0592     }
0593 
0594     m_ownSplit->setChildrenCollapsible(false);
0595 
0596     // ensure proper sidebar state, see resizing issues in bug 460160
0597     m_ownSplit->hide();
0598 
0599     connect(this, &QSplitter::splitterMoved, this, &Sidebar::barSplitMoved);
0600     connect(m_ownSplit, &QSplitter::splitterMoved, this, &Sidebar::ownSplitMoved);
0601     connect(m_splitter, &QSplitter::splitterMoved, this, &Sidebar::handleCollapse);
0602 
0603     insertTabBar();
0604 
0605     // handle config changes & apply initial config
0606     connect(KateApp::self(), &KateApp::configurationChanged, this, &Sidebar::readConfig);
0607     readConfig();
0608 }
0609 
0610 void Sidebar::readConfig()
0611 {
0612     bool needsUpdate = false;
0613 
0614     KSharedConfig::Ptr config = KSharedConfig::openConfig();
0615     KConfigGroup cgGeneral = KConfigGroup(config, QStringLiteral("General"));
0616     const bool syncWithTabs = cgGeneral.readEntry("Sync section size with tab positions", false);
0617     if (syncWithTabs != m_syncWithTabs) {
0618         m_syncWithTabs = syncWithTabs;
0619         needsUpdate = true;
0620     }
0621     // Ignore the option for the bottom bar! Due to it's special design would that never looks good
0622     if (position() == KMultiTabBar::Bottom) {
0623         m_syncWithTabs = false;
0624         needsUpdate = false;
0625     }
0626     if (m_syncWithTabs && needsUpdate) {
0627         // Give the user an immediate feedback that the option has an effect, works not perfect
0628         // when some section is not active, but may better that to do nothing
0629         QList<int> wsizes = m_ownSplit->sizes();
0630         for (int i = 0; i < wsizes.count(); ++i) {
0631             if (wsizes.at(i) == 0) {
0632                 wsizes[i] = tabBar(i)->sectionSize();
0633             }
0634         }
0635         setSizes(wsizes);
0636         adjustSplitterSections();
0637         needsUpdate = false;
0638     }
0639 
0640     // shall we show text for the left and right bars?
0641     const bool showTextForLeftRight = cgGeneral.readEntry("Show text for left and right sidebar", false);
0642     if (showTextForLeftRight != m_showTextForLeftRight) {
0643         m_showTextForLeftRight = showTextForLeftRight;
0644         needsUpdate = true;
0645     }
0646 
0647     int size = cgGeneral.readEntry("Icon size for left and right sidebar buttons", 32);
0648     if (size != m_leftRightSidebarIconSize) {
0649         m_leftRightSidebarIconSize = size;
0650         needsUpdate = true;
0651     }
0652 
0653     if (!needsUpdate) {
0654         return;
0655     }
0656 
0657     for (const auto &[id, wid] : m_idToWidget) {
0658         updateButtonStyle(kmTabBar(wid)->tab(id));
0659     }
0660 }
0661 
0662 void Sidebar::appendStyledTab(int id, MultiTabBar *bar, ToolView *widget)
0663 {
0664     auto newTab = bar->addTab(id, widget);
0665 
0666     Q_ASSERT(newTab);
0667     newTab->installEventFilter(this);
0668 
0669     // remember original text, we will need it again if we update the style in updateButtonStyle
0670     newTab->setProperty("kate_original_text", widget->text);
0671 
0672     // fixup styling
0673     updateButtonStyle(newTab);
0674 
0675     // tell that we have a new tab, useful for e.g. overlays
0676     // do it delayed so that if toolview was constructed before the container widget
0677     // and the container wants to react to this signal, it can do so. Otherwise, this
0678     // will fire before the container has connected to this signal
0679     QMetaObject::invokeMethod(
0680         m_mainWin,
0681         [w = m_mainWin, widget, newTab] {
0682             Q_EMIT w->tabForToolViewAdded(widget, newTab);
0683         },
0684         Qt::QueuedConnection);
0685 }
0686 
0687 void Sidebar::updateButtonStyle(KMultiTabBarTab *button)
0688 {
0689     const auto originalText = button->property("kate_original_text").toString();
0690     if (!m_showTextForLeftRight && (position() == KMultiTabBar::Left || position() == KMultiTabBar::Right)) {
0691         const int iconSize = m_leftRightSidebarIconSize;
0692         button->setIconSize(QSize(iconSize, iconSize));
0693         button->setText(QString());
0694         button->setToolTip(originalText);
0695     } else {
0696         const int iconSize = style()->pixelMetric(QStyle::PM_ButtonIconSize, nullptr, this);
0697         button->setIconSize(QSize(iconSize, iconSize));
0698         button->setText(originalText);
0699         button->setToolTip(QString());
0700     }
0701 }
0702 
0703 void Sidebar::setStyle(KMultiTabBar::KMultiTabBarStyle style)
0704 {
0705     m_tabBarStyle = style;
0706 
0707     for (int i = 0; i < tabBarCount(); ++i) {
0708         tabBar(i)->tabBar()->setStyle(style);
0709     }
0710 }
0711 
0712 QSize Sidebar::sizeHint() const
0713 {
0714     return minimumSizeHint();
0715 }
0716 
0717 QSize Sidebar::minimumSizeHint() const
0718 {
0719     return isVisible() ? QSplitter::minimumSizeHint() : QSize{0, 0};
0720 }
0721 
0722 MultiTabBar *Sidebar::insertTabBar(int idx /* = -1*/)
0723 {
0724     auto *newBar = new MultiTabBar(m_tabBarPosition, this, idx);
0725     newBar->installEventFilter(this);
0726     newBar->tabBar()->setStyle(tabStyle());
0727     // Fetch user set tabBar splitting before the new bar is inserted
0728     auto sections = sizes();
0729     insertWidget(idx, newBar);
0730 
0731     // For a halfway nice new section distribution we need to help the splitter.
0732     // We only support with this math to insert below some section,
0733     // not above like at first place, but that's ok atm
0734     idx = idx < 0 ? sections.count() : idx;
0735     if (idx) {
0736         if (m_syncWithTabs) {
0737             // Share the space where the tab came from with the new bar
0738             sections[idx - 1] = sections.at(idx - 1) / 2;
0739             sections.insert(idx, sections.at(idx - 1));
0740             setSizes(sections);
0741         } else {
0742             // We try here to keep the user manipulated tabBar splitting, but that works not perfect.
0743             // For proper calculations we need to ask for sizeHint, otherwise would tabs with text crunched,
0744             // but because the to be moved tab is still at the old place, we need to delay that.
0745             QTimer::singleShot(100, this, [this, idx, sections]() {
0746                 if (tabBarCount() - 1 < idx) {
0747                     // Config mismatch, the add bar was removed in the meanwhile
0748                     return;
0749                 }
0750                 QList<int> sectionsC(sections); // To manipulate, we need a C-opy
0751                 if (sectionsC.count() == 1) {
0752                     int oldTabSize = isVertical() ? tabBar(idx - 1)->sizeHint().height() : tabBar(idx - 1)->sizeHint().width();
0753                     sectionsC[0] -= oldTabSize;
0754                     sectionsC.insert(0, oldTabSize);
0755                 } else {
0756                     int newTabSize = isVertical() ? tabBar(idx)->sizeHint().height() : tabBar(idx)->sizeHint().width();
0757                     for (int i = 0; i < sections.size(); ++i) {
0758                         sectionsC[i] -= newTabSize;
0759                     }
0760                     sectionsC.insert(idx, newTabSize);
0761                 }
0762                 setSizes(sectionsC);
0763             });
0764         }
0765     }
0766 
0767     connect(newBar, &MultiTabBar::lastTabRemoved, this, &Sidebar::tabBarIsEmpty);
0768 
0769     return newBar;
0770 }
0771 
0772 void Sidebar::updateLastSizeOnResize()
0773 {
0774     const int splitHandleIndex = qMin(m_splitter->indexOf(m_ownSplit) + 1, m_splitter->count() - 1);
0775     // this method requires that a splitter handle for resizing the sidebar exists
0776     Q_ASSERT(splitHandleIndex > 0);
0777     m_splitter->handle(splitHandleIndex)->installEventFilter(this);
0778 }
0779 
0780 int Sidebar::nextId()
0781 {
0782     static int id = 0;
0783     return ++id;
0784 }
0785 
0786 ToolView *Sidebar::addToolView(const QIcon &icon, const QString &text, const QString &identifier, ToolView *widget)
0787 {
0788     if (widget) {
0789         if (widget->sidebar() == this) {
0790             return widget;
0791         }
0792 
0793         widget->sidebar()->removeToolView(widget);
0794 
0795     } else {
0796         widget = new ToolView(m_mainWin, this, nullptr, identifier);
0797         widget->icon = icon;
0798         widget->text = text;
0799     }
0800 
0801     widget->m_sidebar = this;
0802 
0803     auto blankTabId = m_tvIdToTabId.find(identifier);
0804     if (blankTabId != m_tvIdToTabId.end()) {
0805         int newId = blankTabId->second;
0806         m_idToWidget.emplace(newId, widget);
0807         m_widgetToId.emplace(widget, newId);
0808         appendStyledTab(newId, tabBar(m_tvIdToTabBar.at(identifier)), widget);
0809         // Indicate the blank tab is re-used
0810         m_tvIdToTabId.erase(identifier);
0811         m_tvIdToTabBar.erase(identifier);
0812     } else {
0813         int newId = nextId();
0814         m_idToWidget.emplace(newId, widget);
0815         m_widgetToId.emplace(widget, newId);
0816         appendStyledTab(newId, tabBar(0), widget);
0817     }
0818 
0819     show();
0820 
0821     return widget;
0822 }
0823 
0824 bool Sidebar::removeToolView(ToolView *widget)
0825 {
0826     if (m_widgetToId.find(widget) == m_widgetToId.end()) {
0827         return false;
0828     }
0829 
0830     int id = m_widgetToId.at(widget);
0831 
0832     auto tbar = m_widgetToTabBar.at(widget);
0833     tbar->removeTab(id);
0834 
0835     m_idToWidget.erase(id);
0836     m_widgetToId.erase(widget);
0837 
0838     updateSidebar();
0839 
0840     return true;
0841 }
0842 
0843 bool Sidebar::showToolView(ToolView *widget)
0844 {
0845     if (m_widgetToId.find(widget) == m_widgetToId.end()) {
0846         return false;
0847     }
0848 
0849     tabBar(widget)->showToolView(m_widgetToId.at(widget));
0850     updateSidebar();
0851 
0852     return true;
0853 }
0854 
0855 bool Sidebar::hideToolView(ToolView *widget)
0856 {
0857     if (m_widgetToId.find(widget) == m_widgetToId.end()) {
0858         return false;
0859     }
0860 
0861     updateLastSize();
0862     tabBar(widget)->hideToolView(m_widgetToId.at(widget));
0863     updateSidebar();
0864 
0865     return true;
0866 }
0867 
0868 void Sidebar::showToolviewTab(ToolView *widget, bool show)
0869 {
0870     auto it = m_widgetToId.find(widget);
0871     if (it == m_widgetToId.end()) {
0872         qWarning() << Q_FUNC_INFO << "Unexpected no id for widget " << widget;
0873         return;
0874     }
0875     auto *tab = kmTabBar(widget)->tab(it->second);
0876     if (widget->tabButtonVisible() == show) {
0877         return;
0878     } else {
0879         widget->setTabButtonVisible(show);
0880         tab->setVisible(show);
0881         Q_EMIT widget->tabButtonVisibleChanged(show);
0882     }
0883 }
0884 
0885 bool Sidebar::isCollapsed()
0886 {
0887     return m_splitter->sizes().at(m_ownSplitIndex) == 0;
0888 }
0889 
0890 void Sidebar::handleCollapse(int pos, int index)
0891 {
0892     Q_UNUSED(pos);
0893 
0894     // Verify that we are handling the correct/matching sidebar
0895     // 0 | 1 | 2  <- m_ownSplitIndex (can be 0 or 2)
0896     //   1   2    <- index (of splitters, represented by |)
0897     const bool myInterest = ((m_ownSplitIndex == 0) && (1 == index)) || (m_ownSplitIndex == index);
0898     if (!myInterest) {
0899         return;
0900     }
0901 
0902     if (isCollapsed() && !m_isPreviouslyCollapsed) {
0903         if (!m_resizePlaceholder) {
0904             m_resizePlaceholder = new QLabel();
0905             m_ownSplit->addWidget(m_resizePlaceholder);
0906             m_resizePlaceholder->show();
0907             m_resizePlaceholder->setMinimumSize(QSize(160, 160)); // Same minimum size set in ToolView::minimumSizeHint
0908         }
0909         collapseSidebar();
0910     } else if (!isCollapsed() && m_isPreviouslyCollapsed) {
0911         updateSidebar();
0912     }
0913 }
0914 
0915 void Sidebar::ownSplitMoved(int pos, int index)
0916 {
0917     QList<int> wsizes = m_ownSplit->sizes();
0918     for (int i = 0; i < tabBarCount(); ++i) {
0919         if (tabBar(i)->isToolActive()) {
0920             tabBar(i)->setSectionSize(wsizes.at(i));
0921         }
0922     }
0923 
0924     if (m_syncWithTabs) {
0925         moveSplitter(pos, index);
0926     }
0927 }
0928 
0929 void Sidebar::barSplitMoved(int pos, int index)
0930 {
0931     Q_UNUSED(pos);
0932     Q_UNUSED(index);
0933 
0934     if (m_syncWithTabs) {
0935         adjustSplitterSections();
0936     }
0937 }
0938 
0939 bool Sidebar::tabBarIsEmpty(MultiTabBar *bar)
0940 {
0941     // Don't remove the last bar!
0942     if (!bar || bar->tabCount() > 0 || tabBarCount() == 1) {
0943         return false;
0944     }
0945 
0946     delete bar;
0947 
0948     QTimer::singleShot(0, this, [this]() {
0949         // We need to delay the update or m_ownSplit report wrong size count
0950         updateSidebar();
0951     });
0952 
0953     return true;
0954 }
0955 
0956 void Sidebar::collapseSidebar()
0957 {
0958     if (m_isPreviouslyCollapsed) {
0959         return;
0960     }
0961 
0962     updateLastSize();
0963 
0964     for (int i = 0; i < tabBarCount(); ++i) {
0965         tabBar(i)->collapseToolView();
0966     }
0967 
0968     m_isPreviouslyCollapsed = true;
0969 
0970     if (!m_resizePlaceholder) {
0971         // Hiding m_ownSplit will cause that no resize handle is offered and
0972         // as side effect the sidebar collapse
0973         m_ownSplit->hide();
0974     } else {
0975         // We need to force collapse the sidebar by set a zero size
0976         QList<int> wsizes = m_splitter->sizes();
0977         wsizes[m_ownSplitIndex] = 0;
0978         m_splitter->setSizes(wsizes);
0979     }
0980 
0981     // Now that tools are hidden, ensure the doc got the focus
0982     m_mainWin->triggerFocusForCentralWidget();
0983 }
0984 
0985 bool Sidebar::adjustSplitterSections()
0986 {
0987     // Here we catch two birds with one stone
0988     // - Report some caller if any tool is in use or not
0989     // - Adjust the m_ownSplit sizes to fit more sensible the active tools
0990     //   To do so we run the loop in reverse order for a better result
0991     bool anyVis = false;
0992     QList<int> wsizes = sizes();
0993     int sizeCollector = 0;
0994     int lastExpandedId = -1;
0995     for (int i = tabBarCount() - 1; i > -1; --i) {
0996         sizeCollector += wsizes.at(i);
0997         if (tabBar(i)->expandToolView()) {
0998             anyVis = true;
0999             wsizes[i] = sizeCollector;
1000             sizeCollector = 0;
1001             lastExpandedId = i;
1002         } else {
1003             wsizes[i] = 0;
1004         }
1005     }
1006 
1007     if (!anyVis) {
1008         // No need to go on
1009         return false;
1010     }
1011 
1012     if (!m_syncWithTabs) {
1013         return true;
1014     }
1015 
1016     if (sizeCollector && lastExpandedId > -1) {
1017         wsizes[lastExpandedId] += sizeCollector;
1018     }
1019 
1020     m_ownSplit->setSizes(wsizes);
1021 
1022     return true;
1023 }
1024 
1025 void Sidebar::updateSidebar()
1026 {
1027     if (!adjustSplitterSections()) {
1028         // Nothing is shown, don't expand to a blank area
1029         collapseSidebar();
1030         return;
1031     }
1032 
1033     if (m_resizePlaceholder) {
1034         delete m_resizePlaceholder;
1035     }
1036 
1037     m_isPreviouslyCollapsed = false;
1038 
1039     if (isCollapsed()) {
1040         QList<int> wsizes = m_splitter->sizes();
1041         wsizes[m_ownSplitIndex] = m_lastSize;
1042         m_splitter->setSizes(wsizes);
1043     }
1044 
1045     if (!m_syncWithTabs) {
1046         QList<int> wsizes = m_ownSplit->sizes();
1047         for (int i = 0; i < tabBarCount(); ++i) {
1048             wsizes[i] = tabBar(i)->isToolActive() ? tabBar(i)->sectionSize() : 0;
1049         }
1050         m_ownSplit->setSizes(wsizes);
1051     }
1052 
1053     // Ensure we are visible
1054     m_ownSplit->show();
1055 }
1056 
1057 bool Sidebar::eventFilter(QObject *obj, QEvent *ev)
1058 {
1059     if (ev->type() == QEvent::ContextMenu) {
1060         QContextMenuEvent *e = static_cast<QContextMenuEvent *>(ev);
1061         KMultiTabBarTab *bt = qobject_cast<KMultiTabBarTab *>(obj);
1062         if (bt) {
1063             // qCDebug(LOG_KATE) << "Request for popup";
1064 
1065             m_popupButton = bt->id();
1066 
1067             ToolView *w = m_idToWidget[m_popupButton];
1068 
1069             if (w) {
1070                 QMenu menu(this);
1071 
1072                 menu.addSection(w->icon, w->text);
1073 
1074                 if (!w->plugin.isNull()) {
1075                     if (w->plugin.data()->configPages() > 0) {
1076                         menu.addAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("Configure..."))->setData(ConfigureAction);
1077                     }
1078                 }
1079 
1080                 menu.addAction(i18n("Hide Button"))->setData(HideButtonAction);
1081 
1082                 menu.addSection(QIcon::fromTheme(QStringLiteral("move")), i18n("Move To"));
1083 
1084                 int tabBarId = indexOf(m_widgetToTabBar.at(w));
1085 
1086                 if (tabBar(tabBarId)->tabCount() > 1) {
1087                     menu.addAction(QIcon::fromTheme(QStringLiteral("list-add")), i18n("Own Section"))->setData(ToOwnSectAction);
1088                 }
1089 
1090                 if (tabBarCount() > 1) {
1091                     if (tabBarId < 1) {
1092                         if (isVertical()) {
1093                             menu.addAction(QIcon::fromTheme(QStringLiteral("go-down")), i18n("One Down"))->setData(DownRightAction);
1094                         } else {
1095                             menu.addAction(QIcon::fromTheme(QStringLiteral("go-next")), i18n("One Right"))->setData(DownRightAction);
1096                         }
1097                     } else {
1098                         if (isVertical()) {
1099                             menu.addAction(QIcon::fromTheme(QStringLiteral("go-up")), i18n("One Up"))->setData(UpLeftAction);
1100                             if (tabBarId < tabBarCount() - 1) {
1101                                 menu.addAction(QIcon::fromTheme(QStringLiteral("go-down")), i18n("One Down"))->setData(DownRightAction);
1102                             }
1103                         } else {
1104                             menu.addAction(QIcon::fromTheme(QStringLiteral("go-previous")), i18n("One Left"))->setData(UpLeftAction);
1105                             if (tabBarId < tabBarCount() - 1) {
1106                                 menu.addAction(QIcon::fromTheme(QStringLiteral("go-next")), i18n("One Right"))->setData(DownRightAction);
1107                             }
1108                         }
1109                     }
1110                 }
1111 
1112                 if (position() != 0) {
1113                     menu.addAction(QIcon::fromTheme(QStringLiteral("go-previous")), i18n("Left Sidebar"))->setData(0);
1114                 }
1115 
1116                 if (position() != 1) {
1117                     menu.addAction(QIcon::fromTheme(QStringLiteral("go-next")), i18n("Right Sidebar"))->setData(1);
1118                 }
1119 
1120                 if (position() != 2) {
1121                     menu.addAction(QIcon::fromTheme(QStringLiteral("go-up")), i18n("Top Sidebar"))->setData(2);
1122                 }
1123 
1124                 if (position() != 3) {
1125                     menu.addAction(QIcon::fromTheme(QStringLiteral("go-down")), i18n("Bottom Sidebar"))->setData(3);
1126                 }
1127 
1128                 connect(&menu, &QMenu::triggered, this, &Sidebar::buttonPopupActivate);
1129 
1130                 menu.exec(e->globalPos());
1131 
1132                 return true;
1133             }
1134         }
1135     } else if (ev->type() == QEvent::MouseButtonRelease) {
1136         // The sidebar's splitter handle handle was released, so we update the sidebar's size. See Sidebar::updateLastSizeOnResize
1137         QMouseEvent *e = static_cast<QMouseEvent *>(ev);
1138         if (e->button() == Qt::LeftButton) {
1139             updateLastSize();
1140         }
1141     } else if (ev->type() == QEvent::MouseButtonPress) {
1142         QMouseEvent *e = static_cast<QMouseEvent *>(ev);
1143         if (qobject_cast<MultiTabBar *>(obj)) {
1144             // The non-tab area of the sidebar is clicked (well, pressed) => toggle collapse/expand
1145             if (e->button() == Qt::LeftButton) {
1146                 if (obj->property("is-multi-tabbar").toBool()) {
1147                     if (isCollapsed()) {
1148                         updateSidebar();
1149                     } else {
1150                         collapseSidebar();
1151                     }
1152                     return true;
1153                 }
1154             }
1155         } else if (qobject_cast<KMultiTabBarTab *>(obj) && e->button() == Qt::LeftButton) {
1156             dragStartPos = e->pos();
1157         }
1158     } else if (!dragStartPos.isNull() && ev->type() == QEvent::MouseMove) {
1159         QMouseEvent *e = static_cast<QMouseEvent *>(ev);
1160         auto tab = qobject_cast<KMultiTabBarTab *>(obj);
1161         if (tab && (e->pos() - dragStartPos).manhattanLength() >= QApplication::startDragDistance()) {
1162             // start drag
1163             QPixmap pixmap = tab->grab();
1164             QDrag *drag = new QDrag(this);
1165             auto md = new QMimeData();
1166             ToolView *toolView = m_idToWidget[tab->id()];
1167             Q_ASSERT(toolView);
1168             md->setProperty("toolviewToMove", QVariant::fromValue(toolView));
1169             drag->setMimeData(md);
1170             drag->setPixmap(pixmap);
1171             drag->setHotSpot(dragStartPos);
1172             dragStartPos = {};
1173             connect(drag, &QObject::destroyed, this, &Sidebar::dragEnded);
1174 
1175             Q_EMIT dragStarted();
1176 
1177             drag->exec(Qt::MoveAction);
1178             return true;
1179         }
1180     }
1181 
1182     return QSplitter::eventFilter(obj, ev);
1183 }
1184 
1185 void Sidebar::dragEnterEvent(QDragEnterEvent *e)
1186 {
1187     if (e->proposedAction() != Qt::MoveAction) {
1188         return;
1189     }
1190 
1191     if (!e->mimeData() || !e->mimeData()->property("toolviewToMove").value<ToolView *>()) {
1192         return;
1193     }
1194 
1195     if (e->source() == this) {
1196         if (toolviewCount() == 1) {
1197             // only 1 toolview? Nothing to reorder then
1198             return;
1199         }
1200         m_internalDropIndicator->raise();
1201         if (m_internalDropIndicator->geometry() != geometry()) {
1202             m_internalDropIndicator->setGeometry(geometry());
1203         }
1204 
1205         if (isVertical()) {
1206             m_internalDropIndicator->setFixedHeight(4);
1207         } else {
1208             m_internalDropIndicator->setFixedWidth(4);
1209         }
1210 
1211         auto mimeData = e->mimeData();
1212         ToolView *toolview = mimeData->property("toolviewToMove").value<ToolView *>();
1213         QWidget *tab = tabButtonForToolview(toolview);
1214         if (!tab || !toolview) {
1215             return;
1216         }
1217 
1218         const QPoint tabPos = tab->pos();
1219         auto globalPos = mapToGlobal(tabPos);
1220         auto pos = m_mainWin->mapFromGlobal(globalPos);
1221         m_internalDropIndicator->move(pos);
1222         m_internalDropIndicator->show();
1223     } else { // Show Drop Indicator
1224         m_dropIndicator->raise();
1225         if (m_dropIndicator->geometry() != geometry()) {
1226             m_dropIndicator->setGeometry(geometry());
1227         }
1228         auto globalPos = mapToGlobal(pos());
1229         auto indicatorPos = m_dropIndicator->mapFromGlobal(globalPos);
1230         if (indicatorPos != m_dropIndicator->pos()) {
1231             m_dropIndicator->move(indicatorPos);
1232         }
1233 
1234         m_dropIndicator->show();
1235     }
1236     e->acceptProposedAction();
1237 }
1238 
1239 void Sidebar::dropEvent(QDropEvent *e)
1240 {
1241     auto mimeData = e->mimeData();
1242     m_dropIndicator->hide();
1243     m_internalDropIndicator->hide();
1244 
1245     if (!mimeData) {
1246         return;
1247     }
1248     ToolView *toolview = mimeData->property("toolviewToMove").value<ToolView *>();
1249     if (!toolview) {
1250         return;
1251     }
1252 
1253     if (e->source() == this) {
1254         // Re-ordering tabs
1255         auto sourceTab = qobject_cast<KMultiTabBarTab *>(tabButtonForToolview(toolview));
1256         if (!sourceTab) {
1257             return;
1258         }
1259         // destTab might be null, which means we will just append to the end
1260         auto destTab = qobject_cast<KMultiTabBarTab *>(childAt(e->position().toPoint()));
1261         auto tabbar = m_widgetToTabBar[toolview];
1262         tabbar->reorderTab(sourceTab->id(), destTab);
1263     } else {
1264         m_mainWin->moveToolView(toolview, position(), /*isDND=*/true);
1265         m_mainWin->showToolView(toolview);
1266     }
1267 
1268     e->accept();
1269 }
1270 
1271 void Sidebar::dragMoveEvent(QDragMoveEvent *e)
1272 {
1273     if (e->source() != this || toolviewCount() == 1) {
1274         // We only handle drag move if we are moving on source sidebar
1275         // and if there is only 1 toolview, there is nothing to reorder
1276         return;
1277     }
1278 
1279     auto *tab = qobject_cast<KMultiTabBarTab *>(childAt(e->position().toPoint()));
1280     if (tab) {
1281         // moving over a tab? show the indicator at tab start
1282         const QPoint tabPos = tab->pos();
1283         auto globalPos = mapToGlobal(tabPos);
1284         auto pos = m_mainWin->mapFromGlobal(globalPos);
1285         m_internalDropIndicator->move(pos);
1286     } else {
1287         // Otherwise show at the end of tab list
1288         auto mimeData = e->mimeData();
1289         ToolView *toolview = mimeData->property("toolviewToMove").value<ToolView *>();
1290         if (!toolview) {
1291             return;
1292         }
1293         auto tabbar = m_widgetToTabBar[toolview];
1294         auto lastTabId = tabbar->tabList().back();
1295         auto tab = tabbar->tabBar()->tab(lastTabId);
1296 
1297         QPoint tabPos = tab->pos();
1298         if (isVertical()) {
1299             tabPos.setY(tabPos.y() + tab->height());
1300         } else {
1301             tabPos.setX(tabPos.x() + tab->width());
1302         }
1303         auto globalPos = mapToGlobal(tabPos);
1304         auto pos = m_mainWin->mapFromGlobal(globalPos);
1305         m_internalDropIndicator->move(pos);
1306     }
1307 }
1308 
1309 void Sidebar::dragLeaveEvent(QDragLeaveEvent *)
1310 {
1311     m_internalDropIndicator->hide();
1312     m_dropIndicator->hide();
1313 }
1314 
1315 void Sidebar::setVisible(bool visible)
1316 {
1317     // visible==true means show-request
1318     if (visible && (m_idToWidget.empty() || !m_mainWin->sidebarsVisible())) {
1319         return;
1320     }
1321 
1322     QSplitter::setVisible(visible);
1323 }
1324 
1325 void Sidebar::buttonPopupActivate(QAction *a)
1326 {
1327     const int id = a->data().toInt();
1328     ToolView *w = m_idToWidget[m_popupButton];
1329 
1330     if (!w) {
1331         return;
1332     }
1333 
1334     // move to other Sidebar ids
1335     if (id < 4) {
1336         // move + show ;)
1337         m_mainWin->moveToolView(w, static_cast<KMultiTabBar::KMultiTabBarPosition>(id));
1338         m_mainWin->showToolView(w);
1339     }
1340 
1341     if (id == ConfigureAction) {
1342         if (!w->plugin.isNull()) {
1343             if (w->plugin.data()->configPages() > 0) {
1344                 Q_EMIT sigShowPluginConfigPage(w->plugin.data(), 0);
1345             }
1346         }
1347     }
1348 
1349     if (id == HideButtonAction) {
1350         showToolviewTab(w, false);
1351     }
1352 
1353     if (id == ToOwnSectAction) {
1354         auto newBar = insertTabBar(indexOf(m_widgetToTabBar.at(w)) + 1);
1355         tabBar(w)->removeTab(m_widgetToId.at(w));
1356         appendStyledTab(m_widgetToId.at(w), newBar, w);
1357         showToolView(w);
1358     }
1359     if (id == UpLeftAction) {
1360         auto newBar = tabBar(indexOf(tabBar(w)) - 1);
1361         tabBar(w)->removeTab(m_widgetToId.at(w));
1362         appendStyledTab(m_widgetToId.at(w), newBar, w);
1363     }
1364     if (id == DownRightAction) {
1365         auto newBar = tabBar(indexOf(tabBar(w)) + 1);
1366         tabBar(w)->removeTab(m_widgetToId.at(w));
1367         appendStyledTab(m_widgetToId.at(w), newBar, w);
1368     }
1369 }
1370 
1371 void Sidebar::updateLastSize()
1372 {
1373     if (isCollapsed()) {
1374         // We are too late, don't update to stupid value
1375         return;
1376     }
1377 
1378     QList<int> s = m_splitter->sizes();
1379 
1380     // Ensure last size is always sensible
1381     m_lastSize = qMax(s[m_ownSplitIndex], 160);
1382 }
1383 
1384 void Sidebar::startRestoreSession(KConfigGroup &config)
1385 {
1386     // ensure we only run once and we don't start a saveSession in addition
1387     if (m_sessionRestoreRunning) {
1388         return;
1389     }
1390     m_sessionRestoreRunning = true;
1391 
1392     // Using splitter data avoid to store tabBarCount explicit ;-)
1393     QList<int> s = config.readEntry(QStringLiteral("Kate-MDI-Sidebar-%1-Splitter").arg(position()), QList<int>());
1394     // Notice the start value of 1, only add extra tab bars
1395     for (int i = 1; i < s.size(); ++i) {
1396         insertTabBar();
1397     }
1398     // Create for each tool we expect, in the correct order, a tab in advance, a blank one
1399     for (int i = 0; i < s.size(); ++i) {
1400         QStringList tvList = config.readEntry(QStringLiteral("Kate-MDI-Sidebar-%1-Bar-%2-TvList").arg(position()).arg(i), QStringList());
1401         for (int j = 0; j < tvList.size(); ++j) {
1402             // Don't add in case of a config mismatch blank tabs for some stuff twice
1403             auto search = m_tvIdToTabId.find(tvList.at(j));
1404             if (search == m_tvIdToTabId.end()) {
1405                 int id = tabBar(i)->addBlankTab();
1406                 m_tvIdToTabId.emplace(tvList.at(j), id);
1407                 m_tvIdToTabBar.emplace(tvList.at(j), i);
1408             }
1409         }
1410     }
1411 }
1412 
1413 void Sidebar::restoreSession(KConfigGroup &config)
1414 {
1415     // Only continue when restore was started properly
1416     if (!m_sessionRestoreRunning) {
1417         return;
1418     }
1419 
1420     // show only correct toolviews ;)
1421     for (const auto &[id, tv] : m_idToWidget) {
1422         tabBar(tv)->setTabActive(id, config.readEntry(QStringLiteral("Kate-MDI-ToolView-%1-Visible").arg(tv->id), false));
1423         showToolviewTab(tv, config.readEntry(QStringLiteral("Kate-MDI-ToolView-%1-Show-Button-In-Sidebar").arg(tv->id), true));
1424     }
1425 
1426     // In case of some config mismatch, we should remove left over blank tabs
1427     for (const auto &[tv, id] : m_tvIdToTabId) {
1428         tabBar(m_tvIdToTabBar.at(tv))->removeBlankTab(id);
1429     }
1430     m_tvIdToTabId.clear();
1431     m_tvIdToTabBar.clear();
1432 
1433     // In case of some config mismatch, we may have some ugly empty bars
1434     for (int i = 0; i < tabBarCount(); ++i) {
1435         // Don't panic! Function take care not to remove non empty bar
1436         if (tabBarIsEmpty(tabBar(i))) {
1437             // Now that the bar is gone, we need to adjust our index or we would skip some bar
1438             --i;
1439         }
1440     }
1441 
1442     QList<int> sectSizes = config.readEntry(QStringLiteral("Kate-MDI-Sidebar-%1-SectSizes").arg(position()), QList<int>());
1443     for (int i = 0; i < sectSizes.count(); ++i) {
1444         if (tabBarCount() - 1 < i) {
1445             // Config mismatch!
1446             break;
1447         }
1448         tabBar(i)->setSectionSize(sectSizes.at(i));
1449     }
1450 
1451     // Collapse now, and before! we restore m_lastSize, will hide (all) m_stack(s) so that
1452     // expanding will work fine...
1453     collapseSidebar();
1454     m_lastSize = config.readEntry(QStringLiteral("Kate-MDI-Sidebar-%1-LastSize").arg(position()), 160);
1455     // Since we delay in insertTabBar(..) to adjust the sizes, we need it here too or the now set data will overwritten
1456     auto sz = config.readEntry(QStringLiteral("Kate-MDI-Sidebar-%1-Splitter").arg(position()), QList<int>());
1457     QTimer::singleShot(100, this, [this, sz]() {
1458         setSizes(sz);
1459 
1460         // ensure focus is not stolen
1461         m_mainWin->triggerFocusForCentralWidget();
1462     });
1463     // ...now we are ready to get the final splitter sizes by MainWindow::finishRestore
1464     updateSidebar();
1465 
1466     // be done, e.g. saveSession is now allowed again
1467     m_sessionRestoreRunning = false;
1468 }
1469 
1470 void Sidebar::saveSession(KConfigGroup &config)
1471 {
1472     // Don't try to save a session while we are still in "session restore mode" BUG:459108
1473     if (m_sessionRestoreRunning) {
1474         return;
1475     }
1476 
1477     config.writeEntry(QStringLiteral("Kate-MDI-Sidebar-%1-Splitter").arg(position()), sizes());
1478     config.writeEntry(QStringLiteral("Kate-MDI-Sidebar-%1-LastSize").arg(position()), m_lastSize);
1479 
1480     // store the data about all toolviews in this sidebar ;)
1481     for (const auto &[id, tv] : m_idToWidget) {
1482         config.writeEntry(QStringLiteral("Kate-MDI-ToolView-%1-Position").arg(tv->id), int(tv->sidebar()->position()));
1483         config.writeEntry(QStringLiteral("Kate-MDI-ToolView-%1-Visible").arg(tv->id), tv->toolVisible());
1484         config.writeEntry(QStringLiteral("Kate-MDI-ToolView-%1-Show-Button-In-Sidebar").arg(tv->id), tv->tabButtonVisible());
1485     }
1486 
1487     QList<int> sectSizes;
1488     for (int i = 0; i < tabBarCount(); ++i) {
1489         sectSizes << tabBar(i)->sectionSize();
1490 
1491         QStringList tvList;
1492         for (int j : tabBar(i)->tabList()) {
1493             tvList << m_idToWidget.at(j)->id;
1494         }
1495         config.writeEntry(QStringLiteral("Kate-MDI-Sidebar-%1-Bar-%2-TvList").arg(position()).arg(i), tvList);
1496     }
1497 
1498     config.writeEntry(QStringLiteral("Kate-MDI-Sidebar-%1-SectSizes").arg(position()), sectSizes);
1499 }
1500 
1501 // END SIDEBAR
1502 
1503 // BEGIN MAIN WINDOW
1504 
1505 MainWindow::MainWindow(QWidget *parent)
1506     : KParts::MainWindow(parent, Qt::Window)
1507     , m_guiClient(new GUIClient(this))
1508 {
1509     // central frame for all stuff
1510     QFrame *hb = new QFrame(this);
1511     setCentralWidget(hb);
1512 
1513     // top level vbox for all stuff + bottom bar
1514     QVBoxLayout *toplevelVBox = new QVBoxLayout(hb);
1515     toplevelVBox->setContentsMargins(0, 0, 0, 0);
1516     toplevelVBox->setSpacing(0);
1517 
1518     // hbox for all splitters and other side bars
1519     QHBoxLayout *hlayout = new QHBoxLayout;
1520     hlayout->setContentsMargins(0, 0, 0, 0);
1521     hlayout->setSpacing(0);
1522     toplevelVBox->addLayout(hlayout);
1523 
1524     m_hSplitter = new QSplitter(Qt::Horizontal, hb);
1525     m_sidebars[KMultiTabBar::Left] = std::make_unique<Sidebar>(KMultiTabBar::Left, m_hSplitter, this, hb);
1526     hlayout->addWidget(m_sidebars[KMultiTabBar::Left].get());
1527     hlayout->addWidget(m_hSplitter);
1528 
1529     QFrame *vb = new QFrame(m_hSplitter);
1530     QVBoxLayout *vlayout = new QVBoxLayout(vb);
1531     vlayout->setContentsMargins(0, 0, 0, 0);
1532     vlayout->setSpacing(0);
1533 
1534     m_hSplitter->setCollapsible(m_hSplitter->indexOf(vb), false);
1535     m_hSplitter->setStretchFactor(m_hSplitter->indexOf(vb), 1);
1536 
1537     m_vSplitter = new QSplitter(Qt::Vertical, vb);
1538     m_sidebars[KMultiTabBar::Top] = std::make_unique<Sidebar>(KMultiTabBar::Top, m_vSplitter, this, vb);
1539     vlayout->addWidget(m_sidebars[KMultiTabBar::Top].get());
1540     vlayout->addWidget(m_vSplitter);
1541 
1542     m_centralWidget = new QWidget(m_vSplitter);
1543     m_centralWidget->setLayout(new QVBoxLayout);
1544     m_centralWidget->layout()->setSpacing(0);
1545     m_centralWidget->layout()->setContentsMargins(0, 0, 0, 0);
1546 
1547     m_vSplitter->setCollapsible(m_vSplitter->indexOf(m_centralWidget), false);
1548     m_vSplitter->setStretchFactor(m_vSplitter->indexOf(m_centralWidget), 1);
1549 
1550     m_sidebars[KMultiTabBar::Right] = std::make_unique<Sidebar>(KMultiTabBar::Right, m_hSplitter, this, hb);
1551     hlayout->addWidget(m_sidebars[KMultiTabBar::Right].get());
1552 
1553     auto separator = new QFrame(this);
1554     separator->setFrameShape(QFrame::HLine);
1555     separator->setFixedHeight(1);
1556     toplevelVBox->addWidget(separator);
1557 
1558     // bottom side bar spans full windows, include status bar, too
1559     m_sidebars[KMultiTabBar::Bottom] = std::make_unique<Sidebar>(KMultiTabBar::Bottom, m_vSplitter, this, vb);
1560     m_bottomSidebarLayout = new QHBoxLayout;
1561     m_bottomSidebarLayout->addWidget(m_sidebars[KMultiTabBar::Bottom].get());
1562     m_statusBarStackedWidget = new QStackedWidget(this);
1563     m_statusBarStackedWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
1564     // button that shows git branch
1565     // m_branchLabel = new CurrentGitBranchButton(this);
1566     // m_bottomSidebarLayout->addWidget(m_branchLabel);
1567     // widget that hold statusbar of active kte-view
1568     m_bottomSidebarLayout->addWidget(m_statusBarStackedWidget);
1569     m_bottomSidebarLayout->setStretch(0, 100);
1570     toplevelVBox->addLayout(m_bottomSidebarLayout);
1571 
1572     // ensure proper toolview style
1573     setToolViewStyle(KMultiTabBar::KDEV3ICON);
1574 
1575     for (const auto &sidebar : m_sidebars) {
1576         connect(sidebar.get(), &Sidebar::sigShowPluginConfigPage, this, &MainWindow::sigShowPluginConfigPage);
1577 
1578         // Drag/Drop
1579         connect(sidebar.get(), &Sidebar::dragStarted, this, [this]() {
1580             for (const auto &sb : m_sidebars) {
1581                 m_dragState.m_wasVisible[(int)sb->position()] = sb->isVisible();
1582                 if (!sb->isVisible()) {
1583                     sb->setMinimumSize({50, 50});
1584                     sb->QSplitter::setVisible(true);
1585                 }
1586             }
1587         });
1588         connect(sidebar.get(), &Sidebar::dragEnded, this, [this]() {
1589             for (const auto &sb : m_sidebars) {
1590                 bool wasVisible = m_dragState.m_wasVisible[(int)sb->position()];
1591                 if (!wasVisible) {
1592                     sb->setMinimumSize({0, 0});
1593                     sb->QSplitter::setVisible(false);
1594                 }
1595             }
1596         });
1597     }
1598 }
1599 
1600 MainWindow::~MainWindow()
1601 {
1602     // kill all toolviews, they will deregister themself
1603     while (!m_toolviews.empty()) {
1604         delete m_toolviews.begin()->second;
1605     }
1606 
1607     // seems like we really should delete this by hand ;)
1608     delete m_centralWidget;
1609 }
1610 
1611 QWidget *MainWindow::centralWidget() const
1612 {
1613     return m_centralWidget;
1614 }
1615 
1616 void MainWindow::insertWidgetBeforeStatusbar(QWidget *widget)
1617 {
1618     Q_ASSERT(m_bottomSidebarLayout);
1619     const auto idxOfStatusbar = m_bottomSidebarLayout->indexOf(m_statusBarStackedWidget);
1620     Q_ASSERT(idxOfStatusbar != -1);
1621     m_bottomSidebarLayout->insertWidget(idxOfStatusbar, widget);
1622 }
1623 
1624 ToolView *MainWindow::createToolView(KTextEditor::Plugin *plugin,
1625                                      const QString &identifier,
1626                                      KMultiTabBar::KMultiTabBarPosition pos,
1627                                      const QIcon &icon,
1628                                      const QString &text)
1629 {
1630     // clashing names are not allowed
1631     if (toolView(identifier)) {
1632         return nullptr;
1633     }
1634 
1635     // try the restore config to figure out real pos
1636     if (m_restoreConfig && m_restoreConfig->hasGroup(m_restoreGroup)) {
1637         KConfigGroup cg(m_restoreConfig, m_restoreGroup);
1638         pos = static_cast<KMultiTabBar::KMultiTabBarPosition>(cg.readEntry(QStringLiteral("Kate-MDI-ToolView-%1-Position").arg(identifier), int(pos)));
1639     }
1640 
1641     ToolView *v = m_sidebars[pos]->addToolView(icon, text, identifier, nullptr);
1642     v->plugin = plugin;
1643 
1644     m_toolviews.emplace(identifier, v);
1645 
1646     // register for menu stuff
1647     m_guiClient->registerToolView(v);
1648 
1649     return v;
1650 }
1651 
1652 ToolView *MainWindow::toolView(const QString &identifier) const
1653 {
1654     auto it = m_toolviews.find(identifier);
1655     if (it != m_toolviews.end()) {
1656         return it->second;
1657     }
1658     return nullptr;
1659 }
1660 
1661 void MainWindow::toolViewDeleted(ToolView *widget)
1662 {
1663     if (!widget) {
1664         return;
1665     }
1666 
1667     if (widget->mainWindow() != this) {
1668         return;
1669     }
1670 
1671     // unregister from menu stuff
1672     m_guiClient->unregisterToolView(widget);
1673 
1674     widget->sidebar()->removeToolView(widget);
1675 
1676     m_toolviews.erase(widget->id);
1677 }
1678 
1679 void MainWindow::setSidebarsVisibleInternal(bool visible, bool hideFullySilent)
1680 {
1681     bool old_visible = m_sidebarsVisible;
1682     m_sidebarsVisible = visible;
1683 
1684     for (auto &sidebar : m_sidebars) {
1685         sidebar->setVisible(visible);
1686 
1687         // fully hide the stuff, see bug 464320
1688         if (hideFullySilent) {
1689             sidebar->collapseSidebar();
1690         }
1691     }
1692 
1693     m_guiClient->updateSidebarsVisibleAction();
1694 
1695     // show information message box, if the users hides the sidebars
1696     if (!hideFullySilent && old_visible && (!m_sidebarsVisible)) {
1697         KMessageBox::information(this,
1698                                  i18n("<qt>You are about to hide the sidebars. With "
1699                                       "hidden sidebars it is not possible to directly "
1700                                       "access the tool views with the mouse anymore, "
1701                                       "so if you need to access the sidebars again "
1702                                       "invoke <b>View &gt; Tool Views &gt; Show Sidebars</b> "
1703                                       "in the menu. It is still possible to show/hide "
1704                                       "the tool views with the assigned shortcuts.</qt>"),
1705                                  QString(),
1706                                  QStringLiteral("Kate hide sidebars notification message"));
1707     }
1708 }
1709 
1710 bool MainWindow::sidebarsVisible() const
1711 {
1712     return m_sidebarsVisible;
1713 }
1714 
1715 void MainWindow::setToolViewStyle(KMultiTabBar::KMultiTabBarStyle style)
1716 {
1717     for (auto &sidebar : m_sidebars) {
1718         sidebar->setStyle(style);
1719     }
1720 }
1721 
1722 KMultiTabBar::KMultiTabBarStyle MainWindow::toolViewStyle() const
1723 {
1724     // all sidebars have the same style, so just take Top
1725     return m_sidebars[KMultiTabBar::Top]->tabStyle();
1726 }
1727 
1728 bool MainWindow::moveToolView(ToolView *widget, KMultiTabBar::KMultiTabBarPosition pos, bool isDND)
1729 {
1730     if (!widget || widget->mainWindow() != this) {
1731         return false;
1732     }
1733 
1734     // try the restore config to figure out real pos
1735     if (m_restoreConfig && m_restoreConfig->hasGroup(m_restoreGroup)) {
1736         KConfigGroup cg(m_restoreConfig, m_restoreGroup);
1737         pos = static_cast<KMultiTabBar::KMultiTabBarPosition>(cg.readEntry(QStringLiteral("Kate-MDI-ToolView-%1-Position").arg(widget->id), int(pos)));
1738     }
1739 
1740     if (isDND) {
1741         // Ensure that after dropping, sidebar becomes visiblee
1742         // Sidebar might have been hidden because it was empty i.e.,
1743         // had no toolviews
1744         m_dragState.m_wasVisible[pos] = true;
1745 
1746         // If source sidebar contains only 1 toolview, then hide it
1747         // because after the drop it will have nothing.
1748         auto source = widget->sidebar();
1749         if (source->toolviewCount() == 1) {
1750             m_dragState.m_wasVisible[source->position()] = false;
1751         }
1752     }
1753 
1754     m_sidebars[pos]->addToolView(widget->icon, widget->text, widget->id, widget);
1755 
1756     if (isDND) {
1757         // reduce min size so that sidebar can adjust size properly
1758         m_sidebars[pos]->setMinimumSize({0, 0});
1759     }
1760 
1761     return true;
1762 }
1763 
1764 bool MainWindow::showToolView(ToolView *widget)
1765 {
1766     if (!widget || widget->mainWindow() != this) {
1767         return false;
1768     }
1769 
1770     // skip this if happens during restoring, or we will just see flicker
1771     if (m_restoreConfig && m_restoreConfig->hasGroup(m_restoreGroup)) {
1772         return true;
1773     }
1774 
1775     return widget->sidebar()->showToolView(widget);
1776 }
1777 
1778 bool MainWindow::hideToolView(ToolView *widget)
1779 {
1780     if (!widget || widget->mainWindow() != this) {
1781         return false;
1782     }
1783 
1784     // skip this if happens during restoring, or we will just see flicker
1785     if (m_restoreConfig && m_restoreConfig->hasGroup(m_restoreGroup)) {
1786         return true;
1787     }
1788 
1789     const bool ret = widget->sidebar()->hideToolView(widget);
1790     triggerFocusForCentralWidget();
1791     return ret;
1792 }
1793 
1794 void MainWindow::hideToolViews()
1795 {
1796     for (const auto &tv : m_toolviews) {
1797         tv.second->sidebar()->hideToolView(tv.second);
1798     }
1799     triggerFocusForCentralWidget();
1800 }
1801 
1802 void MainWindow::startRestore(KConfigBase *config, const QString &group)
1803 {
1804     // first save this stuff
1805     m_restoreConfig = config;
1806     m_restoreGroup = group;
1807 
1808     if (!m_restoreConfig || !m_restoreConfig->hasGroup(m_restoreGroup)) {
1809         m_restoreConfig = nullptr;
1810         m_restoreGroup.clear();
1811         return;
1812     }
1813 
1814     // apply size once, to get sizes ready ;)
1815     KConfigGroup cg(m_restoreConfig, m_restoreGroup);
1816     KWindowConfig::restoreWindowSize(windowHandle(), cg);
1817 
1818     // KWrite uses no sidebars, avoid all work beside windows sizes restoring above
1819     if (KateApp::isKWrite()) {
1820         m_restoreConfig = nullptr;
1821         m_restoreGroup.clear();
1822         return;
1823     }
1824 
1825     // restore the sidebars
1826     for (auto &sidebar : qAsConst(m_sidebars)) {
1827         sidebar->startRestoreSession(cg);
1828     }
1829 
1830     setToolViewStyle(static_cast<KMultiTabBar::KMultiTabBarStyle>(cg.readEntry("Kate-MDI-Sidebar-Style", static_cast<int>(toolViewStyle()))));
1831     // after reading m_sidebarsVisible, update the GUI toggle action
1832     m_sidebarsVisible = cg.readEntry("Kate-MDI-Sidebar-Visible", true);
1833     m_guiClient->updateSidebarsVisibleAction();
1834 }
1835 
1836 void MainWindow::finishRestore()
1837 {
1838     if (!m_restoreConfig) {
1839         return;
1840     }
1841 
1842     if (m_restoreConfig->hasGroup(m_restoreGroup)) {
1843         // apply all settings, like toolbar pos and more ;)
1844         KConfigGroup cg(m_restoreConfig, m_restoreGroup);
1845         applyMainWindowSettings(cg);
1846 
1847         // reshuffle toolviews only if needed
1848         for (const auto &[id, tv] : m_toolviews) {
1849             KMultiTabBar::KMultiTabBarPosition newPos = static_cast<KMultiTabBar::KMultiTabBarPosition>(
1850                 cg.readEntry(QStringLiteral("Kate-MDI-ToolView-%1-Position").arg(id), int(tv->sidebar()->position())));
1851 
1852             if (tv->sidebar()->position() != newPos) {
1853                 moveToolView(tv, newPos);
1854             }
1855         }
1856 
1857         // Restore the sidebars before we restore h/vSplitter..
1858         for (auto &sidebar : m_sidebars) {
1859             sidebar->restoreSession(cg);
1860         }
1861 
1862         // get main splitter sizes ;)
1863         m_hSplitter->setSizes(cg.readEntry("Kate-MDI-H-Splitter", QList<int>{200, 100, 200}));
1864         m_vSplitter->setSizes(cg.readEntry("Kate-MDI-V-Splitter", QList<int>{150, 100, 200}));
1865 
1866         // Expand again to trigger splitter sync tabs/tools, but for any reason works this sometimes only after enough delay
1867         QTimer::singleShot(400, this, [this]() {
1868             // ensure we don't steal the focus, remember old focus widget
1869             QPointer<QWidget> oldFocusWidget(QApplication::focusWidget());
1870 
1871             for (auto &sidebar : m_sidebars) {
1872                 sidebar->updateSidebar();
1873             }
1874 
1875             // ensure focus is not stolen, pass back to widget or at least central area
1876             if (oldFocusWidget) {
1877                 oldFocusWidget->setFocus();
1878             } else {
1879                 triggerFocusForCentralWidget();
1880             }
1881         });
1882     }
1883 
1884     // clear this stuff, we are done ;)
1885     m_restoreConfig = nullptr;
1886     m_restoreGroup.clear();
1887 }
1888 
1889 void MainWindow::saveSession(KConfigGroup &config)
1890 {
1891     saveMainWindowSettings(config);
1892 
1893     // KWrite uses no sidebars, avoid all work beside windows sizes saving
1894     if (KateApp::isKWrite()) {
1895         return;
1896     }
1897 
1898     // save main splitter sizes ;)
1899     config.writeEntry("Kate-MDI-H-Splitter", m_hSplitter->sizes());
1900     config.writeEntry("Kate-MDI-V-Splitter", m_vSplitter->sizes());
1901 
1902     // save sidebar style
1903     config.writeEntry("Kate-MDI-Sidebar-Style", static_cast<int>(toolViewStyle()));
1904     config.writeEntry("Kate-MDI-Sidebar-Visible", m_sidebarsVisible);
1905 
1906     // save the sidebars
1907     for (auto &sidebar : m_sidebars) {
1908         sidebar->saveSession(config);
1909     }
1910 }
1911 
1912 QWidget *MainWindow::createContainer(QWidget *parent, int index, const QDomElement &element, QAction *&containerAction)
1913 {
1914     // ensure we don't have toolbar accelerators that clash with other stuff
1915     QWidget *createdContainer = KParts::MainWindow::createContainer(parent, index, element, containerAction);
1916     if (element.tagName() == QLatin1String("ToolBar")) {
1917         KAcceleratorManager::setNoAccel(createdContainer);
1918     }
1919     return createdContainer;
1920 }
1921 
1922 // END MAIN WINDOW
1923 
1924 } // namespace KateMDI
1925 
1926 #include "moc_katemdi.cpp"