File indexing completed on 2024-04-21 16:33:19

0001 /*
0002     SPDX-FileCopyrightText: 2000 Shie Erlich <krusader@users.sourceforge.net>
0003     SPDX-FileCopyrightText: 2000 Rafi Yanai <krusader@users.sourceforge.net>
0004     SPDX-FileCopyrightText: 2004-2022 Krusader Krew <https://krusader.org>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "krusaderview.h"
0010 
0011 // QtCore
0012 #include <QDebug>
0013 #include <QEvent>
0014 #include <QList>
0015 // QtGui
0016 #include <QClipboard>
0017 #include <QKeyEvent>
0018 // QtWidgets
0019 #include <QApplication>
0020 #include <QGridLayout>
0021 #include <QMenuBar>
0022 #include <QStatusBar>
0023 
0024 #include <KI18n/KLocalizedString>
0025 #include <KWidgetsAddons/KToggleAction>
0026 #include <KXmlGui/KToolBar>
0027 
0028 #include "Dialogs/percentalsplitter.h"
0029 #include "GUI/kcmdline.h"
0030 #include "GUI/kfnkeys.h"
0031 #include "GUI/profilemanager.h"
0032 #include "GUI/terminaldock.h"
0033 #include "Panel/listpanel.h"
0034 #include "Panel/panelfunc.h"
0035 #include "defaults.h"
0036 #include "icon.h"
0037 #include "kractions.h"
0038 #include "krservices.h"
0039 #include "krslots.h"
0040 #include "krusader.h"
0041 #include "panelmanager.h"
0042 
0043 KrusaderView::KrusaderView(QWidget *parent)
0044     : QWidget(parent)
0045     , activeMng(nullptr)
0046 {
0047 }
0048 
0049 void KrusaderView::start(const KConfigGroup &cfg, bool restoreSettings, const QList<QUrl> &leftTabs, const QList<QUrl> &rightTabs)
0050 {
0051     ////////////////////////////////
0052     // make a 1x1 mainLayout, it will auto-expand:
0053     mainLayout = new QGridLayout(this);
0054     mainLayout->setContentsMargins(0, 0, 0, 0);
0055     mainLayout->setSpacing(0);
0056     // vertical splitter
0057     vert_splitter = new QSplitter(this); // splits between panels and terminal/cmdline
0058     vert_splitter->setOrientation(Qt::Vertical);
0059     // horizontal splitter
0060     horiz_splitter = new PercentalSplitter(vert_splitter);
0061     (_terminalDock = new TerminalDock(vert_splitter, krApp))->hide(); // create it hidden
0062 
0063     // create a command line thing
0064     _cmdLine = new KCMDLine(this);
0065 
0066     // add a panel manager for each side of the splitter
0067     leftMng = createManager(true);
0068     rightMng = createManager(false);
0069     leftMng->setOtherManager(rightMng);
0070     rightMng->setOtherManager(leftMng);
0071 
0072     // make the left panel focused at program start
0073     activeMng = leftMng;
0074 
0075     // create the function keys widget
0076     _fnKeys = new KFnKeys(this, krApp);
0077     _fnKeys->hide();
0078     _fnKeys->setWhatsThis(
0079         i18n("Function keys allow performing fast "
0080              "operations on files."));
0081 
0082     // and insert the whole thing into the main layout... at last
0083     mainLayout->addWidget(vert_splitter, 0, 0); //<>
0084     mainLayout->addWidget(_cmdLine, 1, 0);
0085     mainLayout->addWidget(_fnKeys, 2, 0);
0086     mainLayout->activate();
0087 
0088     // get the last saved sizes of the splitter
0089 
0090     QList<int> lst = cfg.readEntry("Splitter Sizes", QList<int>());
0091     if (lst.count() != 2) {
0092         lst.clear();
0093         lst.push_back(100);
0094         lst.push_back(100);
0095     } else if (lst[0] < 1 && lst[1] < 1) {
0096         lst[0] = 100;
0097         lst[1] = 100;
0098     }
0099 
0100     horiz_splitter->setSizes(lst);
0101 
0102     verticalSplitterSizes = cfg.readEntry("Terminal Emulator Splitter Sizes", QList<int>());
0103     if (verticalSplitterSizes.count() != 2 || (verticalSplitterSizes[0] < 1 && verticalSplitterSizes[1] < 1)) {
0104         verticalSplitterSizes.clear();
0105         verticalSplitterSizes << 100 << 100;
0106     }
0107 
0108     leftPanel()->start(leftTabs.isEmpty() ? QUrl::fromLocalFile(QDir::homePath()) : leftTabs.at(0));
0109     rightPanel()->start(rightTabs.isEmpty() ? QUrl::fromLocalFile(QDir::homePath()) : rightTabs.at(0));
0110 
0111     activePanel()->gui->slotFocusOnMe(); // left starts out active
0112 
0113     for (int i = 1; i < leftTabs.count(); i++)
0114         leftMng->slotNewTab(leftTabs.at(i), false);
0115 
0116     for (int j = 1; j < rightTabs.count(); j++)
0117         rightMng->slotNewTab(rightTabs.at(j), false);
0118 
0119     // this is needed so that all tab labels get updated
0120     leftMng->layoutTabs();
0121     rightMng->layoutTabs();
0122 
0123     if (restoreSettings) {
0124         if (leftTabs.isEmpty())
0125             leftMng->loadSettings(KConfigGroup(&cfg, "Left Tab Bar"));
0126         if (rightTabs.isEmpty())
0127             rightMng->loadSettings(KConfigGroup(&cfg, "Right Tab Bar"));
0128         if (cfg.readEntry("Left Side Is Active", false))
0129             leftPanel()->slotFocusOnMe();
0130         else
0131             rightPanel()->slotFocusOnMe();
0132     }
0133 }
0134 
0135 void KrusaderView::updateGUI(const KConfigGroup &cfg)
0136 {
0137     if (!cfg.readEntry("Show Cmd Line", _ShowCmdline)) {
0138         cmdLine()->hide();
0139         KrActions::actToggleCmdline->setChecked(false);
0140     } else {
0141         cmdLine()->show();
0142         KrActions::actToggleCmdline->setChecked(true);
0143     }
0144     // update the Fn bar to the shortcuts selected by the user
0145     fnKeys()->updateShortcuts();
0146     if (!cfg.readEntry("Show FN Keys", _ShowFNkeys)) {
0147         fnKeys()->hide();
0148         KrActions::actToggleFnkeys->setChecked(false);
0149     } else {
0150         fnKeys()->show();
0151         KrActions::actToggleFnkeys->setChecked(true);
0152     }
0153     // set vertical mode
0154     if (cfg.readEntry("Vertical Mode", false)) {
0155         toggleVerticalMode();
0156     }
0157     if (cfg.readEntry("Show Terminal Emulator", _ShowTerminalEmulator)) {
0158         setTerminalEmulator(true); // create konsole_part
0159     };
0160 }
0161 
0162 void KrusaderView::setPanelSize(bool leftPanel, int percent)
0163 {
0164     QList<int> panelSizes = horiz_splitter->sizes();
0165     int totalSize = panelSizes[0] + panelSizes[1];
0166 
0167     if (leftPanel) {
0168         panelSizes[0] = totalSize * percent / 100;
0169         panelSizes[1] = totalSize * (100 - percent) / 100;
0170     } else { // == RIGHT_PANEL
0171         panelSizes[0] = totalSize * (100 - percent) / 100;
0172         panelSizes[1] = totalSize * percent / 100;
0173     }
0174 
0175     horiz_splitter->setSizes(panelSizes);
0176 }
0177 
0178 PanelManager *KrusaderView::createManager(bool left)
0179 {
0180     auto *panelManager = new PanelManager(horiz_splitter, krApp, left);
0181     connect(panelManager, &PanelManager::draggingTab, this, &KrusaderView::draggingTab);
0182     connect(panelManager, &PanelManager::draggingTabFinished, this, &KrusaderView::draggingTabFinished);
0183     connect(panelManager, &PanelManager::pathChanged, this, &KrusaderView::slotPathChanged);
0184     connect(panelManager, &PanelManager::setActiveManager, this, &KrusaderView::slotSetActiveManager);
0185 
0186     return panelManager;
0187 }
0188 
0189 void KrusaderView::updateCurrentActivePath()
0190 {
0191     const QString path = activePanel()->gui->lastLocalPath();
0192 
0193     _cmdLine->setCurrent(path);
0194     KConfigGroup cfg = krConfig->group("General");
0195     if (_terminalDock->isInitialised() && cfg.readEntry("Send CDs", _SendCDs)) {
0196         _terminalDock->sendCd(path);
0197     }
0198 }
0199 
0200 KrPanel *KrusaderView::activePanel() const
0201 {
0202     // active manager might not be set yet
0203     return activeMng ? activeMng->currentPanel() : nullptr;
0204 }
0205 
0206 ListPanel *KrusaderView::leftPanel() const
0207 {
0208     return leftMng->currentPanel()->gui;
0209 }
0210 
0211 ListPanel *KrusaderView::rightPanel() const
0212 {
0213     return rightMng->currentPanel()->gui;
0214 }
0215 
0216 // updates the command line whenever current panel or its path changes
0217 void KrusaderView::slotPathChanged(ListPanel *listPanel)
0218 {
0219     if (listPanel == activePanel()) {
0220         updateCurrentActivePath();
0221     }
0222 }
0223 
0224 int KrusaderView::getFocusCandidates(QVector<QWidget *> &widgets)
0225 {
0226     activePanel()->gui->getFocusCandidates(widgets);
0227     if (_terminalDock->isTerminalVisible())
0228         widgets << _terminalDock;
0229     if (_cmdLine->isVisible())
0230         widgets << _cmdLine;
0231 
0232     for (int i = 0; i < widgets.count(); i++) {
0233         if (widgets[i] == focusWidget() || widgets[i]->focusWidget() == focusWidget())
0234             return i;
0235     }
0236     return -1;
0237 }
0238 
0239 void KrusaderView::focusUp()
0240 {
0241     qDebug() << "focus UP";
0242     QVector<QWidget *> widgets;
0243     int currentFocus = getFocusCandidates(widgets);
0244 
0245     if (currentFocus < 0)
0246         return;
0247     currentFocus--;
0248 
0249     if (currentFocus >= 0 && currentFocus < widgets.count())
0250         widgets[currentFocus]->setFocus();
0251 }
0252 
0253 void KrusaderView::focusDown()
0254 {
0255     qDebug() << "focus DOWN";
0256     QVector<QWidget *> widgets;
0257     int currentFocus = getFocusCandidates(widgets);
0258 
0259     if (currentFocus < 0)
0260         return;
0261     currentFocus++;
0262 
0263     if (currentFocus < widgets.count())
0264         widgets[currentFocus]->setFocus();
0265 }
0266 
0267 void KrusaderView::cmdLineFocus() // command line receives keyboard focus
0268 {
0269     _cmdLine->setFocus();
0270 }
0271 
0272 void KrusaderView::cmdLineUnFocus() // return focus to the active panel
0273 {
0274     activePanel()->gui->slotFocusOnMe();
0275 }
0276 
0277 // Tab - switch focus
0278 void KrusaderView::panelSwitch()
0279 {
0280     activePanel()->otherPanel()->gui->slotFocusOnMe();
0281 }
0282 
0283 void KrusaderView::slotSetActiveManager(PanelManager *manager)
0284 {
0285     activeMng = manager;
0286     updateCurrentActivePath();
0287 }
0288 
0289 void KrusaderView::swapSides()
0290 {
0291     QList<int> lst = horiz_splitter->sizes();
0292 
0293     horiz_splitter->addWidget(leftMng);
0294 
0295     int old = lst[0];
0296     lst[0] = lst[1];
0297     lst[1] = old;
0298 
0299     horiz_splitter->setSizes(lst);
0300 
0301     PanelManager *tmpMng = leftMng;
0302     leftMng = rightMng;
0303     rightMng = tmpMng;
0304 
0305     leftMng->setLeft(true);
0306     rightMng->setLeft(false);
0307 
0308     leftPanel()->updateGeometry();
0309     rightPanel()->updateGeometry();
0310 }
0311 
0312 void KrusaderView::setTerminalEmulator(bool show, bool fullscreen)
0313 {
0314     qDebug() << "show=" << show << " fullscreen=" << fullscreen;
0315 
0316     static bool fnKeysShown = true; // first time init. should be overridden
0317     static bool cmdLineShown = true;
0318     static bool statusBarShown = true;
0319     static bool mainToolBarShown = true;
0320     static bool jobToolBarShown = true;
0321     static bool actionToolBarShown = true;
0322     static bool menuBarShown = true;
0323     static bool terminalEmulatorShown = true;
0324 
0325     if (show) {
0326         if (fullscreen) {
0327             // save what is shown
0328             fnKeysShown = !_fnKeys->isHidden();
0329             cmdLineShown = !_cmdLine->isHidden();
0330             statusBarShown = !krApp->statusBar()->isHidden();
0331             mainToolBarShown = !krApp->toolBar()->isHidden();
0332             jobToolBarShown = !krApp->toolBar("jobToolBar")->isHidden();
0333             actionToolBarShown = !krApp->toolBar("actionToolBar")->isHidden();
0334             menuBarShown = !krApp->menuBar()->isHidden();
0335             terminalEmulatorShown = _terminalDock->isTerminalVisible();
0336         }
0337 
0338         if (!_terminalDock->isTerminalVisible()) {
0339             // show terminal
0340             const bool isInitialized = _terminalDock->initialise();
0341             if (!isInitialized) {
0342                 _terminalDock->hide();
0343                 KrActions::actToggleTerminal->setChecked(false);
0344                 return;
0345             }
0346 
0347             _terminalDock->show();
0348             _terminalDock->setFocus();
0349             updateCurrentActivePath();
0350             KrActions::actToggleTerminal->setChecked(true);
0351         } else if (fullscreen) {
0352             // save current terminal size before going to fullscreen
0353             verticalSplitterSizes = vert_splitter->sizes();
0354         }
0355 
0356         if (fullscreen) {
0357             // hide everything else
0358             leftMng->hide();
0359             rightMng->hide();
0360             _fnKeys->hide();
0361             _cmdLine->hide();
0362             krApp->statusBar()->hide();
0363             krApp->toolBar()->hide();
0364             krApp->toolBar("jobToolBar")->hide();
0365             krApp->toolBar("actionToolBar")->hide();
0366             krApp->menuBar()->hide();
0367             // fix showing nothing if terminal is open but splitter widget size is zero
0368             vert_splitter->setSizes(QList<int>() << 0 << vert_splitter->height());
0369         } else {
0370             vert_splitter->setSizes(verticalSplitterSizes);
0371         }
0372     } else { // hide
0373         const bool isFullscreen = isTerminalEmulatorFullscreen();
0374         if (!(fullscreen && terminalEmulatorShown)) {
0375             // hide terminal emulator
0376             activePanel()->gui->slotFocusOnMe();
0377             if (_terminalDock->isTerminalVisible() && !isFullscreen) {
0378                 verticalSplitterSizes = vert_splitter->sizes();
0379             }
0380             _terminalDock->hide();
0381             KrActions::actToggleTerminal->setChecked(false);
0382         } else {
0383             // not fullscreen anymore but terminal is still visible
0384             vert_splitter->setSizes(verticalSplitterSizes);
0385         }
0386         if (isFullscreen) {
0387             // restore: unhide everything that was hidden before
0388             leftMng->show();
0389             rightMng->show();
0390             if (fnKeysShown)
0391                 _fnKeys->show();
0392             if (cmdLineShown)
0393                 _cmdLine->show();
0394             if (statusBarShown)
0395                 krApp->statusBar()->show();
0396             if (mainToolBarShown)
0397                 krApp->toolBar()->show();
0398             if (jobToolBarShown)
0399                 krApp->toolBar("jobToolBar")->show();
0400             if (actionToolBarShown)
0401                 krApp->toolBar("actionToolBar")->show();
0402             if (menuBarShown)
0403                 krApp->menuBar()->show();
0404         }
0405     }
0406 }
0407 
0408 void KrusaderView::focusTerminalEmulator()
0409 {
0410     if (_terminalDock->isTerminalVisible())
0411         _terminalDock->setFocus();
0412 }
0413 
0414 void KrusaderView::toggleFullScreenTerminalEmulator()
0415 {
0416     setTerminalEmulator(!isTerminalEmulatorFullscreen(), true);
0417 }
0418 
0419 bool KrusaderView::isTerminalEmulatorFullscreen()
0420 {
0421     return leftMng->isHidden() && rightMng->isHidden();
0422 }
0423 
0424 void KrusaderView::profiles(const QString &profileName)
0425 {
0426     ProfileManager profileManager("Panel", this);
0427     profileManager.hide();
0428     connect(&profileManager, &ProfileManager::saveToProfile, this, &KrusaderView::savePanelProfiles);
0429     connect(&profileManager, &ProfileManager::loadFromProfile, this, &KrusaderView::loadPanelProfiles);
0430     if (profileName.isEmpty())
0431         profileManager.profilePopup();
0432     else
0433         profileManager.loadProfile(profileName);
0434 }
0435 
0436 void KrusaderView::loadPanelProfiles(const QString &group)
0437 {
0438     KConfigGroup ldg(krConfig, group);
0439     leftMng->loadSettings(KConfigGroup(&ldg, "Left Tabs"));
0440     rightMng->loadSettings(KConfigGroup(&ldg, "Right Tabs"));
0441     if (ldg.readEntry("Left Side Is Active", true))
0442         leftPanel()->slotFocusOnMe();
0443     else
0444         rightPanel()->slotFocusOnMe();
0445 }
0446 
0447 void KrusaderView::savePanelProfiles(const QString &group)
0448 {
0449     KConfigGroup svr(krConfig, group);
0450 
0451     svr.writeEntry("Vertical Mode", isVertical());
0452     svr.writeEntry("Left Side Is Active", activePanel()->gui->isLeft());
0453     leftMng->saveSettings(KConfigGroup(&svr, "Left Tabs"), false);
0454     rightMng->saveSettings(KConfigGroup(&svr, "Right Tabs"), false);
0455 }
0456 
0457 void KrusaderView::toggleVerticalMode()
0458 {
0459     if (horiz_splitter->orientation() == Qt::Vertical) {
0460         horiz_splitter->setOrientation(Qt::Horizontal);
0461         KrActions::actVerticalMode->setText(i18n("Vertical Mode"));
0462         KrActions::actVerticalMode->setIcon(Icon("view-split-top-bottom"));
0463     } else {
0464         horiz_splitter->setOrientation(Qt::Vertical);
0465         KrActions::actVerticalMode->setText(i18n("Horizontal Mode"));
0466         KrActions::actVerticalMode->setIcon(Icon("view-split-left-right"));
0467     }
0468 }
0469 
0470 void KrusaderView::saveSettings(KConfigGroup &cfg)
0471 {
0472     QList<int> lst = horiz_splitter->sizes();
0473     cfg.writeEntry("Splitter Sizes", lst);
0474     QList<int> vertSplitterSizes = _terminalDock->isVisible()
0475             && !isTerminalEmulatorFullscreen()
0476             // fix sizes() not returning correct values on fullscreen+shutdown
0477             && vert_splitter->sizes().first() != 0
0478         ? vert_splitter->sizes()
0479         : verticalSplitterSizes;
0480     cfg.writeEntry("Terminal Emulator Splitter Sizes", vertSplitterSizes);
0481     cfg.writeEntry("Vertical Mode", isVertical());
0482     cfg.writeEntry("Left Side Is Active", activePanel()->gui->isLeft());
0483     leftMng->saveSettings(KConfigGroup(&cfg, "Left Tab Bar"), true);
0484     rightMng->saveSettings(KConfigGroup(&cfg, "Right Tab Bar"), true);
0485 }
0486 
0487 bool KrusaderView::cursorIsOnOtherSide(PanelManager *of, const QPoint &globalPos)
0488 {
0489     int border = -1;
0490     int pos = -1;
0491 
0492     if (horiz_splitter->orientation() == Qt::Horizontal) {
0493         pos = globalPos.x();
0494         if (of == leftMng)
0495             border = leftMng->mapToGlobal(QPoint(leftMng->width(), 0)).x();
0496         else
0497             border = rightMng->mapToGlobal(QPoint(0, 0)).x();
0498     } else {
0499         pos = globalPos.y();
0500         if (of == leftMng)
0501             border = leftMng->mapToGlobal(QPoint(0, leftMng->height())).y();
0502         else
0503             border = rightMng->mapToGlobal(QPoint(0, 0)).y();
0504     }
0505 
0506     return (of == leftMng) ? pos > border : pos < border;
0507 }
0508 
0509 void KrusaderView::draggingTab(PanelManager *from, QMouseEvent *e)
0510 {
0511     QString icon;
0512     if (horiz_splitter->orientation() == Qt::Horizontal)
0513         icon = (from == leftMng) ? "arrow-right" : "arrow-left";
0514     else
0515         icon = (from == leftMng) ? "arrow-down" : "arrow-up";
0516 
0517     QCursor cursor(Icon(icon).pixmap(22));
0518 
0519     if (cursorIsOnOtherSide(from, e->globalPos())) {
0520         if (!qApp->overrideCursor())
0521             qApp->setOverrideCursor(cursor);
0522     } else
0523         qApp->restoreOverrideCursor();
0524 }
0525 
0526 void KrusaderView::draggingTabFinished(PanelManager *from, QMouseEvent *e)
0527 {
0528     qApp->restoreOverrideCursor();
0529 
0530     if (cursorIsOnOtherSide(from, e->globalPos()))
0531         from->moveTabToOtherSide();
0532 }