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 }