File indexing completed on 2024-04-28 05:50:56

0001 /*
0002     SPDX-FileCopyrightText: 2006-2008 Robert Knight <robertknight@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 // Own
0008 #include "widgets/ViewSplitter.h"
0009 #include "KonsoleSettings.h"
0010 
0011 // Qt
0012 #include <QApplication>
0013 #include <QChildEvent>
0014 #include <QDragEnterEvent>
0015 #include <QMimeData>
0016 
0017 // C++
0018 #include <memory>
0019 
0020 // Konsole
0021 #include "terminalDisplay/TerminalDisplay.h"
0022 #include "terminalDisplay/TerminalFonts.h"
0023 #include "terminalDisplay/TerminalScrollBar.h"
0024 #include "widgets/ViewContainer.h"
0025 
0026 using Konsole::TerminalDisplay;
0027 using Konsole::ViewSplitter;
0028 
0029 bool ViewSplitter::m_drawTopLevelHandler;
0030 Qt::Orientation ViewSplitter::m_topLevelHandlerDrawnOrientation;
0031 int ViewSplitter::lastSplitterId = -1;
0032 
0033 // TODO: Connect the TerminalDisplay destroyed signal here.
0034 
0035 namespace
0036 {
0037 int calculateHandleWidth(int settingsEnum)
0038 {
0039     switch (settingsEnum) {
0040     case Konsole::KonsoleSettings::SplitDragHandleLarge:
0041         return 10;
0042     case Konsole::KonsoleSettings::SplitDragHandleMedium:
0043         return 5;
0044     case Konsole::KonsoleSettings::SplitDragHandleSmall:
0045         return 1;
0046     default:
0047         return 1;
0048     }
0049 }
0050 }
0051 
0052 ViewSplitter::ViewSplitter(QWidget *parent)
0053     : QSplitter(parent)
0054     , _id(++lastSplitterId)
0055 {
0056     setAcceptDrops(true);
0057     connect(KonsoleSettings::self(), &KonsoleSettings::configChanged, this, [this] {
0058         setHandleWidth(calculateHandleWidth(KonsoleSettings::self()->splitDragHandleSize()));
0059     });
0060 }
0061 
0062 /* This function is called on the toplevel splitter, we need to look at the actual ViewSplitter inside it */
0063 void ViewSplitter::adjustActiveTerminalDisplaySize(int percentage)
0064 {
0065     auto focusedTerminalDisplay = activeTerminalDisplay();
0066     Q_ASSERT(focusedTerminalDisplay);
0067 
0068     auto parentSplitter = qobject_cast<ViewSplitter *>(focusedTerminalDisplay->parent());
0069     const int containerIndex = parentSplitter->indexOf(activeTerminalDisplay());
0070     Q_ASSERT(containerIndex != -1);
0071 
0072     QList<int> containerSizes = parentSplitter->sizes();
0073 
0074     const int oldSize = containerSizes[containerIndex];
0075     const auto newSize = static_cast<int>(oldSize * (1.0 + percentage / 100.0));
0076     const int perContainerDelta = (count() == 1) ? 0 : ((newSize - oldSize) / (count() - 1)) * (-1);
0077 
0078     for (int &size : containerSizes) {
0079         size += perContainerDelta;
0080     }
0081     containerSizes[containerIndex] = newSize;
0082 
0083     parentSplitter->setSizes(containerSizes);
0084 }
0085 
0086 // Get the first splitter that's a parent of the current focused widget.
0087 ViewSplitter *ViewSplitter::activeSplitter()
0088 {
0089     QWidget *widget = focusWidget() != nullptr ? focusWidget() : this;
0090 
0091     ViewSplitter *splitter = nullptr;
0092 
0093     while ((splitter == nullptr) && (widget != nullptr)) {
0094         splitter = qobject_cast<ViewSplitter *>(widget);
0095         widget = widget->parentWidget();
0096     }
0097 
0098     Q_ASSERT(splitter);
0099     return splitter;
0100 }
0101 
0102 void ViewSplitter::updateSizes()
0103 {
0104     const int space = (orientation() == Qt::Horizontal ? width() : height()) / count();
0105     setSizes(QVector<int>(count(), space).toList());
0106 }
0107 
0108 void ViewSplitter::addTerminalDisplay(TerminalDisplay *terminalDisplay, Qt::Orientation containerOrientation, AddBehavior behavior)
0109 {
0110     ViewSplitter *splitter = activeSplitter();
0111     const int currentIndex = splitter->activeTerminalDisplay() == nullptr ? splitter->count() : splitter->indexOf(splitter->activeTerminalDisplay());
0112 
0113     if (splitter->count() < 2) {
0114         splitter->insertWidget(behavior == AddBehavior::AddBefore ? currentIndex : currentIndex + 1, terminalDisplay);
0115         splitter->setOrientation(containerOrientation);
0116         splitter->updateSizes();
0117     } else if (containerOrientation == splitter->orientation()) {
0118         splitter->insertWidget(currentIndex, terminalDisplay);
0119         splitter->updateSizes();
0120     } else {
0121         QList<int> sizes = splitter->sizes();
0122         auto newSplitter = new ViewSplitter();
0123         TerminalDisplay *oldTerminalDisplay = splitter->activeTerminalDisplay();
0124         const int oldContainerIndex = splitter->indexOf(oldTerminalDisplay);
0125         splitter->m_blockPropagatedDeletion = true;
0126         newSplitter->addWidget(behavior == AddBehavior::AddBefore ? terminalDisplay : oldTerminalDisplay);
0127         newSplitter->addWidget(behavior == AddBehavior::AddBefore ? oldTerminalDisplay : terminalDisplay);
0128         newSplitter->setOrientation(containerOrientation);
0129         newSplitter->show();
0130         splitter->insertWidget(oldContainerIndex, newSplitter);
0131         splitter->m_blockPropagatedDeletion = false;
0132         splitter->setSizes(sizes);
0133         newSplitter->updateSizes();
0134     }
0135 }
0136 
0137 void ViewSplitter::addTerminalDisplay(TerminalDisplay *terminalDisplay, int index)
0138 {
0139     auto toplevelSplitter = getToplevelSplitter();
0140 
0141     if (index == -1)
0142         index = count();
0143 
0144     if (toplevelSplitter->count() == 2 && toplevelSplitter == qobject_cast<ViewSplitter *>(parent()) && toplevelSplitter->indexOf(terminalDisplay) != -1) {
0145         QVector<QWidget *> childWidgets;
0146 
0147         for (int i = 0; i < count(); ++i) {
0148             childWidgets.append(widget(i));
0149         }
0150 
0151         childWidgets.insert(index, terminalDisplay);
0152 
0153         for (auto child : childWidgets) {
0154             toplevelSplitter->addWidget(child);
0155         }
0156     } else {
0157         insertWidget(index, terminalDisplay);
0158     }
0159 
0160     updateSizes();
0161 }
0162 
0163 void ViewSplitter::addSplitter(ViewSplitter *splitter, int index)
0164 {
0165     if (index == -1)
0166         index = count();
0167 
0168     m_blockPropagatedDeletion = true;
0169 
0170     if (splitter->orientation() == orientation()) {
0171         QVector<QWidget *> children;
0172 
0173         for (int i = 0; i < splitter->count(); ++i) {
0174             children.append(splitter->widget(i));
0175         }
0176 
0177         for (int i = 0; i < children.count(); ++i) {
0178             insertWidget(index + i, children[i]);
0179         }
0180     } else {
0181         insertWidget(index, splitter);
0182     }
0183 
0184     m_blockPropagatedDeletion = false;
0185     updateSizes();
0186 }
0187 
0188 void ViewSplitter::childEvent(QChildEvent *event)
0189 {
0190     QSplitter::childEvent(event);
0191 
0192     if (event->removed()) {
0193         if (count() == 0) {
0194             auto *parent_splitter = qobject_cast<ViewSplitter *>(parent());
0195             if (parent_splitter != nullptr) {
0196                 setParent(nullptr);
0197             }
0198             deleteLater();
0199         } else if (count() == 1) {
0200             if (!m_blockPropagatedDeletion) {
0201                 auto *parent_splitter = qobject_cast<ViewSplitter *>(parent());
0202                 if (parent_splitter) {
0203                     parent_splitter->m_blockPropagatedDeletion = true;
0204 
0205                     // Force recalculation of sizes to take into account recent
0206                     // setVisible(true) calls due to a clearMaximize() upon
0207                     // sessionFinished. Without the refresh() call, the size of
0208                     // a previously hidden splitter could still be 0. The
0209                     // documentation of refresh() says "You should not need to
0210                     // call this function", but it seems we do.
0211                     parent_splitter->refresh();
0212 
0213                     const auto sizes = parent_splitter->sizes();
0214                     auto *wdg = widget(0);
0215                     const int oldContainerIndex = parent_splitter->indexOf(this);
0216                     parent_splitter->replaceWidget(oldContainerIndex, wdg);
0217                     parent_splitter->m_blockPropagatedDeletion = false;
0218                     parent_splitter->setSizes(sizes);
0219                     wdg->setFocus();
0220                     deleteLater();
0221                 }
0222             }
0223         }
0224     }
0225 
0226     auto terminals = getToplevelSplitter()->findChildren<TerminalDisplay *>();
0227     for (auto terminal : terminals) {
0228         terminal->headerBar()->applyVisibilitySettings();
0229     }
0230 }
0231 
0232 void ViewSplitter::handleFocusDirection(Qt::Orientation orientation, int direction)
0233 {
0234     auto terminalDisplay = activeTerminalDisplay();
0235     auto parentSplitter = qobject_cast<ViewSplitter *>(terminalDisplay->parentWidget());
0236     auto topSplitter = parentSplitter->getToplevelSplitter();
0237 
0238     // Find the theme's splitter width + extra space to find valid terminal
0239     // See https://bugs.kde.org/show_bug.cgi?id=411387 for more info
0240     const auto handleWidth = parentSplitter->handleWidth() + 3;
0241 
0242     const auto start = QPoint(terminalDisplay->x(), terminalDisplay->y());
0243     const auto startMapped = parentSplitter->mapTo(topSplitter, start);
0244 
0245     const int newX = orientation != Qt::Horizontal ? startMapped.x() + handleWidth
0246         : direction == 1                           ? startMapped.x() + terminalDisplay->width() + handleWidth
0247                                                    : startMapped.x() - handleWidth;
0248 
0249     const int newY = orientation != Qt::Vertical ? startMapped.y() + handleWidth
0250         : direction == 1                         ? startMapped.y() + terminalDisplay->height() + handleWidth
0251                                                  : startMapped.y() - handleWidth;
0252 
0253     const auto newPoint = QPoint(newX, newY);
0254     auto child = topSplitter->childAt(newPoint);
0255 
0256     TerminalDisplay *focusTerminal = nullptr;
0257     if (auto *terminal = qobject_cast<TerminalDisplay *>(child)) {
0258         focusTerminal = terminal;
0259     } else if (qobject_cast<QSplitterHandle *>(child) != nullptr) {
0260         auto targetSplitter = qobject_cast<QSplitter *>(child->parent());
0261         focusTerminal = qobject_cast<TerminalDisplay *>(targetSplitter->widget(0));
0262     } else if (qobject_cast<QWidget *>(child) != nullptr) {
0263         while (child != nullptr && focusTerminal == nullptr) {
0264             focusTerminal = qobject_cast<TerminalDisplay *>(child->parentWidget());
0265             child = child->parentWidget();
0266         }
0267     }
0268     if (focusTerminal != nullptr) {
0269         focusTerminal->setFocus(Qt::OtherFocusReason);
0270     }
0271 }
0272 
0273 void ViewSplitter::focusUp()
0274 {
0275     handleFocusDirection(Qt::Vertical, -1);
0276 }
0277 
0278 void ViewSplitter::focusDown()
0279 {
0280     handleFocusDirection(Qt::Vertical, +1);
0281 }
0282 
0283 void ViewSplitter::focusLeft()
0284 {
0285     handleFocusDirection(Qt::Horizontal, -1);
0286 }
0287 
0288 void ViewSplitter::focusRight()
0289 {
0290     handleFocusDirection(Qt::Horizontal, +1);
0291 }
0292 
0293 TerminalDisplay *ViewSplitter::activeTerminalDisplay() const
0294 {
0295     auto focusedWidget = focusWidget();
0296     auto focusedTerminalDisplay = qobject_cast<TerminalDisplay *>(focusedWidget);
0297 
0298     // TD's child can be focused - try to find parent.
0299     while (focusedTerminalDisplay == nullptr && focusedWidget != nullptr && focusedWidget != this) {
0300         focusedWidget = focusedWidget->parentWidget();
0301         focusedTerminalDisplay = qobject_cast<TerminalDisplay *>(focusedWidget);
0302     }
0303 
0304     return focusedTerminalDisplay != nullptr ? focusedTerminalDisplay : findChild<TerminalDisplay *>();
0305 }
0306 
0307 void ViewSplitter::toggleMaximizeCurrentTerminal()
0308 {
0309     m_terminalMaximized = !m_terminalMaximized;
0310     handleMinimizeMaximize(m_terminalMaximized, false);
0311 }
0312 
0313 void ViewSplitter::toggleZoomMaximizeCurrentTerminal()
0314 {
0315     m_terminalMaximized = !m_terminalMaximized;
0316     handleMinimizeMaximize(m_terminalMaximized, true);
0317 }
0318 
0319 namespace
0320 {
0321 void restoreAll(QList<TerminalDisplay *> &&terminalDisplays, QList<ViewSplitter *> &&splitters)
0322 {
0323     for (auto splitter : splitters) {
0324         splitter->setVisible(true);
0325     }
0326     for (auto terminalDisplay : terminalDisplays) {
0327         terminalDisplay->setVisible(true);
0328     }
0329 }
0330 }
0331 
0332 bool ViewSplitter::hideRecurse(TerminalDisplay *currentTerminalDisplay)
0333 {
0334     bool allHidden = true;
0335 
0336     for (int i = 0, end = count(); i < end; i++) {
0337         if (auto *maybeSplitter = qobject_cast<ViewSplitter *>(widget(i))) {
0338             allHidden = maybeSplitter->hideRecurse(currentTerminalDisplay) && allHidden;
0339             continue;
0340         }
0341         if (auto maybeTerminalDisplay = qobject_cast<TerminalDisplay *>(widget(i))) {
0342             if (maybeTerminalDisplay == currentTerminalDisplay) {
0343                 allHidden = false;
0344             } else {
0345                 maybeTerminalDisplay->setVisible(false);
0346             }
0347         }
0348     }
0349 
0350     if (allHidden) {
0351         setVisible(false);
0352     }
0353     return allHidden;
0354 }
0355 
0356 void ViewSplitter::handleMinimizeMaximize(bool maximize, bool zoom)
0357 {
0358     auto topLevelSplitter = getToplevelSplitter();
0359     auto currentTerminalDisplay = topLevelSplitter->activeTerminalDisplay();
0360     currentTerminalDisplay->setExpandedMode(maximize);
0361 
0362     auto currentTerminalFont = currentTerminalDisplay->terminalFont()->getVTFont();
0363 
0364     if (maximize) {
0365         if (zoom) {
0366             fontSizeBeforeMaximization = currentTerminalFont.pointSizeF();
0367             auto headerBar = currentTerminalDisplay->headerBar();
0368             auto headerBarHeight = headerBar->isVisible() ? headerBar->height() : 0;
0369             auto scrollBar = currentTerminalDisplay->scrollBar();
0370             auto scrollBarWidth = scrollBar->scrollBarPosition() != Enum::ScrollBarHidden ? scrollBar->width() : 0;
0371 
0372             // This is very inexact, which is fine, because the aspect ratio of
0373             // both the scaled font and the terminal can differ anyway.
0374             auto scaleFactor = std::min(
0375                 (topLevelSplitter->width() - scrollBarWidth)
0376                 / (currentTerminalDisplay->width() - scrollBarWidth),
0377 
0378                 (topLevelSplitter->height() - headerBarHeight)
0379                 / (currentTerminalDisplay->height() - headerBarHeight)
0380             );
0381             auto newSize = int(fontSizeBeforeMaximization * scaleFactor * 0.97);
0382 
0383             if (newSize > fontSizeBeforeMaximization) {
0384                 currentTerminalFont.setPointSizeF(newSize);
0385                 currentTerminalDisplay->terminalFont()->setVTFont(currentTerminalFont);
0386             }
0387         } else {
0388             fontSizeBeforeMaximization = 0;
0389         }
0390 
0391         for (int i = 0, end = topLevelSplitter->count(); i < end; i++) {
0392             auto widgetAt = topLevelSplitter->widget(i);
0393             if (auto *maybeSplitter = qobject_cast<ViewSplitter *>(widgetAt)) {
0394                 maybeSplitter->hideRecurse(currentTerminalDisplay);
0395             }
0396             if (auto maybeTerminalDisplay = qobject_cast<TerminalDisplay *>(widgetAt)) {
0397                 if (maybeTerminalDisplay != currentTerminalDisplay) {
0398                     maybeTerminalDisplay->setVisible(false);
0399                 }
0400             }
0401         }
0402     } else {
0403         // When restoring to un-maximzed state, it doesn't matter if this is done through
0404         // toggleMaximizeCurrentTerminal or toggleZoomMaximizeCurrentTerminal.
0405         // Only the method which was used to maximize, determines if the font size needs
0406         // to be restored.
0407         if (fontSizeBeforeMaximization) {
0408             currentTerminalFont.setPointSizeF(fontSizeBeforeMaximization);
0409             currentTerminalDisplay->terminalFont()->setVTFont(currentTerminalFont);
0410         }
0411 
0412         restoreAll(topLevelSplitter->findChildren<TerminalDisplay *>(), topLevelSplitter->findChildren<ViewSplitter *>());
0413     }
0414 }
0415 
0416 void ViewSplitter::clearMaximized()
0417 {
0418     ViewSplitter *top = getToplevelSplitter();
0419     Q_ASSERT(top);
0420     if (top->terminalMaximized()) {
0421         top->toggleMaximizeCurrentTerminal();
0422     }
0423 }
0424 
0425 ViewSplitter *ViewSplitter::getToplevelSplitter()
0426 {
0427     ViewSplitter *current = this;
0428     while (qobject_cast<ViewSplitter *>(current->parentWidget()) != nullptr) {
0429         current = qobject_cast<ViewSplitter *>(current->parentWidget());
0430     }
0431     return current;
0432 }
0433 
0434 ViewSplitter *ViewSplitter::getChildSplitter(int id)
0435 {
0436     for (auto childSplitter : findChildren<ViewSplitter *>()) {
0437         if (childSplitter->id() == id)
0438             return childSplitter;
0439     }
0440 
0441     return nullptr;
0442 }
0443 
0444 QString ViewSplitter::getChildWidgetsLayout()
0445 {
0446     QString layoutString;
0447 
0448     for (int i = 0; i < count(); ++i) {
0449         if (auto v = qobject_cast<TerminalDisplay *>(widget(i)))
0450             layoutString += QString::number(v->id());
0451         else if (auto s = qobject_cast<ViewSplitter *>(widget(i)))
0452             layoutString += s->getChildWidgetsLayout();
0453 
0454         layoutString += QLatin1Char('|');
0455     }
0456 
0457     layoutString.removeLast();
0458 
0459     if (orientation() == Qt::Orientation::Horizontal)
0460         layoutString = QLatin1Char('[') + layoutString + QLatin1Char(']');
0461     else
0462         layoutString = QLatin1Char('{') + layoutString + QLatin1Char('}');
0463 
0464     return QStringLiteral("(%1)").arg(id()) + layoutString;
0465 }
0466 
0467 namespace
0468 {
0469 TerminalDisplay *currentDragTarget = nullptr;
0470 }
0471 
0472 void Konsole::ViewSplitter::dragEnterEvent(QDragEnterEvent *ev)
0473 {
0474     const auto dragId = QStringLiteral("konsole/terminal_display");
0475     if (ev->mimeData()->hasFormat(dragId)) {
0476         auto other_pid = ev->mimeData()->data(dragId).toInt();
0477         // don't accept the drop if it's another instance of konsole
0478         if (qApp->applicationPid() != other_pid) {
0479             return;
0480         }
0481         if (getToplevelSplitter()->terminalMaximized()) {
0482             return;
0483         }
0484         ev->accept();
0485     }
0486 }
0487 
0488 void Konsole::ViewSplitter::dragMoveEvent(QDragMoveEvent *ev)
0489 {
0490     auto currentWidget = childAt(ev->position().toPoint());
0491     if (auto terminal = qobject_cast<TerminalDisplay *>(currentWidget)) {
0492         if ((currentDragTarget != nullptr) && currentDragTarget != terminal) {
0493             currentDragTarget->hideDragTarget();
0494         }
0495         if (terminal == ev->source()) {
0496             return;
0497         }
0498         currentDragTarget = terminal;
0499         auto localPos = currentDragTarget->mapFromParent(ev->position().toPoint());
0500         currentDragTarget->showDragTarget(localPos);
0501     }
0502 }
0503 
0504 void Konsole::ViewSplitter::dragLeaveEvent(QDragLeaveEvent *event)
0505 {
0506     Q_UNUSED(event)
0507     if (currentDragTarget != nullptr) {
0508         currentDragTarget->hideDragTarget();
0509         currentDragTarget = nullptr;
0510     }
0511 }
0512 
0513 void Konsole::ViewSplitter::dropEvent(QDropEvent *ev)
0514 {
0515     if (ev->mimeData()->hasFormat(QStringLiteral("konsole/terminal_display"))) {
0516         if (getToplevelSplitter()->terminalMaximized()) {
0517             return;
0518         }
0519         if (currentDragTarget != nullptr) {
0520             m_blockPropagatedDeletion = true;
0521 
0522             currentDragTarget->hideDragTarget();
0523             auto source = qobject_cast<TerminalDisplay *>(ev->source());
0524             source->setVisible(false);
0525             source->setParent(nullptr);
0526 
0527             currentDragTarget->setFocus(Qt::OtherFocusReason);
0528             const auto droppedEdge = currentDragTarget->droppedEdge();
0529 
0530             AddBehavior behavior = droppedEdge == Qt::LeftEdge || droppedEdge == Qt::TopEdge ? AddBehavior::AddBefore : AddBehavior::AddAfter;
0531 
0532             Qt::Orientation orientation = droppedEdge == Qt::LeftEdge || droppedEdge == Qt::RightEdge ? Qt::Horizontal : Qt::Vertical;
0533 
0534             // Add the display so it can be counted correctly by ViewManager
0535             addTerminalDisplay(source, orientation, behavior);
0536 
0537             // topLevel is the splitter that's connected with the ViewManager
0538             // that in turn can call the SessionController.
0539             Q_EMIT getToplevelSplitter()->terminalDisplayDropped(source);
0540             source->setVisible(true);
0541             currentDragTarget = nullptr;
0542 
0543             m_blockPropagatedDeletion = false;
0544         }
0545     }
0546 }
0547 
0548 void Konsole::ViewSplitter::showEvent(QShowEvent *)
0549 {
0550     // Fixes lost focus in background mode.
0551     setFocusProxy(activeSplitter()->activeTerminalDisplay());
0552 }
0553 
0554 QPoint Konsole::ViewSplitter::mapToTopLevel(const QPoint &p)
0555 {
0556     auto parentSplitter = qobject_cast<ViewSplitter *>(parent());
0557     if (parentSplitter) {
0558         auto next_pos = mapToParent(p);
0559         return parentSplitter->mapToTopLevel(next_pos);
0560     }
0561     return p;
0562 }
0563 
0564 QPoint Konsole::ViewSplitter::mapFromTopLevel(const QPoint &p)
0565 {
0566     auto parentSplitter = qobject_cast<ViewSplitter *>(parent());
0567     if (parentSplitter) {
0568         return mapFromParent(parentSplitter->mapFromTopLevel(p));
0569     }
0570     return p;
0571 }
0572 
0573 Konsole::ViewSplitterHandle::ViewSplitterHandle(Qt::Orientation orientation, QSplitter *parent)
0574     : QSplitterHandle(orientation, parent)
0575     , mouseReleaseEventCounter(0)
0576 {
0577 }
0578 
0579 QSplitterHandle *ViewSplitter::createHandle()
0580 {
0581     return new ViewSplitterHandle(orientation(), this);
0582 }
0583 
0584 namespace
0585 {
0586 QList<int> allSplitterSizes;
0587 
0588 int search_closest(const QList<int> &sorted_array, int x)
0589 {
0590     if (sorted_array.isEmpty()) {
0591         return -1;
0592     }
0593 
0594     auto iter_geq = std::lower_bound(sorted_array.begin(), sorted_array.end(), x);
0595 
0596     if (iter_geq == sorted_array.begin()) {
0597         return sorted_array[0];
0598     }
0599 
0600     int a = *(iter_geq - 1);
0601     int b = *(iter_geq);
0602 
0603     if (abs(x - a) < abs(x - b)) {
0604         return a;
0605     }
0606 
0607     return b;
0608 }
0609 }
0610 
0611 void Konsole::ViewSplitterHandle::mousePressEvent(QMouseEvent *ev)
0612 {
0613     auto parentSplitter = qobject_cast<ViewSplitter *>(parentWidget());
0614     auto topLevelSplitter = parentSplitter->getToplevelSplitter();
0615 
0616     QList<ViewSplitter *> splitters = topLevelSplitter->findChildren<ViewSplitter *>();
0617     splitters.append(topLevelSplitter);
0618 
0619     for (auto splitter : splitters) {
0620         if (splitter->orientation() != orientation()) {
0621             continue;
0622         }
0623 
0624         int delta = 0;
0625         for (auto point : splitter->sizes()) {
0626             delta += point;
0627             QPoint thisPoint = orientation() == Qt::Horizontal ? QPoint(delta, 0) : QPoint(0, delta);
0628 
0629             QPoint splitterPos = splitter->mapToTopLevel(thisPoint);
0630 
0631             const int ourPos = orientation() == Qt::Horizontal ? splitterPos.x() : splitterPos.y();
0632 
0633             allSplitterSizes.push_back(ourPos);
0634         }
0635     }
0636 
0637     std::sort(std::begin(allSplitterSizes), std::end(allSplitterSizes));
0638 
0639     QPoint thisPoint = parentSplitter->mapToTopLevel(mapToParent(ev->pos()));
0640     const int thisValue = search_closest(allSplitterSizes, orientation() == Qt::Horizontal ? thisPoint.x() : thisPoint.y());
0641     allSplitterSizes.removeOne(thisValue);
0642     { // context for the splitterSet temporary.
0643         auto splitterSet = QSet<int>(std::begin(allSplitterSizes), std::end(allSplitterSizes));
0644         allSplitterSizes = QList<int>(std::begin(splitterSet), std::end(splitterSet));
0645     }
0646     std::sort(std::begin(allSplitterSizes), std::end(allSplitterSizes));
0647 
0648     mouseReleaseEventCounter = 0;
0649 
0650     QSplitterHandle::mousePressEvent(ev);
0651 }
0652 
0653 void Konsole::ViewSplitterHandle::mouseReleaseEvent(QMouseEvent *ev)
0654 {
0655     allSplitterSizes.clear();
0656     if (++mouseReleaseEventCounter > 1) {
0657         mouseDoubleClickEvent(ev);
0658     }
0659     QSplitterHandle::mouseReleaseEvent(ev);
0660 }
0661 
0662 void Konsole::ViewSplitterHandle::mouseMoveEvent(QMouseEvent *ev)
0663 {
0664     ViewSplitter *parentSplitter = qobject_cast<ViewSplitter *>(parentWidget());
0665 
0666     QPoint thisPoint = parentSplitter->mapToTopLevel(mapToParent(ev->pos()));
0667 
0668     const int thisValue = orientation() == Qt::Horizontal ? thisPoint.x() : thisPoint.y();
0669     const int nearest = search_closest(allSplitterSizes, thisValue);
0670     const int threshould = qAbs(nearest - thisValue);
0671     if (threshould <= 20) {
0672         auto *thisSplitter = qobject_cast<ViewSplitter *>(splitter());
0673         QPoint localPoint = thisSplitter->mapFromTopLevel(orientation() == Qt::Horizontal ? QPoint(nearest, 0) : QPoint(0, nearest));
0674         moveSplitter(orientation() == Qt::Horizontal ? localPoint.x() : localPoint.y());
0675         return;
0676     }
0677 
0678     mouseReleaseEventCounter = 0;
0679 
0680     QSplitterHandle::mouseMoveEvent(ev);
0681 }
0682 
0683 void Konsole::ViewSplitterHandle::mouseDoubleClickEvent(QMouseEvent *ev)
0684 {
0685     auto parentSplitter = qobject_cast<ViewSplitter *>(parentWidget());
0686 
0687     if (parentSplitter->count() > 1) {
0688         for (int i = 1; i < parentSplitter->count(); i++) {
0689             if (parentSplitter->handle(i) != this) {
0690                 continue;
0691             }
0692             if (orientation() == Qt::Horizontal) {
0693                 moveSplitter(parentSplitter->widget(i - 1)->pos().x()
0694                              + ((parentSplitter->widget(i)->pos().x() + parentSplitter->widget(i)->width() - (parentSplitter->widget(i - 1)->pos().x())) / 2));
0695             } else {
0696                 moveSplitter(parentSplitter->widget(i - 1)->pos().y()
0697                              + ((parentSplitter->widget(i)->pos().y() + parentSplitter->widget(i)->height() - (parentSplitter->widget(i - 1)->pos().y())) / 2));
0698             }
0699             break;
0700         }
0701     }
0702 
0703     mouseReleaseEventCounter = 0;
0704     QSplitterHandle::mouseDoubleClickEvent(ev);
0705 }
0706 
0707 #include "moc_ViewSplitter.cpp"