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 }