File indexing completed on 2024-04-28 17:06:22
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 "listpanel.h" 0010 0011 // QtCore 0012 #include <QEvent> 0013 #include <QList> 0014 #include <QMimeData> 0015 #include <QRegExp> 0016 #include <QStringList> 0017 #include <QTimer> 0018 #include <QUrl> 0019 // QtGui 0020 #include <QBitmap> 0021 #include <QDrag> 0022 #include <QDropEvent> 0023 #include <QHideEvent> 0024 #include <QImage> 0025 #include <QKeyEvent> 0026 #include <QPixmap> 0027 #include <QShowEvent> 0028 // QtWidgets 0029 #include <QFrame> 0030 #include <QHBoxLayout> 0031 #include <QMenu> 0032 #include <QSplitter> 0033 #include <QTabBar> 0034 0035 #include <KCoreAddons/KUrlMimeData> 0036 #include <KI18n/KLocalizedString> 0037 #include <KIO/DropJob> 0038 #include <KIOFileWidgets/KFilePlacesModel> 0039 #include <KIOWidgets/KUrlComboBox> 0040 #include <KWidgetsAddons/KCursor> 0041 #include <KWidgetsAddons/KMessageBox> 0042 #include <utility> 0043 0044 #include "dirhistoryqueue.h" 0045 #include "krcolorcache.h" 0046 #include "krerrordisplay.h" 0047 #include "krlayoutfactory.h" 0048 #include "krpreviewpopup.h" 0049 #include "krsearchbar.h" 0050 #include "listpanelactions.h" 0051 #include "panelfunc.h" 0052 #include "sidebar.h" 0053 #include "viewactions.h" 0054 0055 #include "PanelView/krview.h" 0056 #include "PanelView/krviewfactory.h" 0057 #include "PanelView/krviewitem.h" 0058 0059 #include "../defaults.h" 0060 #include "../icon.h" 0061 #include "../krservices.h" 0062 #include "../krslots.h" 0063 #include "../krusader.h" 0064 #include "../krusaderview.h" 0065 0066 #include "../Archive/krarchandler.h" 0067 #include "../BookMan/krbookmarkbutton.h" 0068 #include "../Dialogs/krdialogs.h" 0069 #include "../Dialogs/krspwidgets.h" 0070 #include "../Dialogs/krsqueezedtextlabel.h" 0071 #include "../Dialogs/percentalsplitter.h" 0072 #include "../Dialogs/popularurls.h" 0073 #include "../FileSystem/fileitem.h" 0074 #include "../FileSystem/filesystem.h" 0075 #include "../FileSystem/krpermhandler.h" 0076 #include "../FileSystem/sizecalculator.h" 0077 #include "../GUI/dirhistorybutton.h" 0078 #include "../GUI/kcmdline.h" 0079 #include "../GUI/mediabutton.h" 0080 #include "../GUI/terminaldock.h" 0081 #include "../MountMan/kmountman.h" 0082 #include "../UserAction/useractionpopupmenu.h" 0083 0084 class ActionButton : public QToolButton 0085 { 0086 public: 0087 ActionButton(QWidget *parent, ListPanel *panel, QAction *action, const QString &text = QString()) 0088 : QToolButton(parent) 0089 , panel(panel) 0090 , action(action) 0091 { 0092 setText(text); 0093 setAutoRaise(true); 0094 if (KConfigGroup(krConfig, "ListPanelButtons").readEntry("Icons", false) || text.isEmpty()) 0095 setIcon(action->icon()); 0096 setToolTip(action->toolTip()); 0097 } 0098 0099 protected: 0100 void mousePressEvent(QMouseEvent *) override 0101 { 0102 panel->slotFocusOnMe(); 0103 action->trigger(); 0104 } 0105 0106 ListPanel *panel; 0107 QAction *action; 0108 }; 0109 0110 ///////////////////////////////////////////////////// 0111 // The list panel constructor // 0112 ///////////////////////////////////////////////////// 0113 ListPanel::ListPanel(QWidget *parent, AbstractPanelManager *manager, const KConfigGroup &cfg) 0114 : QWidget(parent) 0115 , KrPanel(manager, this, new ListPanelFunc(this)) 0116 , panelType(-1) 0117 , colorMask(255) 0118 , compareMode(false) 0119 , previewJob(nullptr) 0120 , inlineRefreshJob(nullptr) 0121 , searchBar(nullptr) 0122 , cdRootButton(nullptr) 0123 , cdUpButton(nullptr) 0124 , sidebarButton(nullptr) 0125 , sidebar(nullptr) 0126 , fileSystemError(nullptr) 0127 , _tabState(TabState::DEFAULT) 0128 { 0129 if (cfg.isValid()) 0130 panelType = cfg.readEntry("Type", -1); 0131 if (panelType == -1) 0132 panelType = defaultPanelType(); 0133 0134 _actions = krApp->listPanelActions(); 0135 0136 setAcceptDrops(true); 0137 0138 QHash<QString, QWidget *> widgets; 0139 0140 #define ADD_WIDGET(widget) widgets.insert(#widget, widget); 0141 0142 // media button 0143 mediaButton = new MediaButton(this); 0144 connect(mediaButton, &MediaButton::aboutToShow, this, [=]() { 0145 slotFocusOnMe(); 0146 }); 0147 connect(mediaButton, &MediaButton::openUrl, [=](const QUrl &_t1) { 0148 func->openUrl(_t1); 0149 }); 0150 connect(mediaButton, &MediaButton::newTab, this, [=](const QUrl &url) { 0151 duplicateTab(url); 0152 }); 0153 ADD_WIDGET(mediaButton); 0154 0155 // status bar 0156 status = new KrSqueezedTextLabel(this); 0157 KConfigGroup group(krConfig, "Look&Feel"); 0158 status->setFont(group.readEntry("Filelist Font", _FilelistFont)); 0159 status->setAutoFillBackground(false); 0160 status->setText(""); // needed for initialization code! 0161 status->setWhatsThis( 0162 i18n("The statusbar displays information about the filesystem " 0163 "which holds your current folder: total size, free space, " 0164 "type of filesystem, etc.")); 0165 ADD_WIDGET(status); 0166 0167 // back button 0168 backButton = new ActionButton(this, this, _actions->actHistoryBackward); 0169 ADD_WIDGET(backButton); 0170 0171 // forward button 0172 forwardButton = new ActionButton(this, this, _actions->actHistoryForward); 0173 ADD_WIDGET(forwardButton); 0174 0175 // ... create the history button 0176 historyButton = new DirHistoryButton(func->history, this); 0177 connect(historyButton, &DirHistoryButton::aboutToShow, this, [=]() { 0178 slotFocusOnMe(); 0179 }); 0180 connect(historyButton, &DirHistoryButton::gotoPos, func, &ListPanelFunc::historyGotoPos); 0181 ADD_WIDGET(historyButton); 0182 0183 // bookmarks button 0184 bookmarksButton = new KrBookmarkButton(this); 0185 connect(bookmarksButton, &KrBookmarkButton::aboutToShow, this, [=]() { 0186 slotFocusOnMe(); 0187 }); 0188 connect(bookmarksButton, &KrBookmarkButton::openUrl, this, [this](const QUrl &_t1) { 0189 func->openUrl(_t1); 0190 }); 0191 bookmarksButton->setWhatsThis( 0192 i18n("Open menu with bookmarks. You can also add " 0193 "current location to the list, edit bookmarks " 0194 "or add subfolder to the list.")); 0195 ADD_WIDGET(bookmarksButton); 0196 0197 // url input field 0198 urlNavigator = new KUrlNavigator(new KFilePlacesModel(this), QUrl(), this); 0199 urlNavigator->setWhatsThis( 0200 i18n("Name of folder where you are. You can also " 0201 "enter name of desired location to move there. " 0202 "Use of Net protocols like ftp or fish is possible.")); 0203 // handle certain key events here in event filter 0204 urlNavigator->editor()->installEventFilter(this); 0205 urlNavigator->setUrlEditable(isNavigatorEditModeSet()); 0206 urlNavigator->setShowFullPath(group.readEntry("Navigator Full Path", false)); 0207 connect(urlNavigator, &KUrlNavigator::returnPressed, this, [=]() { 0208 slotFocusOnMe(); 0209 }); 0210 connect(urlNavigator, &KUrlNavigator::urlChanged, this, &ListPanel::slotNavigatorUrlChanged); 0211 connect(urlNavigator->editor()->lineEdit(), &QLineEdit::editingFinished, this, &ListPanel::resetNavigatorMode); 0212 connect(urlNavigator, &KUrlNavigator::tabRequested, this, [=](const QUrl &url) { 0213 ListPanel::duplicateTab(url); 0214 }); 0215 connect(urlNavigator, &KUrlNavigator::urlsDropped, this, QOverload<const QUrl &, QDropEvent *>::of(&ListPanel::handleDrop)); 0216 ADD_WIDGET(urlNavigator); 0217 0218 // toolbar 0219 QWidget *toolbar = new QWidget(this); 0220 auto *toolbarLayout = new QHBoxLayout(toolbar); 0221 toolbarLayout->setContentsMargins(0, 0, 0, 0); 0222 toolbarLayout->setSpacing(0); 0223 ADD_WIDGET(toolbar); 0224 0225 fileSystemError = new KrErrorDisplay(this); 0226 fileSystemError->setWordWrap(true); 0227 fileSystemError->hide(); 0228 ADD_WIDGET(fileSystemError); 0229 0230 // client area 0231 clientArea = new QWidget(this); 0232 auto *clientLayout = new QVBoxLayout(clientArea); 0233 clientLayout->setSpacing(0); 0234 clientLayout->setContentsMargins(0, 0, 0, 0); 0235 ADD_WIDGET(clientArea); 0236 0237 // totals label 0238 totals = new KrSqueezedTextLabel(this); 0239 totals->setFont(group.readEntry("Filelist Font", _FilelistFont)); 0240 totals->setAutoFillBackground(false); 0241 totals->setWhatsThis( 0242 i18n("The totals bar shows how many files exist, " 0243 "how many selected and the bytes math")); 0244 ADD_WIDGET(totals); 0245 0246 // free space label 0247 freeSpace = new KrSqueezedTextLabel(this); 0248 freeSpace->setFont(group.readEntry("Filelist Font", _FilelistFont)); 0249 freeSpace->setAutoFillBackground(false); 0250 freeSpace->setText(""); 0251 freeSpace->setAlignment(Qt::AlignRight | Qt::AlignVCenter); 0252 ADD_WIDGET(freeSpace); 0253 0254 // progress indicator and cancel button for the quick calc size 0255 quickSizeCalcProgress = new QProgressBar(this); 0256 quickSizeCalcProgress->hide(); 0257 ADD_WIDGET(quickSizeCalcProgress); 0258 cancelQuickSizeCalcButton = new QToolButton(this); 0259 cancelQuickSizeCalcButton->hide(); 0260 cancelQuickSizeCalcButton->setIcon(Icon("dialog-cancel")); 0261 cancelQuickSizeCalcButton->setToolTip(i18n("Cancel folder space calculation")); 0262 ADD_WIDGET(cancelQuickSizeCalcButton); 0263 0264 // make sure to reserve enough vertical space for the progress indicator to become visible 0265 quickSizeCalcProgress->adjustSize(); 0266 cancelQuickSizeCalcButton->adjustSize(); 0267 totals->setMinimumHeight(std::max(quickSizeCalcProgress->height(), cancelQuickSizeCalcButton->height())); 0268 0269 // progress indicator for the preview job 0270 previewProgress = new QProgressBar(this); 0271 previewProgress->hide(); 0272 ADD_WIDGET(previewProgress); 0273 0274 // a cancel button for the filesystem refresh and preview job 0275 cancelProgressButton = new QToolButton(this); 0276 cancelProgressButton->hide(); 0277 cancelProgressButton->setIcon(Icon("dialog-cancel")); 0278 connect(cancelProgressButton, &QToolButton::clicked, this, &ListPanel::cancelProgress); 0279 ADD_WIDGET(cancelProgressButton); 0280 0281 // button for changing the panel sidebar position in the panel 0282 sidebarPositionButton = new QToolButton(this); 0283 sidebarPositionButton->hide(); 0284 sidebarPositionButton->setAutoRaise(true); 0285 sidebarPositionButton->setIcon(Icon("exchange-positions")); 0286 sidebarPositionButton->setToolTip(i18n("Move Sidebar clockwise")); 0287 connect(sidebarPositionButton, &QToolButton::clicked, this, [this]() { 0288 // moving position clockwise 0289 setSidebarPosition((sidebarPosition() + 1) % 4); 0290 }); 0291 ADD_WIDGET(sidebarPositionButton); 0292 0293 // a quick button to open the sidebar 0294 sidebarButton = new QToolButton(this); 0295 sidebarButton->setAutoRaise(true); 0296 sidebarButton->setIcon(Icon("arrow-up")); 0297 connect(sidebarButton, &QToolButton::clicked, this, &ListPanel::toggleSidebar); 0298 sidebarButton->setToolTip(i18n("Open the Sidebar")); 0299 ADD_WIDGET(sidebarButton); 0300 0301 #undef ADD_WIDGET 0302 0303 // toolbar buttons 0304 cdOtherButton = new ActionButton(toolbar, this, _actions->actCdToOther, "="); 0305 toolbarLayout->addWidget(cdOtherButton); 0306 0307 cdUpButton = new ActionButton(toolbar, this, _actions->actDirUp, ".."); 0308 toolbarLayout->addWidget(cdUpButton); 0309 0310 cdHomeButton = new ActionButton(toolbar, this, _actions->actHome, "~"); 0311 toolbarLayout->addWidget(cdHomeButton); 0312 0313 cdRootButton = new ActionButton(toolbar, this, _actions->actRoot, "/"); 0314 toolbarLayout->addWidget(cdRootButton); 0315 0316 // create the button for sync-browsing 0317 syncBrowseButton = new QToolButton(toolbar); 0318 syncBrowseButton->setIcon(Icon("kr_syncbrowse_off")); 0319 syncBrowseButton->setCheckable(true); 0320 0321 const QString syncBrowseText = i18n( 0322 "This button toggles the sync-browse mode.\n" 0323 "When active, each folder change is performed in the\n" 0324 "active and inactive panel - if possible."); 0325 syncBrowseButton->setText(syncBrowseText); 0326 syncBrowseButton->setToolTip(syncBrowseText); 0327 connect(syncBrowseButton, &QToolButton::toggled, syncBrowseButton, [this](bool checked) { 0328 syncBrowseButton->setIcon(Icon(checked ? "kr_syncbrowse_on" : "kr_syncbrowse_off")); 0329 }); 0330 syncBrowseButton->setAutoRaise(true); 0331 toolbarLayout->addWidget(syncBrowseButton); 0332 0333 setButtons(); 0334 0335 // create a splitter to hold the view and the sidebar 0336 sidebarSplitter = new PercentalSplitter(clientArea); 0337 sidebarSplitter->setChildrenCollapsible(true); 0338 sidebarSplitter->setOrientation(Qt::Horizontal); 0339 // expand vertical if splitter orientation is horizontal 0340 QSizePolicy sizePolicy = sidebarSplitter->sizePolicy(); 0341 sizePolicy.setVerticalPolicy(QSizePolicy::Expanding); 0342 sidebarSplitter->setSizePolicy(sizePolicy); 0343 clientLayout->addWidget(sidebarSplitter); 0344 0345 // view 0346 createView(); 0347 0348 // search (in folder) bar 0349 searchBar = new KrSearchBar(view, clientArea); 0350 searchBar->hide(); 0351 bool top = group.readEntry("Quicksearch Position", "bottom") == "top"; 0352 clientLayout->insertWidget(top ? 0 : -1, searchBar); 0353 0354 // create the layout 0355 KrLayoutFactory fact(this, widgets); 0356 QLayout *layout = fact.createLayout(); 0357 0358 if (!layout) { // fallback: create a layout by ourself 0359 auto *v = new QVBoxLayout; 0360 v->setContentsMargins(0, 0, 0, 0); 0361 v->setSpacing(0); 0362 0363 auto *h = new QHBoxLayout; 0364 h->setContentsMargins(0, 0, 0, 0); 0365 h->setSpacing(0); 0366 h->addWidget(urlNavigator); 0367 h->addWidget(toolbar); 0368 h->addStretch(); 0369 v->addLayout(h); 0370 0371 h = new QHBoxLayout; 0372 h->setContentsMargins(0, 0, 0, 0); 0373 h->setSpacing(0); 0374 h->addWidget(mediaButton); 0375 h->addWidget(status); 0376 h->addWidget(backButton); 0377 h->addWidget(forwardButton); 0378 h->addWidget(historyButton); 0379 h->addWidget(bookmarksButton); 0380 v->addLayout(h); 0381 0382 v->addWidget(fileSystemError); 0383 v->addWidget(clientArea); 0384 0385 h = new QHBoxLayout; 0386 h->setContentsMargins(0, 0, 0, 0); 0387 h->setSpacing(0); 0388 h->addWidget(totals); 0389 h->addWidget(freeSpace); 0390 h->addWidget(quickSizeCalcProgress); 0391 h->addWidget(cancelQuickSizeCalcButton); 0392 h->addWidget(previewProgress); 0393 h->addWidget(cancelProgressButton); 0394 h->addWidget(sidebarButton); 0395 v->addLayout(h); 0396 0397 layout = v; 0398 } 0399 0400 setLayout(layout); 0401 0402 connect(&KrColorCache::getColorCache(), &KrColorCache::colorsRefreshed, this, &ListPanel::slotRefreshColors); 0403 connect(krApp, &Krusader::shutdown, this, &ListPanel::cancelProgress); 0404 } 0405 0406 ListPanel::~ListPanel() 0407 { 0408 view->widget()->removeEventFilter(this); 0409 urlNavigator->editor()->removeEventFilter(this); 0410 cancelProgress(); 0411 delete view; 0412 view = nullptr; 0413 delete func; 0414 delete status; 0415 delete bookmarksButton; 0416 delete totals; 0417 delete urlNavigator; 0418 delete cdRootButton; 0419 delete cdHomeButton; 0420 delete cdUpButton; 0421 delete cdOtherButton; 0422 delete syncBrowseButton; 0423 // delete layout; 0424 } 0425 0426 void ListPanel::reparent(QWidget *parent, AbstractPanelManager *manager) 0427 { 0428 setParent(parent); 0429 _manager = manager; 0430 } 0431 0432 int ListPanel::defaultPanelType() 0433 { 0434 KConfigGroup group(krConfig, "Look&Feel"); 0435 return group.readEntry("Default Panel Type", KrViewFactory::defaultViewId()); 0436 } 0437 0438 bool ListPanel::isNavigatorEditModeSet() 0439 { 0440 KConfigGroup group(krConfig, "Look&Feel"); 0441 return group.readEntry("Navigator Edit Mode", false); 0442 } 0443 0444 void ListPanel::createView() 0445 { 0446 view = KrViewFactory::createView(panelType, sidebarSplitter, krConfig); 0447 view->init(); 0448 view->setMainWindow(krApp); 0449 0450 // KrViewFactory may create a different view type than requested 0451 panelType = view->instance()->id(); 0452 0453 if (this == ACTIVE_PANEL) 0454 view->prepareForActive(); 0455 else 0456 view->prepareForPassive(); 0457 view->refreshColors(); 0458 0459 sidebarSplitter->insertWidget(sidebarPosition() < 2 ? 1 : 0, view->widget()); 0460 0461 view->widget()->installEventFilter(this); 0462 0463 connect(view->op(), &KrViewOperator::quickCalcSpace, func, &ListPanelFunc::quickCalcSpace); 0464 connect(view->op(), &KrViewOperator::goHome, func, &ListPanelFunc::home); 0465 connect(view->op(), &KrViewOperator::dirUp, func, &ListPanelFunc::dirUp); 0466 connect(view->op(), &KrViewOperator::defaultDeleteFiles, func, &ListPanelFunc::defaultDeleteFiles); 0467 connect(view->op(), &KrViewOperator::middleButtonClicked, this, QOverload<KrViewItem *>::of(&ListPanel::duplicateTab)); 0468 connect(view->op(), &KrViewOperator::currentChanged, this, &ListPanel::slotCurrentChanged); 0469 connect(view->op(), &KrViewOperator::renameItem, func, QOverload<const QString &, const QString &>::of(&ListPanelFunc::rename)); 0470 connect(view->op(), &KrViewOperator::executed, func, &ListPanelFunc::execute); 0471 connect(view->op(), &KrViewOperator::goInside, func, &ListPanelFunc::goInside); 0472 connect(view->op(), &KrViewOperator::needFocus, this, [=]() { 0473 slotFocusOnMe(); 0474 }); 0475 connect(view->op(), &KrViewOperator::selectionChanged, this, &ListPanel::slotUpdateTotals); 0476 connect(view->op(), &KrViewOperator::itemDescription, krApp, &Krusader::statusBarUpdate); 0477 connect(view->op(), &KrViewOperator::contextMenu, this, &ListPanel::popRightClickMenu); 0478 connect(view->op(), &KrViewOperator::emptyContextMenu, this, &ListPanel::popEmptyRightClickMenu); 0479 connect(view->op(), &KrViewOperator::letsDrag, this, &ListPanel::startDragging); 0480 connect(view->op(), &KrViewOperator::gotDrop, this, [this](QDropEvent *event) { 0481 handleDrop(event, true); 0482 }); 0483 connect(view->op(), &KrViewOperator::previewJobStarted, this, &ListPanel::slotPreviewJobStarted); 0484 connect(view->op(), &KrViewOperator::refreshActions, krApp->viewActions(), &ViewActions::refreshActions); 0485 connect(view->op(), &KrViewOperator::currentChanged, func->history, &DirHistoryQueue::saveCurrentItem); 0486 connect(view->op(), &KrViewOperator::goBack, func, &ListPanelFunc::historyBackward); 0487 connect(view->op(), &KrViewOperator::goForward, func, &ListPanelFunc::historyForward); 0488 0489 view->setFiles(func->files()); 0490 0491 func->refreshActions(); 0492 } 0493 0494 void ListPanel::changeType(int type) 0495 { 0496 if (panelType != type) { 0497 QString current = view->getCurrentItem(); 0498 QList<QUrl> selection = view->selectedUrls(); 0499 bool filterApplysToDirs = view->properties()->filterApplysToDirs; 0500 KrViewProperties::FilterSpec filter = view->filter(); 0501 FilterSettings filterSettings = view->properties()->filterSettings; 0502 0503 panelType = type; 0504 0505 KrView *oldView = view; 0506 createView(); 0507 searchBar->setView(view); 0508 delete oldView; 0509 0510 view->setFilter(filter, filterSettings, filterApplysToDirs); 0511 view->setSelectionUrls(selection); 0512 view->setCurrentItem(current); 0513 view->makeItemVisible(view->getCurrentKrViewItem()); 0514 } 0515 } 0516 0517 int ListPanel::getProperties() 0518 { 0519 int props = 0; 0520 if (syncBrowseButton->isChecked()) { 0521 props |= PROP_SYNC_BUTTON_ON; 0522 } 0523 if (isLocked()) { 0524 props |= PROP_LOCKED; 0525 } else if (isPinned()) { 0526 props |= PROP_PINNED; 0527 } 0528 return props; 0529 } 0530 0531 void ListPanel::setProperties(int prop) 0532 { 0533 syncBrowseButton->setChecked(prop & PROP_SYNC_BUTTON_ON); 0534 if (prop & PROP_LOCKED) { 0535 _tabState = TabState::LOCKED; 0536 } else if (prop & PROP_PINNED) { 0537 _tabState = TabState::PINNED; 0538 } else { 0539 _tabState = TabState::DEFAULT; 0540 } 0541 } 0542 0543 bool ListPanel::eventFilter(QObject *watched, QEvent *e) 0544 { 0545 if (view && watched == view->widget()) { 0546 if (e->type() == QEvent::FocusIn && this != ACTIVE_PANEL && !isHidden()) 0547 slotFocusOnMe(); 0548 else if (e->type() == QEvent::ShortcutOverride) { 0549 auto *ke = dynamic_cast<QKeyEvent *>(e); 0550 if (ke->key() == Qt::Key_Escape && ke->modifiers() == Qt::NoModifier) { 0551 // if the cancel refresh action has no shortcut assigned, 0552 // we need this event ourselves to cancel refresh 0553 if (_actions->actCancelRefresh->shortcut().isEmpty()) { 0554 e->accept(); 0555 return true; 0556 } 0557 } 0558 } 0559 } 0560 // handle URL navigator key events 0561 else if (urlNavigator && watched == urlNavigator->editor()) { 0562 // override default shortcut for panel focus 0563 if (e->type() == QEvent::ShortcutOverride) { 0564 auto *ke = dynamic_cast<QKeyEvent *>(e); 0565 if ((ke->key() == Qt::Key_Escape) && (ke->modifiers() == Qt::NoModifier)) { 0566 e->accept(); // we will get the key press event now 0567 return true; 0568 } 0569 } else if (e->type() == QEvent::KeyPress) { 0570 auto *ke = dynamic_cast<QKeyEvent *>(e); 0571 if ((ke->key() == Qt::Key_Down) && (ke->modifiers() == Qt::ControlModifier)) { 0572 slotFocusOnMe(); 0573 return true; 0574 } else if ((ke->key() == Qt::Key_Escape) && (ke->modifiers() == Qt::NoModifier)) { 0575 // reset navigator 0576 urlNavigator->editor()->setUrl(urlNavigator->locationUrl()); 0577 slotFocusOnMe(); 0578 return true; 0579 } 0580 } 0581 } 0582 return false; 0583 } 0584 0585 void ListPanel::toggleSidebar() 0586 { 0587 if (!sidebar) { 0588 sidebar = new Sidebar(sidebarSplitter); 0589 // fix vertical grow of splitter (and entire window) if its content 0590 // demands more space 0591 QSizePolicy sizePolicy = sidebar->sizePolicy(); 0592 sizePolicy.setVerticalPolicy(QSizePolicy::Ignored); 0593 sidebar->setSizePolicy(sizePolicy); 0594 connect(this, &ListPanel::pathChanged, sidebar, &Sidebar::onPanelPathChange); 0595 connect(sidebar, &Sidebar::urlActivated, SLOTS, &KrSlots::refresh); 0596 sidebarSplitter->insertWidget(0, sidebar); 0597 } 0598 0599 if (sidebar->isHidden()) { 0600 if (sidebarSplitterSizes.count() > 0) { 0601 sidebarSplitter->setSizes(sidebarSplitterSizes); 0602 } else { // on the first time, resize to 50% 0603 QList<int> lst; 0604 lst << height() / 2 << height() / 2; 0605 sidebarSplitter->setSizes(lst); 0606 } 0607 0608 sidebar->show(); 0609 sidebarButton->setIcon(Icon("arrow-down")); 0610 sidebarButton->setToolTip(i18n("Close the Sidebar")); 0611 sidebarPositionButton->show(); 0612 } else { 0613 sidebarSplitterSizes.clear(); 0614 sidebarSplitterSizes = sidebarSplitter->sizes(); 0615 sidebar->hide(); 0616 sidebarButton->setIcon(Icon("arrow-up")); 0617 sidebarButton->setToolTip(i18n("Open the Sidebar")); 0618 sidebarPositionButton->hide(); 0619 0620 QList<int> lst; 0621 lst << height() << 0; 0622 sidebarSplitter->setSizes(lst); 0623 if (ACTIVE_PANEL) 0624 ACTIVE_PANEL->gui->slotFocusOnMe(); 0625 } 0626 } 0627 0628 QString ListPanel::lastLocalPath() const 0629 { 0630 return _lastLocalPath; 0631 } 0632 0633 void ListPanel::setButtons() 0634 { 0635 KConfigGroup group(krConfig, "Look&Feel"); 0636 0637 mediaButton->setVisible(group.readEntry("Media Button Visible", true)); 0638 backButton->setVisible(group.readEntry("Back Button Visible", false)); 0639 forwardButton->setVisible(group.readEntry("Forward Button Visible", false)); 0640 historyButton->setVisible(group.readEntry("History Button Visible", true)); 0641 bookmarksButton->setVisible(group.readEntry("Bookmarks Button Visible", true)); 0642 0643 if (group.readEntry("Panel Toolbar visible", _PanelToolBar)) { 0644 cdRootButton->setVisible(group.readEntry("Root Button Visible", _cdRoot)); 0645 cdHomeButton->setVisible(group.readEntry("Home Button Visible", _cdHome)); 0646 cdUpButton->setVisible(group.readEntry("Up Button Visible", _cdUp)); 0647 cdOtherButton->setVisible(group.readEntry("Equal Button Visible", _cdOther)); 0648 syncBrowseButton->setVisible(group.readEntry("SyncBrowse Button Visible", _syncBrowseButton)); 0649 } else { 0650 cdRootButton->hide(); 0651 cdHomeButton->hide(); 0652 cdUpButton->hide(); 0653 cdOtherButton->hide(); 0654 syncBrowseButton->hide(); 0655 } 0656 } 0657 0658 void ListPanel::slotUpdateTotals() 0659 { 0660 totals->setText(view->statistics()); 0661 } 0662 0663 void ListPanel::compareDirs(bool otherPanelToo) 0664 { 0665 // Performs a check in order to avoid that the next code is executed twice 0666 if (otherPanelToo == true) { 0667 // If both panels are showing the same directory 0668 if (_manager->currentPanel()->virtualPath() == otherPanel()->virtualPath()) { 0669 if (KMessageBox::warningContinueCancel(this, i18n("Warning: The left and the right side are showing the same folder.")) != KMessageBox::Continue) { 0670 return; 0671 } 0672 } 0673 } 0674 0675 KConfigGroup pg(krConfig, "Private"); 0676 int compareMode = pg.readEntry("Compare Mode", 0); 0677 KConfigGroup group(krConfig, "Look&Feel"); 0678 bool selectDirs = group.readEntry("Mark Dirs", false); 0679 0680 KrViewItem *item, *otherItem; 0681 0682 for (item = view->getFirst(); item != nullptr; item = view->getNext(item)) { 0683 if (item->name() == "..") 0684 continue; 0685 0686 for (otherItem = otherPanel()->view->getFirst(); otherItem != nullptr && otherItem->name() != item->name(); 0687 otherItem = otherPanel()->view->getNext(otherItem)) 0688 ; 0689 0690 bool isSingle = (otherItem == nullptr), isDifferent = false, isNewer = false; 0691 0692 if (func->getFileItem(item)->isDir() && !selectDirs) { 0693 item->setSelected(false); 0694 continue; 0695 } 0696 0697 if (otherItem) { 0698 if (!func->getFileItem(item)->isDir()) 0699 isDifferent = otherPanel()->func->getFileItem(otherItem)->getSize() != func->getFileItem(item)->getSize(); 0700 isNewer = func->getFileItem(item)->getModificationTime() > otherPanel()->func->getFileItem(otherItem)->getModificationTime(); 0701 } 0702 0703 switch (compareMode) { 0704 case 0: 0705 item->setSelected(isNewer || isSingle); 0706 break; 0707 case 1: 0708 item->setSelected(isNewer); 0709 break; 0710 case 2: 0711 item->setSelected(isSingle); 0712 break; 0713 case 3: 0714 item->setSelected(isDifferent || isSingle); 0715 break; 0716 case 4: 0717 item->setSelected(isDifferent); 0718 break; 0719 } 0720 } 0721 0722 view->updateView(); 0723 0724 if (otherPanelToo) 0725 otherPanel()->gui->compareDirs(false); 0726 } 0727 0728 void ListPanel::slotRefreshColors() 0729 { 0730 view->refreshColors(); 0731 emit signalRefreshColors(this == ACTIVE_PANEL); 0732 } 0733 0734 void ListPanel::slotFocusOnMe(bool focus) 0735 { 0736 if (focus && _manager->currentPanel() != this) { 0737 // ignore focus request if this panel is not shown 0738 return; 0739 } 0740 0741 krApp->setUpdatesEnabled(false); 0742 0743 if (focus) { 0744 emit activate(); 0745 _actions->activePanelChanged(); 0746 func->refreshActions(); 0747 slotCurrentChanged(view->getCurrentKrViewItem()); 0748 view->prepareForActive(); 0749 otherPanel()->gui->slotFocusOnMe(false); 0750 } else { 0751 // in case a new url was entered but not refreshed to, 0752 // reset url navigator to the current url 0753 setNavigatorUrl(virtualPath()); 0754 view->prepareForPassive(); 0755 } 0756 0757 urlNavigator->setActive(focus); 0758 slotRefreshColors(); 0759 0760 krApp->setUpdatesEnabled(true); 0761 } 0762 0763 // this is used to start the panel 0764 ////////////////////////////////////////////////////////////////// 0765 void ListPanel::start(const QUrl &url) 0766 { 0767 QUrl startUrl(url); 0768 0769 if (!startUrl.isValid()) 0770 startUrl = QUrl::fromLocalFile(ROOT_DIR); 0771 0772 _lastLocalPath = startUrl.isLocalFile() ? startUrl.path() : ROOT_DIR; 0773 0774 func->openUrl(startUrl); 0775 0776 setJumpBack(startUrl); 0777 } 0778 0779 void ListPanel::slotStartUpdate(bool directoryChange) 0780 { 0781 if (inlineRefreshJob) 0782 inlineRefreshListResult(nullptr); 0783 0784 setCursor(Qt::BusyCursor); 0785 0786 const QUrl currentUrl = virtualPath(); 0787 if (directoryChange) { 0788 if (this == ACTIVE_PANEL && !MAIN_VIEW->terminalDock()->hasFocus()) { 0789 slotFocusOnMe(); 0790 } 0791 0792 if (currentUrl.isLocalFile()) 0793 _lastLocalPath = currentUrl.path(); 0794 0795 setNavigatorUrl(currentUrl); 0796 0797 emit pathChanged(currentUrl); 0798 0799 krApp->popularUrls()->addUrl(currentUrl); 0800 0801 searchBar->hideBar(); 0802 } 0803 0804 if (compareMode) 0805 otherPanel()->view->refresh(); 0806 0807 // return cursor to normal arrow 0808 setCursor(Qt::ArrowCursor); 0809 0810 slotUpdateTotals(); 0811 } 0812 0813 void ListPanel::updateFilesystemStats(const QString &metaInfo, const QString &fsType, KIO::filesize_t total, KIO::filesize_t free) 0814 { 0815 QString statusText, mountPoint, freeSpaceText; 0816 0817 if (!metaInfo.isEmpty()) { 0818 statusText = metaInfo; 0819 mountPoint = freeSpaceText = ""; 0820 } else { 0821 const int perc = total == 0 ? 0 : (int)(((float)free / (float)total) * 100.0); 0822 mountPoint = func->files()->mountPoint(); 0823 statusText = i18nc( 0824 "%1=free space,%2=total space,%3=percentage of usage, " 0825 "%4=mountpoint,%5=filesystem type", 0826 "%1 free out of %2 (%3%) on %4 [(%5)]", 0827 KIO::convertSize(free), 0828 KIO::convertSize(total), 0829 perc, 0830 mountPoint, 0831 fsType); 0832 0833 freeSpaceText = " " + i18n("%1 free", KIO::convertSize(free)); 0834 } 0835 0836 status->setText(statusText); 0837 freeSpace->setText(freeSpaceText); 0838 mediaButton->updateIcon(mountPoint); 0839 } 0840 0841 void ListPanel::handleDrop(QDropEvent *event, bool onView) 0842 { 0843 // check what was dropped 0844 const QList<QUrl> urls = KUrlMimeData::urlsFromMimeData(event->mimeData()); 0845 if (urls.isEmpty()) { 0846 event->ignore(); // not for us to handle! 0847 return; 0848 } 0849 0850 // find dropping destination 0851 QString destinationDir = ""; 0852 const bool dragFromThisPanel = event->source() == this; 0853 const KrViewItem *item = onView ? view->getKrViewItemAt(event->pos()) : nullptr; 0854 if (item) { 0855 const FileItem *file = item->getFileItem(); 0856 if (file && !file->isDir() && dragFromThisPanel) { 0857 event->ignore(); // dragging on files in same panel, ignore 0858 return; 0859 } else if (!file || file->isDir()) { // item is ".." dummy or a directory 0860 destinationDir = item->name(); 0861 } 0862 } else if (dragFromThisPanel) { 0863 event->ignore(); // dragged from this panel onto an empty spot in this panel, ignore 0864 return; 0865 } 0866 0867 QUrl destination = QUrl(virtualPath()); 0868 destination.setPath(destination.path() + '/' + destinationDir); 0869 0870 func->files()->dropFiles(destination, event); 0871 0872 if (KConfigGroup(krConfig, "Look&Feel").readEntry("UnselectBeforeOperation", _UnselectBeforeOperation)) { 0873 KrPanel *p = dragFromThisPanel ? this : otherPanel(); 0874 p->view->saveSelection(); 0875 p->view->unselectAll(); 0876 } 0877 } 0878 0879 void ListPanel::handleDrop(const QUrl &destination, QDropEvent *event) 0880 { 0881 func->files()->dropFiles(destination, event); 0882 } 0883 0884 void ListPanel::startDragging(const QStringList &names, const QPixmap &px) 0885 { 0886 if (names.isEmpty()) { // avoid dragging empty urls 0887 return; 0888 } 0889 0890 QList<QUrl> urls = func->files()->getUrls(names); 0891 0892 auto *drag = new QDrag(this); 0893 auto *mimeData = new QMimeData; 0894 drag->setPixmap(px); 0895 mimeData->setUrls(urls); 0896 drag->setMimeData(mimeData); 0897 0898 drag->exec(Qt::MoveAction | Qt::CopyAction | Qt::LinkAction); 0899 } 0900 0901 // pops a right-click menu for items 0902 void ListPanel::popRightClickMenu(const QPoint &loc) 0903 { 0904 // run it, on the mouse location 0905 int j = QFontMetrics(font()).height() * 2; 0906 auto menu = PanelContextMenu::run(QPoint(loc.x() + 5, loc.y() + j), this); 0907 _contextMenu.reset(menu); 0908 } 0909 0910 void ListPanel::popEmptyRightClickMenu(const QPoint &loc) 0911 { 0912 auto menu = PanelContextMenu::run(loc, this); 0913 _contextMenu.reset(menu); 0914 } 0915 0916 QString ListPanel::getCurrentName() const 0917 { 0918 const QString name = view->getCurrentItem(); 0919 return name == ".." ? QString() : name; 0920 } 0921 0922 QStringList ListPanel::getSelectedNames() 0923 { 0924 QStringList fileNames; 0925 view->getSelectedItems(&fileNames); 0926 return fileNames; 0927 } 0928 0929 void ListPanel::prepareToDelete() 0930 { 0931 const bool skipCurrent = (view->numSelected() == 0); 0932 view->setNameToMakeCurrent(view->firstUnmarkedBelowCurrent(skipCurrent)); 0933 } 0934 0935 void ListPanel::keyPressEvent(QKeyEvent *e) 0936 { 0937 switch (e->key()) { 0938 case Qt::Key_Enter: 0939 case Qt::Key_Return: 0940 if (e->modifiers() & Qt::ControlModifier) { 0941 if (e->modifiers() & Qt::AltModifier) { 0942 FileItem *fileitem = func->files()->getFileItem(view->getCurrentKrViewItem()->name()); 0943 if (fileitem && fileitem->isDir()) 0944 duplicateTab(fileitem->getUrl(), true); 0945 } else { 0946 SLOTS->insertFileName((e->modifiers() & Qt::ShiftModifier) != 0); 0947 } 0948 } else { 0949 e->ignore(); 0950 } 0951 break; 0952 case Qt::Key_Right: 0953 case Qt::Key_Left: 0954 if (e->modifiers() == Qt::ControlModifier) { 0955 // user pressed CTRL+Right/Left - refresh other panel to the selected path if it's a 0956 // directory otherwise as this one 0957 if ((isLeft() && e->key() == Qt::Key_Right) || (!isLeft() && e->key() == Qt::Key_Left)) { 0958 if (KrViewItem *it = view->getCurrentKrViewItem()) { 0959 QUrl newPath; 0960 if (it->name() == "..") { 0961 newPath = KIO::upUrl(virtualPath()); 0962 } else { 0963 FileItem *v = func->getFileItem(it); 0964 // If it's a directory different from ".." 0965 if (v && v->isDir() && v->getName() != "..") { 0966 newPath = v->getUrl(); 0967 } else { 0968 // If it's a supported compressed file 0969 if (v && KrArcHandler::arcSupported(v->getMime())) { 0970 newPath = func->browsableArchivePath(v->getUrl().fileName()); 0971 } else { 0972 newPath = virtualPath(); 0973 } 0974 } 0975 } 0976 otherPanel()->func->openUrl(newPath); 0977 } 0978 } else { 0979 func->openUrl(otherPanel()->virtualPath()); 0980 } 0981 return; 0982 } else 0983 e->ignore(); 0984 break; 0985 case Qt::Key_Down: 0986 if (e->modifiers() == Qt::ControlModifier) { // give the keyboard focus to the command line 0987 if (MAIN_VIEW->cmdLine()->isVisible()) 0988 MAIN_VIEW->cmdLineFocus(); 0989 else 0990 MAIN_VIEW->focusTerminalEmulator(); 0991 return; 0992 } else if (e->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) { // give the keyboard focus to TE 0993 MAIN_VIEW->focusTerminalEmulator(); 0994 } else 0995 e->ignore(); 0996 break; 0997 0998 case Qt::Key_Up: 0999 if (e->modifiers() == Qt::ControlModifier) { // give the keyboard focus to the url navigator 1000 editLocation(); 1001 return; 1002 } else 1003 e->ignore(); 1004 break; 1005 1006 case Qt::Key_Escape: 1007 cancelProgress(); 1008 break; 1009 1010 default: 1011 // if we got this, it means that the view is not doing 1012 // the quick search thing, so send the characters to the commandline, if normal key 1013 if (e->modifiers() == Qt::NoModifier) 1014 MAIN_VIEW->cmdLine()->addText(e->text()); 1015 1016 // e->ignore(); 1017 } 1018 } 1019 1020 void ListPanel::showEvent(QShowEvent *e) 1021 { 1022 panelVisible(); 1023 QWidget::showEvent(e); 1024 } 1025 1026 void ListPanel::hideEvent(QHideEvent *e) 1027 { 1028 panelHidden(); 1029 QWidget::hideEvent(e); 1030 } 1031 1032 void ListPanel::panelVisible() 1033 { 1034 func->setPaused(false); 1035 } 1036 1037 void ListPanel::panelHidden() 1038 { 1039 func->setPaused(true); 1040 } 1041 1042 void ListPanel::slotPreviewJobStarted(KJob *job) 1043 { 1044 previewJob = job; 1045 connect(job, SIGNAL(percent(KJob *, ulong)), SLOT(slotPreviewJobPercent(KJob *, ulong))); 1046 connect(job, &KJob::result, this, &ListPanel::slotPreviewJobResult); 1047 cancelProgressButton->setMaximumHeight(sidebarButton->height()); 1048 cancelProgressButton->show(); 1049 previewProgress->setValue(0); 1050 previewProgress->setFormat(i18n("loading previews: %p%")); 1051 previewProgress->setMaximumHeight(cancelProgressButton->height()); 1052 previewProgress->show(); 1053 } 1054 1055 void ListPanel::slotPreviewJobPercent(KJob * /*job*/, unsigned long percent) 1056 { 1057 previewProgress->setValue(static_cast<int>(percent)); 1058 } 1059 1060 void ListPanel::slotPreviewJobResult(KJob * /*job*/) 1061 { 1062 previewJob = nullptr; 1063 previewProgress->hide(); 1064 if (!inlineRefreshJob) 1065 cancelProgressButton->hide(); 1066 } 1067 1068 void ListPanel::slotRefreshJobStarted(KIO::Job *job) 1069 { 1070 // disable the parts of the panel we don't want touched 1071 status->setEnabled(false); 1072 urlNavigator->setEnabled(false); 1073 cdRootButton->setEnabled(false); 1074 cdHomeButton->setEnabled(false); 1075 cdUpButton->setEnabled(false); 1076 cdOtherButton->setEnabled(false); 1077 sidebarButton->setEnabled(false); 1078 if (sidebar) 1079 sidebar->setEnabled(false); 1080 bookmarksButton->setEnabled(false); 1081 historyButton->setEnabled(false); 1082 syncBrowseButton->setEnabled(false); 1083 1084 // connect to the job interface to provide in-panel refresh notification 1085 connect(job, &KIO::Job::infoMessage, this, &ListPanel::inlineRefreshInfoMessage); 1086 connect(job, SIGNAL(percent(KJob *, ulong)), SLOT(inlineRefreshPercent(KJob *, ulong))); 1087 connect(job, &KIO::Job::result, this, &ListPanel::inlineRefreshListResult); 1088 1089 inlineRefreshJob = job; 1090 1091 totals->setText(i18n(">> Reading...")); 1092 cancelProgressButton->show(); 1093 } 1094 1095 void ListPanel::cancelProgress() 1096 { 1097 if (inlineRefreshJob) { 1098 disconnect(inlineRefreshJob, nullptr, this, nullptr); 1099 inlineRefreshJob->kill(KJob::EmitResult); 1100 inlineRefreshListResult(nullptr); 1101 } 1102 if (previewJob) { 1103 disconnect(previewJob, nullptr, this, nullptr); 1104 previewJob->kill(KJob::EmitResult); 1105 slotPreviewJobResult(nullptr); 1106 } 1107 } 1108 1109 void ListPanel::setNavigatorUrl(const QUrl &url) 1110 { 1111 _navigatorUrl = url; 1112 urlNavigator->setLocationUrl(url); 1113 } 1114 1115 void ListPanel::inlineRefreshPercent(KJob *, unsigned long perc) 1116 { 1117 QString msg = i18n(">> Reading: %1 % complete...", perc); 1118 totals->setText(msg); 1119 } 1120 1121 void ListPanel::inlineRefreshInfoMessage(KJob *, const QString &msg) 1122 { 1123 totals->setText(i18n(">> Reading: %1", msg)); 1124 } 1125 1126 void ListPanel::inlineRefreshListResult(KJob *) 1127 { 1128 if (inlineRefreshJob) 1129 disconnect(inlineRefreshJob, nullptr, this, nullptr); 1130 inlineRefreshJob = nullptr; 1131 // reenable everything 1132 status->setEnabled(true); 1133 urlNavigator->setEnabled(true); 1134 cdRootButton->setEnabled(true); 1135 cdHomeButton->setEnabled(true); 1136 cdUpButton->setEnabled(true); 1137 cdOtherButton->setEnabled(true); 1138 sidebarButton->setEnabled(true); 1139 if (sidebar) 1140 sidebar->setEnabled(true); 1141 bookmarksButton->setEnabled(true); 1142 historyButton->setEnabled(true); 1143 syncBrowseButton->setEnabled(true); 1144 1145 if (!previewJob) 1146 cancelProgressButton->hide(); 1147 } 1148 1149 void ListPanel::jumpBack() 1150 { 1151 func->openUrl(_jumpBackURL); 1152 } 1153 1154 void ListPanel::setJumpBack(QUrl url) 1155 { 1156 _jumpBackURL = std::move(url); 1157 } 1158 1159 void ListPanel::slotFilesystemError(const QString &msg) 1160 { 1161 slotRefreshColors(); 1162 fileSystemError->setText(i18n("Error: %1", msg)); 1163 fileSystemError->show(); 1164 } 1165 1166 void ListPanel::showButtonMenu(QToolButton *b) 1167 { 1168 if (this != ACTIVE_PANEL) 1169 slotFocusOnMe(); 1170 1171 if (b->isHidden()) 1172 b->menu()->exec(mapToGlobal(clientArea->pos())); 1173 else 1174 b->click(); 1175 } 1176 1177 void ListPanel::openBookmarks() 1178 { 1179 showButtonMenu(bookmarksButton); 1180 } 1181 1182 void ListPanel::openHistory() 1183 { 1184 showButtonMenu(historyButton); 1185 } 1186 1187 void ListPanel::openMedia() 1188 { 1189 showButtonMenu(mediaButton); 1190 } 1191 1192 void ListPanel::rightclickMenu() 1193 { 1194 if (view->getCurrentKrViewItem()) 1195 popRightClickMenu(mapToGlobal(view->getCurrentKrViewItem()->itemRect().topLeft())); 1196 } 1197 1198 void ListPanel::toggleSyncBrowse() 1199 { 1200 syncBrowseButton->toggle(); 1201 } 1202 1203 void ListPanel::editLocation() 1204 { 1205 urlNavigator->setUrlEditable(true); 1206 urlNavigator->setFocus(); 1207 urlNavigator->editor()->lineEdit()->selectAll(); 1208 } 1209 1210 void ListPanel::showSearchBar() 1211 { 1212 searchBar->showBar(KrSearchBar::MODE_SEARCH); 1213 } 1214 1215 void ListPanel::showSearchBarSelection() 1216 { 1217 searchBar->showBar(KrSearchBar::MODE_SELECT); 1218 } 1219 1220 void ListPanel::showSearchBarFilter() 1221 { 1222 searchBar->showBar(KrSearchBar::MODE_FILTER); 1223 } 1224 1225 void ListPanel::saveSettings(KConfigGroup cfg, bool saveHistory) 1226 { 1227 QUrl url = virtualPath(); 1228 url.setPassword(QString()); // make sure no password is saved 1229 cfg.writeEntry("Url", url.toString()); 1230 cfg.writeEntry("Type", getType()); 1231 cfg.writeEntry("Properties", getProperties()); 1232 cfg.writeEntry("PinnedUrl", pinnedUrl().toString()); 1233 if (saveHistory) 1234 func->history->save(KConfigGroup(&cfg, "History")); 1235 view->saveSettings(KConfigGroup(&cfg, "View")); 1236 1237 // splitter/sidebar state 1238 if (sidebar && !sidebar->isHidden()) { 1239 sidebar->saveSettings(KConfigGroup(&cfg, "PanelPopup")); 1240 cfg.writeEntry("PopupPosition", sidebarPosition()); 1241 cfg.writeEntry("SplitterSizes", sidebarSplitter->saveState()); 1242 cfg.writeEntry("PopupPage", sidebar->currentPage()); 1243 } else { 1244 cfg.deleteEntry("PopupPosition"); 1245 cfg.deleteEntry("SplitterSizes"); 1246 cfg.deleteEntry("PopupPage"); 1247 } 1248 } 1249 1250 void ListPanel::restoreSettings(KConfigGroup cfg) 1251 { 1252 changeType(cfg.readEntry("Type", defaultPanelType())); 1253 view->restoreSettings(KConfigGroup(&cfg, "View")); 1254 1255 // "locked" property must be set after URL path is restored! 1256 // This panel can be reused when loading a profile, 1257 // so we reset its properties before calling openUrl(). 1258 setProperties(0); 1259 1260 _lastLocalPath = ROOT_DIR; 1261 if (func->history->restore(KConfigGroup(&cfg, "History"))) { 1262 func->refresh(); 1263 } else { 1264 QUrl url(cfg.readEntry("Url", "invalid")); 1265 if (!url.isValid()) 1266 url = QUrl::fromLocalFile(ROOT_DIR); 1267 func->openUrl(url); 1268 } 1269 1270 setJumpBack(func->history->currentUrl()); 1271 1272 setProperties(cfg.readEntry("Properties", 0)); 1273 1274 if (isPinned()) { 1275 QUrl pinnedUrl(cfg.readEntry("PinnedUrl", "invalid")); 1276 if (!pinnedUrl.isValid()) { 1277 pinnedUrl = func->history->currentUrl(); 1278 } 1279 func->openUrl(pinnedUrl); 1280 setPinnedUrl(pinnedUrl); 1281 } 1282 1283 if (cfg.hasKey("PopupPosition")) { // sidebar was visible, restore 1284 toggleSidebar(); // create and show 1285 sidebar->restoreSettings(KConfigGroup(&cfg, "PanelPopup")); 1286 setSidebarPosition(cfg.readEntry("PopupPosition", 42 /* dummy */)); 1287 sidebarSplitter->restoreState(cfg.readEntry("SplitterSizes", QByteArray())); 1288 sidebar->setCurrentPage(cfg.readEntry("PopupPage", 0)); 1289 } 1290 } 1291 1292 void ListPanel::slotCurrentChanged(KrViewItem *item) 1293 { 1294 // update status bar 1295 if (item) 1296 krApp->statusBarUpdate(item->description()); 1297 1298 // update sidebar; which panel to display on? 1299 Sidebar *p; 1300 if (sidebar && !sidebar->isHidden()) { 1301 p = sidebar; 1302 } else if (otherPanel()->gui->sidebar && !otherPanel()->gui->sidebar->isHidden()) { 1303 p = otherPanel()->gui->sidebar; 1304 } else { 1305 return; 1306 } 1307 1308 p->update(item ? func->files()->getFileItem(item->name()) : nullptr); 1309 } 1310 1311 void ListPanel::otherPanelChanged() 1312 { 1313 func->syncURL = QUrl(); 1314 } 1315 1316 void ListPanel::getFocusCandidates(QVector<QWidget *> &widgets) 1317 { 1318 if (urlNavigator->editor()->isVisible()) 1319 widgets << urlNavigator->editor(); 1320 if (view->widget()->isVisible()) 1321 widgets << view->widget(); 1322 if (sidebar && sidebar->isVisible()) 1323 widgets << sidebar; 1324 } 1325 1326 void ListPanel::updateButtons() 1327 { 1328 backButton->setEnabled(func->history->canGoBack()); 1329 forwardButton->setEnabled(func->history->canGoForward()); 1330 historyButton->setEnabled(func->history->count() > 1); 1331 cdRootButton->setEnabled(!virtualPath().matches(QUrl::fromLocalFile(ROOT_DIR), QUrl::StripTrailingSlash)); 1332 cdUpButton->setEnabled(!func->files()->isRoot()); 1333 cdHomeButton->setEnabled(!func->atHome()); 1334 } 1335 1336 void ListPanel::duplicateTab(KrViewItem *it) 1337 { 1338 if (!it) 1339 return; 1340 else if (it->name() == "..") { 1341 duplicateTab(KIO::upUrl(virtualPath()), true); 1342 } else if (func->getFileItem(it)->isDir()) { 1343 QUrl url = virtualPath(); 1344 url = url.adjusted(QUrl::StripTrailingSlash); 1345 url.setPath(url.path() + '/' + (it->name())); 1346 duplicateTab(url, true); 1347 } 1348 } 1349 1350 void ListPanel::duplicateTab(const QUrl &url, bool nextToThis) 1351 { 1352 _manager->duplicateTab(url, nextToThis ? this : nullptr); 1353 } 1354 1355 void ListPanel::slotNavigatorUrlChanged(const QUrl &url) 1356 { 1357 if (url == _navigatorUrl) 1358 return; // this is the URL we just set ourself 1359 1360 if (!isNavigatorEditModeSet()) { 1361 urlNavigator->setUrlEditable(false); 1362 } 1363 1364 func->openUrl(KrServices::escapeFileUrl(url), QString(), true); 1365 } 1366 1367 void ListPanel::resetNavigatorMode() 1368 { 1369 if (isNavigatorEditModeSet()) 1370 return; 1371 1372 // set to "navigate" mode if url wasn't changed 1373 if (urlNavigator->uncommittedUrl().matches(virtualPath(), QUrl::StripTrailingSlash)) { 1374 // NOTE: this also sets focus to the navigator 1375 urlNavigator->setUrlEditable(false); 1376 slotFocusOnMe(); 1377 } 1378 } 1379 1380 int ListPanel::sidebarPosition() const 1381 { 1382 int pos = sidebarSplitter->orientation() == Qt::Vertical ? 1 : 0; 1383 return pos + (qobject_cast<Sidebar *>(sidebarSplitter->widget(0)) == NULL ? 2 : 0); 1384 } 1385 1386 void ListPanel::setSidebarPosition(int pos) 1387 { 1388 sidebarSplitter->setOrientation(pos % 2 == 0 ? Qt::Horizontal : Qt::Vertical); 1389 if ((pos < 2) != (qobject_cast<Sidebar *>(sidebarSplitter->widget(0)) != NULL)) { 1390 sidebarSplitter->insertWidget(0, sidebarSplitter->widget(1)); // swapping widgets in splitter 1391 } 1392 } 1393 1394 void ListPanel::connectQuickSizeCalculator(SizeCalculator *sizeCalculator) 1395 { 1396 connect(sizeCalculator, &SizeCalculator::started, this, [=]() { 1397 quickSizeCalcProgress->reset(); 1398 quickSizeCalcProgress->show(); 1399 cancelQuickSizeCalcButton->show(); 1400 }); 1401 connect(cancelQuickSizeCalcButton, &QToolButton::clicked, sizeCalculator, &SizeCalculator::cancel); 1402 connect(sizeCalculator, &SizeCalculator::progressChanged, quickSizeCalcProgress, &QProgressBar::setValue); 1403 connect(sizeCalculator, &SizeCalculator::finished, this, [=]() { 1404 cancelQuickSizeCalcButton->hide(); 1405 quickSizeCalcProgress->hide(); 1406 }); 1407 }