1 /*****************************************************************************
2  * Copyright (C) 2000 Shie Erlich <krusader@users.sourceforge.net>           *
3  * Copyright (C) 2000 Rafi Yanai <krusader@users.sourceforge.net>            *
4  * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org]              *
5  *                                                                           *
6  * This file is part of Krusader [https://krusader.org].                     *
7  *                                                                           *
8  * Krusader is free software: you can redistribute it and/or modify          *
9  * it under the terms of the GNU General Public License as published by      *
10  * the Free Software Foundation, either version 2 of the License, or         *
11  * (at your option) any later version.                                       *
12  *                                                                           *
13  * Krusader is distributed in the hope that it will be useful,               *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of            *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
16  * GNU General Public License for more details.                              *
17  *                                                                           *
18  * You should have received a copy of the GNU General Public License         *
19  * along with Krusader.  If not, see [http://www.gnu.org/licenses/].         *
20  *****************************************************************************/
21 
22 #include "listpanel.h"
23 
24 // QtCore
25 #include <QStringList>
26 #include <QList>
27 #include <QEvent>
28 #include <QMimeData>
29 #include <QTimer>
30 #include <QRegExp>
31 #include <QUrl>
32 // QtGui
33 #include <QBitmap>
34 #include <QKeyEvent>
35 #include <QPixmap>
36 #include <QDropEvent>
37 #include <QHideEvent>
38 #include <QShowEvent>
39 #include <QDrag>
40 #include <QImage>
41 // QtWidgets
42 #include <QHBoxLayout>
43 #include <QFrame>
44 #include <QMenu>
45 #include <QSplitter>
46 #include <QTabBar>
47 
48 #include <KCoreAddons/KUrlMimeData>
49 #include <KI18n/KLocalizedString>
50 #include <KIO/DropJob>
51 #include <KWidgetsAddons/KCursor>
52 #include <KWidgetsAddons/KMessageBox>
53 #include <KIOFileWidgets/KFilePlacesModel>
54 #include <KIOWidgets/KUrlComboBox>
55 
56 #include "dirhistoryqueue.h"
57 #include "krcolorcache.h"
58 #include "krerrordisplay.h"
59 #include "krlayoutfactory.h"
60 #include "krpreviewpopup.h"
61 #include "krsearchbar.h"
62 #include "listpanelactions.h"
63 #include "panelcontextmenu.h"
64 #include "panelfunc.h"
65 #include "sidebar.h"
66 #include "viewactions.h"
67 
68 #include "PanelView/krview.h"
69 #include "PanelView/krviewfactory.h"
70 #include "PanelView/krviewitem.h"
71 
72 #include "../defaults.h"
73 #include "../icon.h"
74 #include "../krservices.h"
75 #include "../krslots.h"
76 #include "../krusader.h"
77 #include "../krusaderview.h"
78 
79 #include "../Archive/krarchandler.h"
80 #include "../BookMan/krbookmarkbutton.h"
81 #include "../FileSystem/fileitem.h"
82 #include "../FileSystem/filesystem.h"
83 #include "../FileSystem/krpermhandler.h"
84 #include "../FileSystem/sizecalculator.h"
85 #include "../Dialogs/krdialogs.h"
86 #include "../Dialogs/krspwidgets.h"
87 #include "../Dialogs/krsqueezedtextlabel.h"
88 #include "../Dialogs/percentalsplitter.h"
89 #include "../Dialogs/popularurls.h"
90 #include "../GUI/dirhistorybutton.h"
91 #include "../GUI/kcmdline.h"
92 #include "../GUI/mediabutton.h"
93 #include "../MountMan/kmountman.h"
94 #include "../UserAction/useractionpopupmenu.h"
95 
96 class ActionButton : public QToolButton
97 {
98 public:
ActionButton(QWidget * parent,ListPanel * panel,QAction * action,QString text=QString ())99     ActionButton(QWidget *parent, ListPanel *panel, QAction *action, QString text = QString()) :
100             QToolButton(parent),  panel(panel), action(action) {
101         setText(text);
102         setAutoRaise(true);
103         if(KConfigGroup(krConfig, "ListPanelButtons").readEntry("Icons", false) || text.isEmpty())
104             setIcon(action->icon());
105         setToolTip(action->toolTip());
106     }
107 
108 protected:
mousePressEvent(QMouseEvent *)109     virtual void mousePressEvent(QMouseEvent *) Q_DECL_OVERRIDE {
110         panel->slotFocusOnMe();
111         action->trigger();
112     }
113 
114     ListPanel *panel;
115     QAction *action;
116 };
117 
118 
119 /////////////////////////////////////////////////////
120 //      The list panel constructor       //
121 /////////////////////////////////////////////////////
ListPanel(QWidget * parent,AbstractPanelManager * manager,KConfigGroup cfg)122 ListPanel::ListPanel(QWidget *parent, AbstractPanelManager *manager, KConfigGroup cfg) :
123         QWidget(parent), KrPanel(manager, this, new ListPanelFunc(this)),
124         panelType(-1), colorMask(255), compareMode(false),
125         previewJob(0), inlineRefreshJob(0), searchBar(0), cdRootButton(0), cdUpButton(0),
126         sidebarButton(0), sidebar(0), fileSystemError(0), _navigatorUrl(), _tabState(TabState::DEFAULT)
127 {
128     if(cfg.isValid())
129         panelType = cfg.readEntry("Type", -1);
130     if (panelType == -1)
131         panelType = defaultPanelType();
132 
133     _actions = krApp->listPanelActions();
134 
135     setAcceptDrops(true);
136 
137     QHash<QString, QWidget*> widgets;
138 
139 #define ADD_WIDGET(widget) widgets.insert(#widget, widget);
140 
141     // media button
142     mediaButton = new MediaButton(this);
143     connect(mediaButton, SIGNAL(aboutToShow()), this, SLOT(slotFocusOnMe()));
144     connect(mediaButton, SIGNAL(openUrl(QUrl)), func, SLOT(openUrl(QUrl)));
145     connect(mediaButton, SIGNAL(newTab(QUrl)), SLOT(newTab(QUrl)));
146     ADD_WIDGET(mediaButton);
147 
148     // status bar
149     status = new KrSqueezedTextLabel(this);
150     KConfigGroup group(krConfig, "Look&Feel");
151     status->setFont(group.readEntry("Filelist Font", _FilelistFont));
152     status->setAutoFillBackground(false);
153     status->setText("");          // needed for initialization code!
154     status->setWhatsThis(i18n("The statusbar displays information about the filesystem "
155                               "which holds your current folder: total size, free space, "
156                               "type of filesystem, etc."));
157     ADD_WIDGET(status);
158 
159     // back button
160     backButton = new ActionButton(this, this, _actions->actHistoryBackward);
161     ADD_WIDGET(backButton);
162 
163     // forward button
164     forwardButton = new ActionButton(this, this, _actions->actHistoryForward);
165     ADD_WIDGET(forwardButton);
166 
167 
168     // ... create the history button
169     historyButton = new DirHistoryButton(func->history, this);
170     connect(historyButton, SIGNAL(aboutToShow()), this, SLOT(slotFocusOnMe()));
171     connect(historyButton, SIGNAL(gotoPos(int)), func, SLOT(historyGotoPos(int)));
172     ADD_WIDGET(historyButton);
173 
174     // bookmarks button
175     bookmarksButton = new KrBookmarkButton(this);
176     connect(bookmarksButton, SIGNAL(aboutToShow()), this, SLOT(slotFocusOnMe()));
177     connect(bookmarksButton, SIGNAL(openUrl(QUrl)), func, SLOT(openUrl(QUrl)));
178     bookmarksButton->setWhatsThis(i18n("Open menu with bookmarks. You can also add "
179                                        "current location to the list, edit bookmarks "
180                                        "or add subfolder to the list."));
181     ADD_WIDGET(bookmarksButton);
182 
183     // url input field
184     urlNavigator = new KUrlNavigator(new KFilePlacesModel(this), QUrl(), this);
185     urlNavigator->setWhatsThis(i18n("Name of folder where you are. You can also "
186                                     "enter name of desired location to move there. "
187                                     "Use of Net protocols like ftp or fish is possible."));
188     // handle certain key events here in event filter
189     urlNavigator->editor()->installEventFilter(this);
190     urlNavigator->setUrlEditable(isNavigatorEditModeSet());
191     urlNavigator->setShowFullPath(group.readEntry("Navigator Full Path", false));
192     connect(urlNavigator, SIGNAL(returnPressed()), this, SLOT(slotFocusOnMe()));
193     connect(urlNavigator, &KUrlNavigator::urlChanged, this, &ListPanel::slotNavigatorUrlChanged);
194     connect(urlNavigator->editor()->lineEdit(), &QLineEdit::editingFinished, this, &ListPanel::resetNavigatorMode);
195     connect(urlNavigator, SIGNAL(tabRequested(QUrl)), this, SLOT(newTab(QUrl)));
196     connect(urlNavigator, SIGNAL(urlsDropped(QUrl,QDropEvent*)), this, SLOT(handleDrop(QUrl,QDropEvent*)));
197     ADD_WIDGET(urlNavigator);
198 
199     // toolbar
200     QWidget * toolbar = new QWidget(this);
201     QHBoxLayout * toolbarLayout = new QHBoxLayout(toolbar);
202     toolbarLayout->setContentsMargins(0, 0, 0, 0);
203     toolbarLayout->setSpacing(0);
204     ADD_WIDGET(toolbar);
205 
206     fileSystemError = new KrErrorDisplay(this);
207     fileSystemError->setWordWrap(true);
208     fileSystemError->hide();
209     ADD_WIDGET(fileSystemError);
210 
211     // client area
212     clientArea = new QWidget(this);
213     QVBoxLayout *clientLayout = new QVBoxLayout(clientArea);
214     clientLayout->setSpacing(0);
215     clientLayout->setContentsMargins(0, 0, 0, 0);
216     ADD_WIDGET(clientArea);
217 
218     // totals label
219     totals = new KrSqueezedTextLabel(this);
220     totals->setFont(group.readEntry("Filelist Font", _FilelistFont));
221     totals->setAutoFillBackground(false);
222     totals->setWhatsThis(i18n("The totals bar shows how many files exist, "
223                               "how many selected and the bytes math"));
224     ADD_WIDGET(totals);
225 
226     // free space label
227     freeSpace = new KrSqueezedTextLabel(this);
228     freeSpace->setFont(group.readEntry("Filelist Font", _FilelistFont));
229     freeSpace->setAutoFillBackground(false);
230     freeSpace->setText("");
231     freeSpace->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
232     ADD_WIDGET(freeSpace);
233 
234     // progress indicator and cancel button for the quick calc size
235     quickSizeCalcProgress = new QProgressBar(this);
236     quickSizeCalcProgress->hide();
237     ADD_WIDGET(quickSizeCalcProgress);
238     cancelQuickSizeCalcButton = new QToolButton(this);
239     cancelQuickSizeCalcButton->hide();
240     cancelQuickSizeCalcButton->setIcon(Icon("dialog-cancel"));
241     cancelQuickSizeCalcButton->setToolTip(i18n("Cancel directory space calculation"));
242     ADD_WIDGET(cancelQuickSizeCalcButton);
243 
244     // progress indicator for the preview job
245     previewProgress = new QProgressBar(this);
246     previewProgress->hide();
247     ADD_WIDGET(previewProgress);
248 
249     // a cancel button for the filesystem refresh and preview job
250     cancelProgressButton = new QToolButton(this);
251     cancelProgressButton->hide();
252     cancelProgressButton->setIcon(Icon("dialog-cancel"));
253     connect(cancelProgressButton, SIGNAL(clicked()), this, SLOT(cancelProgress()));
254     ADD_WIDGET(cancelProgressButton);
255 
256     // button for changing the panel sidebar position in the panel
257     sidebarPositionButton = new QToolButton(this);
258     sidebarPositionButton->hide();
259     sidebarPositionButton->setAutoRaise(true);
260     sidebarPositionButton->setIcon(Icon("exchange-positions"));
261     sidebarPositionButton->setToolTip(i18n("Move Sidebar clockwise"));
262     connect(sidebarPositionButton, &QToolButton::clicked, [this]() {
263         // moving position clockwise
264         setSidebarPosition((sidebarPosition() + 1) % 4); });
265     ADD_WIDGET(sidebarPositionButton);
266 
267     // a quick button to open the sidebar
268     sidebarButton = new QToolButton(this);
269     sidebarButton->setAutoRaise(true);
270     sidebarButton->setIcon(Icon("arrow-up"));
271     connect(sidebarButton, &QToolButton::clicked, this, &ListPanel::toggleSidebar);
272     sidebarButton->setToolTip(i18n("Open the Sidebar"));
273     ADD_WIDGET(sidebarButton);
274 
275 #undef ADD_WIDGET
276 
277     // toolbar buttons
278     cdOtherButton = new ActionButton(toolbar, this, _actions->actCdToOther, "=");
279     toolbarLayout->addWidget(cdOtherButton);
280 
281     cdUpButton = new ActionButton(toolbar, this, _actions->actDirUp, "..");
282     toolbarLayout->addWidget(cdUpButton);
283 
284     cdHomeButton = new ActionButton(toolbar, this, _actions->actHome, "~");
285     toolbarLayout->addWidget(cdHomeButton);
286 
287     cdRootButton = new ActionButton(toolbar, this, _actions->actRoot, "/");
288     toolbarLayout->addWidget(cdRootButton);
289 
290     // create the button for sync-browsing
291     syncBrowseButton = new QToolButton(toolbar);
292     syncBrowseButton->setIcon(Icon("kr_syncbrowse_off"));
293     syncBrowseButton->setCheckable(true);
294 
295     const QString syncBrowseText = i18n("This button toggles the sync-browse mode.\n"
296                                         "When active, each folder change is performed in the\n"
297                                         "active and inactive panel - if possible.");
298     syncBrowseButton->setText(syncBrowseText);
299     syncBrowseButton->setToolTip(syncBrowseText);
300     connect(syncBrowseButton, &QToolButton::toggled, [=](bool checked) {
301         syncBrowseButton->setIcon(
302             Icon(checked ? "kr_syncbrowse_on" : "kr_syncbrowse_off"));
303     });
304     syncBrowseButton->setAutoRaise(true);
305     toolbarLayout->addWidget(syncBrowseButton);
306 
307     setButtons();
308 
309     // create a splitter to hold the view and the sidebar
310     sidebarSplitter = new PercentalSplitter(clientArea);
311     sidebarSplitter->setChildrenCollapsible(true);
312     sidebarSplitter->setOrientation(Qt::Horizontal);
313     // expand vertical if splitter orientation is horizontal
314     QSizePolicy sizePolicy = sidebarSplitter->sizePolicy();
315     sizePolicy.setVerticalPolicy(QSizePolicy::Expanding);
316     sidebarSplitter->setSizePolicy(sizePolicy);
317     clientLayout->addWidget(sidebarSplitter);
318 
319     // view
320     createView();
321 
322     // search (in folder) bar
323     searchBar = new KrSearchBar(view, clientArea);
324     searchBar->hide();
325     bool top = group.readEntry("Quicksearch Position", "bottom") == "top";
326     clientLayout->insertWidget(top ? 0 : -1, searchBar);
327 
328     // create the layout
329     KrLayoutFactory fact(this, widgets);
330     QLayout *layout = fact.createLayout();
331 
332     if(!layout) { // fallback: create a layout by ourself
333         QVBoxLayout *v = new QVBoxLayout;
334         v->setContentsMargins(0, 0, 0, 0);
335         v->setSpacing(0);
336 
337         QHBoxLayout *h = new QHBoxLayout;
338         h->setContentsMargins(0, 0, 0, 0);
339         h->setSpacing(0);
340         h->addWidget(urlNavigator);
341         h->addWidget(toolbar);
342         h->addStretch();
343         v->addLayout(h);
344 
345         h = new QHBoxLayout;
346         h->setContentsMargins(0, 0, 0, 0);
347         h->setSpacing(0);
348         h->addWidget(mediaButton);
349         h->addWidget(status);
350         h->addWidget(backButton);
351         h->addWidget(forwardButton);
352         h->addWidget(historyButton);
353         h->addWidget(bookmarksButton);
354         v->addLayout(h);
355 
356         v->addWidget(fileSystemError);
357         v->addWidget(clientArea);
358 
359         h = new QHBoxLayout;
360         h->setContentsMargins(0, 0, 0, 0);
361         h->setSpacing(0);
362         h->addWidget(totals);
363         h->addWidget(freeSpace);
364         h->addWidget(quickSizeCalcProgress);
365         h->addWidget(cancelQuickSizeCalcButton);
366         h->addWidget(previewProgress);
367         h->addWidget(cancelProgressButton);
368         h->addWidget(sidebarButton);
369         v->addLayout(h);
370 
371         layout = v;
372     }
373 
374     setLayout(layout);
375 
376     connect(&KrColorCache::getColorCache(), SIGNAL(colorsRefreshed()), this, SLOT(refreshColors()));
377     connect(krApp, SIGNAL(shutdown()), SLOT(cancelProgress()));
378 }
379 
~ListPanel()380 ListPanel::~ListPanel()
381 {
382     view->widget()->removeEventFilter(this);
383     urlNavigator->editor()->removeEventFilter(this);
384     cancelProgress();
385     delete view;
386     view = 0;
387     delete func;
388     delete status;
389     delete bookmarksButton;
390     delete totals;
391     delete urlNavigator;
392     delete cdRootButton;
393     delete cdHomeButton;
394     delete cdUpButton;
395     delete cdOtherButton;
396     delete syncBrowseButton;
397 //     delete layout;
398 }
399 
reparent(QWidget * parent,AbstractPanelManager * manager)400 void ListPanel::reparent(QWidget *parent, AbstractPanelManager *manager)
401 {
402     setParent(parent);
403     _manager = manager;
404 }
405 
defaultPanelType()406 int ListPanel::defaultPanelType()
407 {
408     KConfigGroup group(krConfig, "Look&Feel");
409     return group.readEntry("Default Panel Type", KrViewFactory::defaultViewId());
410 }
411 
isNavigatorEditModeSet()412 bool ListPanel::isNavigatorEditModeSet()
413 {
414     KConfigGroup group(krConfig, "Look&Feel");
415     return group.readEntry("Navigator Edit Mode", false);
416 }
417 
createView()418 void ListPanel::createView()
419 {
420     view = KrViewFactory::createView(panelType, sidebarSplitter, krConfig);
421     view->init();
422     view->setMainWindow(krApp);
423 
424     // KrViewFactory may create a different view type than requested
425     panelType = view->instance()->id();
426 
427     if(this == ACTIVE_PANEL)
428         view->prepareForActive();
429     else
430         view->prepareForPassive();
431     view->refreshColors();
432 
433     sidebarSplitter->insertWidget(sidebarPosition() < 2 ? 1 : 0, view->widget());
434 
435     view->widget()->installEventFilter(this);
436 
437     connect(view->op(), &KrViewOperator::quickCalcSpace, func, &ListPanelFunc::quickCalcSpace);
438     connect(view->op(), SIGNAL(goHome()), func, SLOT(home()));
439     connect(view->op(), SIGNAL(dirUp()), func, SLOT(dirUp()));
440     connect(view->op(), &KrViewOperator::defaultDeleteFiles, func, &ListPanelFunc::defaultDeleteFiles);
441     connect(view->op(), SIGNAL(middleButtonClicked(KrViewItem*)), SLOT(newTab(KrViewItem*)));
442     connect(view->op(), SIGNAL(currentChanged(KrViewItem*)), SLOT(slotCurrentChanged(KrViewItem*)));
443     connect(view->op(), SIGNAL(renameItem(QString,QString)),
444             func, SLOT(rename(QString,QString)));
445     connect(view->op(), SIGNAL(executed(QString)), func, SLOT(execute(QString)));
446     connect(view->op(), SIGNAL(goInside(QString)), func, SLOT(goInside(QString)));
447     connect(view->op(), SIGNAL(needFocus()), this, SLOT(slotFocusOnMe()));
448     connect(view->op(), SIGNAL(selectionChanged()), this, SLOT(slotUpdateTotals()));
449     connect(view->op(), SIGNAL(itemDescription(QString)), krApp, SLOT(statusBarUpdate(QString)));
450     connect(view->op(), SIGNAL(contextMenu(QPoint)), this, SLOT(popRightClickMenu(QPoint)));
451     connect(view->op(), SIGNAL(emptyContextMenu(QPoint)),
452             this, SLOT(popEmptyRightClickMenu(QPoint)));
453     connect(view->op(), SIGNAL(letsDrag(QStringList,QPixmap)), this, SLOT(startDragging(QStringList,QPixmap)));
454     connect(view->op(), &KrViewOperator::gotDrop,
455             this, [this](QDropEvent *event) {handleDrop(event, true); });
456     connect(view->op(), SIGNAL(previewJobStarted(KJob*)), this, SLOT(slotPreviewJobStarted(KJob*)));
457     connect(view->op(), SIGNAL(refreshActions()), krApp->viewActions(), SLOT(refreshActions()));
458     connect(view->op(), SIGNAL(currentChanged(KrViewItem*)), func->history, SLOT(saveCurrentItem()));
459     connect(view->op(), &KrViewOperator::goBack, func, &ListPanelFunc::historyBackward);
460     connect(view->op(), &KrViewOperator::goForward, func, &ListPanelFunc::historyForward);
461 
462     view->setFiles(func->files());
463 
464     func->refreshActions();
465 }
466 
changeType(int type)467 void ListPanel::changeType(int type)
468 {
469     if (panelType != type) {
470         QString current = view->getCurrentItem();
471         QList<QUrl> selection = view->selectedUrls();
472         bool filterApplysToDirs = view->properties()->filterApplysToDirs;
473         KrViewProperties::FilterSpec filter = view->filter();
474         FilterSettings filterSettings = view->properties()->filterSettings;
475 
476         panelType = type;
477 
478         KrView *oldView = view;
479         createView();
480         searchBar->setView(view);
481         delete oldView;
482 
483         view->setFilter(filter, filterSettings, filterApplysToDirs);
484         view->setSelectionUrls(selection);
485         view->setCurrentItem(current);
486         view->makeItemVisible(view->getCurrentKrViewItem());
487     }
488 }
489 
getProperties()490 int ListPanel::getProperties()
491 {
492     int props = 0;
493     if (syncBrowseButton->isChecked()) {
494         props |= PROP_SYNC_BUTTON_ON;
495     }
496     if (isLocked()) {
497         props |= PROP_LOCKED;
498     } else if (isPinned()) {
499         props |= PROP_PINNED;
500     }
501     return props;
502 }
503 
setProperties(int prop)504 void ListPanel::setProperties(int prop)
505 {
506     syncBrowseButton->setChecked(prop & PROP_SYNC_BUTTON_ON);
507     if (prop & PROP_LOCKED) {
508         _tabState = TabState::LOCKED;
509     } else if (prop & PROP_PINNED) {
510         _tabState = TabState::PINNED;
511     } else {
512         _tabState = TabState::DEFAULT;
513     }
514 }
515 
eventFilter(QObject * watched,QEvent * e)516 bool ListPanel::eventFilter(QObject * watched, QEvent * e)
517 {
518     if(view && watched == view->widget()) {
519         if(e->type() == QEvent::FocusIn && this != ACTIVE_PANEL && !isHidden())
520             slotFocusOnMe();
521         else if(e->type() == QEvent::ShortcutOverride) {
522             QKeyEvent *ke = static_cast<QKeyEvent*>(e);
523             if(ke->key() == Qt::Key_Escape && ke->modifiers() == Qt::NoModifier) {
524                 // if the cancel refresh action has no shortcut assigned,
525                 // we need this event ourselves to cancel refresh
526                 if(_actions->actCancelRefresh->shortcut().isEmpty()) {
527                     e->accept();
528                     return true;
529                 }
530             }
531         }
532     }
533     // handle URL navigator key events
534     else if(urlNavigator && watched == urlNavigator->editor()) {
535         // override default shortcut for panel focus
536         if(e->type() == QEvent::ShortcutOverride) {
537             QKeyEvent *ke = static_cast<QKeyEvent *>(e);
538             if ((ke->key() == Qt::Key_Escape) && (ke->modifiers() == Qt::NoModifier)) {
539                 e->accept(); // we will get the key press event now
540                 return true;
541             }
542         } else if(e->type() == QEvent::KeyPress) {
543             QKeyEvent *ke = static_cast<QKeyEvent *>(e);
544             if ((ke->key() == Qt::Key_Down) && (ke->modifiers() == Qt::ControlModifier)) {
545                 slotFocusOnMe();
546                 return true;
547             } else if ((ke->key() == Qt::Key_Escape) && (ke->modifiers() == Qt::NoModifier)) {
548                 // reset navigator
549                 urlNavigator->editor()->setUrl(urlNavigator->locationUrl());
550                 slotFocusOnMe();
551                 return true;
552             }
553         }
554     }
555     return false;
556 }
557 
toggleSidebar()558 void ListPanel::toggleSidebar()
559 {
560     if(!sidebar) {
561         sidebar = new Sidebar(sidebarSplitter);
562         // fix vertical grow of splitter (and entire window) if its content
563         // demands more space
564         QSizePolicy sizePolicy = sidebar->sizePolicy();
565         sizePolicy.setVerticalPolicy(QSizePolicy::Ignored);
566         sidebar->setSizePolicy(sizePolicy);
567         connect(this, &ListPanel::pathChanged, sidebar, &Sidebar::onPanelPathChange);
568         connect(sidebar, &Sidebar::urlActivated, SLOTS, &KRslots::refresh);
569         sidebarSplitter->insertWidget(0, sidebar);
570     }
571 
572     if (sidebar->isHidden()) {
573         if (sidebarSplitterSizes.count() > 0) {
574             sidebarSplitter->setSizes(sidebarSplitterSizes);
575         } else { // on the first time, resize to 50%
576             QList<int> lst;
577             lst << height() / 2 << height() / 2;
578             sidebarSplitter->setSizes(lst);
579         }
580 
581         sidebar->show();
582         sidebarButton->setIcon(Icon("arrow-down"));
583         sidebarButton->setToolTip(i18n("Close the Sidebar"));
584         sidebarPositionButton->show();
585     } else {
586         sidebarSplitterSizes.clear();
587         sidebarSplitterSizes = sidebarSplitter->sizes();
588         sidebar->hide();
589         sidebarButton->setIcon(Icon("arrow-up"));
590         sidebarButton->setToolTip(i18n("Open the Sidebar"));
591         sidebarPositionButton->hide();
592 
593         QList<int> lst;
594         lst << height() << 0;
595         sidebarSplitter->setSizes(lst);
596         if (ACTIVE_PANEL)
597             ACTIVE_PANEL->gui->slotFocusOnMe();
598     }
599 }
600 
lastLocalPath() const601 QString ListPanel::lastLocalPath() const
602 {
603     return _lastLocalPath;
604 }
605 
setButtons()606 void ListPanel::setButtons()
607 {
608     KConfigGroup group(krConfig, "Look&Feel");
609 
610     mediaButton->setVisible(group.readEntry("Media Button Visible", true));
611     backButton->setVisible(group.readEntry("Back Button Visible", false));
612     forwardButton->setVisible(group.readEntry("Forward Button Visible", false));
613     historyButton->setVisible(group.readEntry("History Button Visible", true));
614     bookmarksButton->setVisible(group.readEntry("Bookmarks Button Visible", true));
615 
616     if (group.readEntry("Panel Toolbar visible", _PanelToolBar)) {
617         cdRootButton->setVisible(group.readEntry("Root Button Visible", _cdRoot));
618         cdHomeButton->setVisible(group.readEntry("Home Button Visible", _cdHome));
619         cdUpButton->setVisible(group.readEntry("Up Button Visible", _cdUp));
620         cdOtherButton->setVisible(group.readEntry("Equal Button Visible", _cdOther));
621         syncBrowseButton->setVisible(group.readEntry("SyncBrowse Button Visible", _syncBrowseButton));
622     } else {
623         cdRootButton->hide();
624         cdHomeButton->hide();
625         cdUpButton->hide();
626         cdOtherButton->hide();
627         syncBrowseButton->hide();
628     }
629 }
630 
slotUpdateTotals()631 void ListPanel::slotUpdateTotals()
632 {
633     totals->setText(view->statistics());
634 }
635 
compareDirs(bool otherPanelToo)636 void ListPanel::compareDirs(bool otherPanelToo)
637 {
638     // Performs a check in order to avoid that the next code is executed twice
639     if (otherPanelToo == true) {
640         // If both panels are showing the same directory
641         if (_manager->currentPanel()->virtualPath() == otherPanel()->virtualPath()) {
642             if (KMessageBox::warningContinueCancel(this,
643                 i18n("Warning: The left and the right side are showing the same folder.")) != KMessageBox::Continue) {
644                 return;
645             }
646         }
647     }
648 
649     KConfigGroup pg(krConfig, "Private");
650     int compareMode = pg.readEntry("Compare Mode", 0);
651     KConfigGroup group(krConfig, "Look&Feel");
652     bool selectDirs = group.readEntry("Mark Dirs", false);
653 
654     KrViewItem *item, *otherItem;
655 
656     for (item = view->getFirst(); item != 0; item = view->getNext(item)) {
657         if (item->name() == "..")
658             continue;
659 
660         for (otherItem = otherPanel()->view->getFirst(); otherItem != 0 && otherItem->name() != item->name();
661                 otherItem = otherPanel()->view->getNext(otherItem));
662 
663         bool isSingle = (otherItem == 0), isDifferent = false, isNewer = false;
664 
665         if (func->getFileItem(item)->isDir() && !selectDirs) {
666             item->setSelected(false);
667             continue;
668         }
669 
670         if (otherItem) {
671             if (!func->getFileItem(item)->isDir())
672                 isDifferent = otherPanel()->func->getFileItem(otherItem)->getSize() != func->getFileItem(item)->getSize();
673             isNewer = func->getFileItem(item)->getModificationTime() > otherPanel()->func->getFileItem(otherItem)->getModificationTime();
674         }
675 
676         switch (compareMode) {
677         case 0:
678             item->setSelected(isNewer || isSingle);
679             break;
680         case 1:
681             item->setSelected(isNewer);
682             break;
683         case 2:
684             item->setSelected(isSingle);
685             break;
686         case 3:
687             item->setSelected(isDifferent || isSingle);
688             break;
689         case 4:
690             item->setSelected(isDifferent);
691             break;
692         }
693     }
694 
695     view->updateView();
696 
697     if (otherPanelToo)
698         otherPanel()->gui->compareDirs(false);
699 }
700 
refreshColors()701 void ListPanel::refreshColors()
702 {
703     view->refreshColors();
704     emit refreshColors(this == ACTIVE_PANEL);
705 }
706 
slotFocusOnMe(bool focus)707 void ListPanel::slotFocusOnMe(bool focus)
708 {
709     if (focus && _manager->currentPanel() != this) {
710         // ignore focus request if this panel is not shown
711         return;
712     }
713 
714     krApp->setUpdatesEnabled(false);
715 
716     if(focus) {
717         emit activate();
718         _actions->activePanelChanged();
719         func->refreshActions();
720         slotCurrentChanged(view->getCurrentKrViewItem());
721         view->prepareForActive();
722         otherPanel()->gui->slotFocusOnMe(false);
723     } else {
724         // in case a new url was entered but not refreshed to,
725         // reset url navigator to the current url
726         setNavigatorUrl(virtualPath());
727         view->prepareForPassive();
728     }
729 
730     urlNavigator->setActive(focus);
731     refreshColors();
732 
733     krApp->setUpdatesEnabled(true);
734 }
735 
736 // this is used to start the panel
737 //////////////////////////////////////////////////////////////////
start(const QUrl & url)738 void ListPanel::start(const QUrl &url)
739 {
740     QUrl startUrl(url);
741 
742     if (!startUrl.isValid())
743         startUrl = QUrl::fromLocalFile(ROOT_DIR);
744 
745     _lastLocalPath = startUrl.isLocalFile() ? startUrl.path() : ROOT_DIR;
746 
747 
748     func->openUrl(startUrl);
749 
750     setJumpBack(startUrl);
751 }
752 
slotStartUpdate(bool directoryChange)753 void ListPanel::slotStartUpdate(bool directoryChange)
754 {
755     if (inlineRefreshJob)
756         inlineRefreshListResult(0);
757 
758     setCursor(Qt::BusyCursor);
759 
760     const QUrl currentUrl = virtualPath();
761     if (directoryChange) {
762 
763         if (this == ACTIVE_PANEL) {
764             slotFocusOnMe();
765         }
766 
767         if (currentUrl.isLocalFile())
768             _lastLocalPath = currentUrl.path();
769 
770         setNavigatorUrl(currentUrl);
771 
772         emit pathChanged(currentUrl);
773 
774         krApp->popularUrls()->addUrl(currentUrl);
775 
776         searchBar->hideBar();
777     }
778 
779     if (compareMode)
780         otherPanel()->view->refresh();
781 
782     // return cursor to normal arrow
783     setCursor(Qt::ArrowCursor);
784 
785     slotUpdateTotals();
786 }
787 
updateFilesystemStats(const QString & metaInfo,const QString & fsType,KIO::filesize_t total,KIO::filesize_t free)788 void ListPanel::updateFilesystemStats(const QString &metaInfo, const QString &fsType,
789                                       KIO::filesize_t total, KIO::filesize_t free)
790 {
791     QString statusText, mountPoint, freeSpaceText;
792 
793     if (!metaInfo.isEmpty()) {
794         statusText = metaInfo;
795         mountPoint = freeSpaceText = "";
796     } else {
797         const int perc = total == 0 ? 0 : (int)(((float)free / (float)total) * 100.0);
798         mountPoint = func->files()->mountPoint();
799         statusText = i18nc("%1=free space,%2=total space,%3=percentage of usage, "
800                            "%4=mountpoint,%5=filesystem type",
801                            "%1 free out of %2 (%3%) on %4 [(%5)]", KIO::convertSize(free),
802                            KIO::convertSize(total), perc, mountPoint, fsType);
803 
804         freeSpaceText = "    " + i18n("%1 free", KIO::convertSize(free));
805     }
806 
807     status->setText(statusText);
808     freeSpace->setText(freeSpaceText);
809     mediaButton->updateIcon(mountPoint);
810 }
811 
handleDrop(QDropEvent * event,bool onView)812 void ListPanel::handleDrop(QDropEvent *event, bool onView)
813 {
814     // check what was dropped
815     const QList<QUrl> urls = KUrlMimeData::urlsFromMimeData(event->mimeData());
816     if (urls.isEmpty()) {
817         event->ignore(); // not for us to handle!
818         return;
819     }
820 
821     // find dropping destination
822     QString destinationDir = "";
823     const bool dragFromThisPanel = event->source() == this;
824     const KrViewItem *item = onView ? view->getKrViewItemAt(event->pos()) : 0;
825     if (item) {
826         const FileItem *file = item->getFileItem();
827         if (file && !file->isDir() && dragFromThisPanel) {
828             event->ignore(); // dragging on files in same panel, ignore
829             return;
830         } else if (!file || file->isDir()) { // item is ".." dummy or a directory
831             destinationDir = item->name();
832         }
833     } else if (dragFromThisPanel) {
834         event->ignore(); // dragged from this panel onto an empty spot in this panel, ignore
835         return;
836     }
837 
838     QUrl destination = QUrl(virtualPath());
839     destination.setPath(destination.path() + '/' + destinationDir);
840 
841     func->files()->dropFiles(destination, event);
842 
843     if(KConfigGroup(krConfig, "Look&Feel").readEntry("UnselectBeforeOperation",
844                                                      _UnselectBeforeOperation)) {
845         KrPanel *p = dragFromThisPanel ? this : otherPanel();
846         p->view->saveSelection();
847         p->view->unselectAll();
848     }
849 }
850 
handleDrop(const QUrl & destination,QDropEvent * event)851 void ListPanel::handleDrop(const QUrl &destination, QDropEvent *event)
852 {
853     func->files()->dropFiles(destination, event);
854 }
855 
startDragging(QStringList names,QPixmap px)856 void ListPanel::startDragging(QStringList names, QPixmap px)
857 {
858     if (names.isEmpty()) {  // avoid dragging empty urls
859         return;
860     }
861 
862     QList<QUrl> urls = func->files()->getUrls(names);
863 
864     QDrag *drag = new QDrag(this);
865     QMimeData *mimeData = new QMimeData;
866     drag->setPixmap(px);
867     mimeData->setUrls(urls);
868     drag->setMimeData(mimeData);
869 
870     drag->start(Qt::MoveAction | Qt::CopyAction | Qt::LinkAction);
871 }
872 
873 // pops a right-click menu for items
popRightClickMenu(const QPoint & loc)874 void ListPanel::popRightClickMenu(const QPoint &loc)
875 {
876     // run it, on the mouse location
877     int j = QFontMetrics(font()).height() * 2;
878     PanelContextMenu::run(QPoint(loc.x() + 5, loc.y() + j), this);
879 }
880 
popEmptyRightClickMenu(const QPoint & loc)881 void ListPanel::popEmptyRightClickMenu(const QPoint &loc)
882 {
883     PanelContextMenu::run(loc, this);
884 }
885 
getCurrentName() const886 QString ListPanel::getCurrentName() const
887 {
888     const QString name = view->getCurrentItem();
889     return name == ".." ? QString() : name;
890 }
891 
getSelectedNames()892 QStringList ListPanel::getSelectedNames()
893 {
894     QStringList fileNames;
895     view->getSelectedItems(&fileNames);
896     return fileNames;
897 }
898 
prepareToDelete()899 void ListPanel::prepareToDelete()
900 {
901     const bool skipCurrent = (view->numSelected() == 0);
902     view->setNameToMakeCurrent(view->firstUnmarkedBelowCurrent(skipCurrent));
903 }
904 
keyPressEvent(QKeyEvent * e)905 void ListPanel::keyPressEvent(QKeyEvent *e)
906 {
907     switch (e->key()) {
908     case Qt::Key_Enter :
909     case Qt::Key_Return :
910         if (e->modifiers() & Qt::ControlModifier) {
911             if (e->modifiers() & Qt::AltModifier) {
912                 FileItem *fileitem =
913                     func->files()->getFileItem(view->getCurrentKrViewItem()->name());
914                 if (fileitem && fileitem->isDir())
915                     newTab(fileitem->getUrl(), true);
916             } else {
917                 SLOTS->insertFileName((e->modifiers() & Qt::ShiftModifier) != 0);
918             }
919         } else {
920             e->ignore();
921         }
922         break;
923     case Qt::Key_Right :
924     case Qt::Key_Left :
925         if (e->modifiers() == Qt::ControlModifier) {
926             // user pressed CTRL+Right/Left - refresh other panel to the selected path if it's a
927             // directory otherwise as this one
928             if ((isLeft() && e->key() == Qt::Key_Right) || (!isLeft() && e->key() == Qt::Key_Left)) {
929                 QUrl newPath;
930                 KrViewItem *it = view->getCurrentKrViewItem();
931 
932                 if (it->name() == "..") {
933                     newPath = KIO::upUrl(virtualPath());
934                 } else {
935                     FileItem *v = func->getFileItem(it);
936                     // If it's a directory different from ".."
937                     if (v && v->isDir() && v->getName() != "..") {
938                         newPath = v->getUrl();
939                     } else {
940                         // If it's a supported compressed file
941                         if (v && KRarcHandler::arcSupported(v->getMime()))   {
942                             newPath = func->browsableArchivePath(v->getUrl().fileName());
943                         } else {
944                             newPath = virtualPath();
945                         }
946                     }
947                 }
948                 otherPanel()->func->openUrl(newPath);
949             } else {
950                 func->openUrl(otherPanel()->virtualPath());
951             }
952             return;
953         } else
954             e->ignore();
955         break;
956     case Qt::Key_Down :
957         if (e->modifiers() == Qt::ControlModifier) {   // give the keyboard focus to the command line
958             if (MAIN_VIEW->cmdLine()->isVisible())
959                 MAIN_VIEW->cmdLineFocus();
960             else
961                 MAIN_VIEW->focusTerminalEmulator();
962             return;
963         } else if (e->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) {     // give the keyboard focus to TE
964             MAIN_VIEW->focusTerminalEmulator();
965         } else
966             e->ignore();
967         break;
968 
969     case Qt::Key_Up :
970         if (e->modifiers() == Qt::ControlModifier) {   // give the keyboard focus to the url navigator
971             editLocation();
972             return;
973         } else
974             e->ignore();
975         break;
976 
977     case Qt::Key_Escape:
978         cancelProgress();
979         break;
980 
981     default:
982         // if we got this, it means that the view is not doing
983         // the quick search thing, so send the characters to the commandline, if normal key
984         if (e->modifiers() == Qt::NoModifier)
985             MAIN_VIEW->cmdLine()->addText(e->text());
986 
987         //e->ignore();
988     }
989 }
990 
showEvent(QShowEvent * e)991 void ListPanel::showEvent(QShowEvent *e)
992 {
993     panelVisible();
994     QWidget::showEvent(e);
995 }
996 
hideEvent(QHideEvent * e)997 void ListPanel::hideEvent(QHideEvent *e)
998 {
999     panelHidden();
1000     QWidget::hideEvent(e);
1001 }
1002 
panelVisible()1003 void ListPanel::panelVisible()
1004 {
1005     func->setPaused(false);
1006 }
1007 
panelHidden()1008 void ListPanel::panelHidden()
1009 {
1010     func->setPaused(true);
1011 }
1012 
slotPreviewJobStarted(KJob * job)1013 void ListPanel::slotPreviewJobStarted(KJob *job)
1014 {
1015     previewJob = job;
1016     connect(job, SIGNAL(percent(KJob*,ulong)), SLOT(slotPreviewJobPercent(KJob*,ulong)));
1017     connect(job, &KJob::result, this, &ListPanel::slotPreviewJobResult);
1018     cancelProgressButton->setMaximumHeight(sidebarButton->height());
1019     cancelProgressButton->show();
1020     previewProgress->setValue(0);
1021     previewProgress->setFormat(i18n("loading previews: %p%"));
1022     previewProgress->setMaximumHeight(cancelProgressButton->height());
1023     previewProgress->show();
1024 }
1025 
slotPreviewJobPercent(KJob *,unsigned long percent)1026 void ListPanel::slotPreviewJobPercent(KJob* /*job*/, unsigned long percent)
1027 {
1028     previewProgress->setValue(percent);
1029 }
1030 
slotPreviewJobResult(KJob *)1031 void ListPanel::slotPreviewJobResult(KJob* /*job*/)
1032 {
1033     previewJob = 0;
1034     previewProgress->hide();
1035     if(!inlineRefreshJob)
1036         cancelProgressButton->hide();
1037 }
1038 
slotRefreshJobStarted(KIO::Job * job)1039 void ListPanel::slotRefreshJobStarted(KIO::Job* job)
1040 {
1041     // disable the parts of the panel we don't want touched
1042     status->setEnabled(false);
1043     urlNavigator->setEnabled(false);
1044     cdRootButton->setEnabled(false);
1045     cdHomeButton->setEnabled(false);
1046     cdUpButton->setEnabled(false);
1047     cdOtherButton->setEnabled(false);
1048     sidebarButton->setEnabled(false);
1049     if(sidebar)
1050         sidebar->setEnabled(false);
1051     bookmarksButton->setEnabled(false);
1052     historyButton->setEnabled(false);
1053     syncBrowseButton->setEnabled(false);
1054 
1055     // connect to the job interface to provide in-panel refresh notification
1056     connect(job, SIGNAL(infoMessage(KJob*,QString)),
1057             SLOT(inlineRefreshInfoMessage(KJob*,QString)));
1058     connect(job, SIGNAL(percent(KJob*,ulong)),
1059             SLOT(inlineRefreshPercent(KJob*,ulong)));
1060     connect(job, SIGNAL(result(KJob*)),
1061             this, SLOT(inlineRefreshListResult(KJob*)));
1062 
1063     inlineRefreshJob = job;
1064 
1065     totals->setText(i18n(">> Reading..."));
1066     cancelProgressButton->show();
1067 }
1068 
cancelProgress()1069 void ListPanel::cancelProgress()
1070 {
1071     if (inlineRefreshJob) {
1072         disconnect(inlineRefreshJob, 0, this, 0);
1073         inlineRefreshJob->kill(KJob::EmitResult);
1074         inlineRefreshListResult(0);
1075     }
1076     if(previewJob) {
1077         disconnect(previewJob, 0, this, 0);
1078         previewJob->kill(KJob::EmitResult);
1079         slotPreviewJobResult(0);
1080     }
1081 }
1082 
setNavigatorUrl(const QUrl & url)1083 void ListPanel::setNavigatorUrl(const QUrl &url)
1084 {
1085     _navigatorUrl = url;
1086    urlNavigator->setLocationUrl(url);
1087 }
1088 
inlineRefreshPercent(KJob *,unsigned long perc)1089 void ListPanel::inlineRefreshPercent(KJob*, unsigned long perc)
1090 {
1091     QString msg = i18n(">> Reading: %1 % complete...", perc);
1092     totals->setText(msg);
1093 }
1094 
inlineRefreshInfoMessage(KJob *,const QString & msg)1095 void ListPanel::inlineRefreshInfoMessage(KJob*, const QString &msg)
1096 {
1097     totals->setText(i18n(">> Reading: %1", msg));
1098 }
1099 
inlineRefreshListResult(KJob *)1100 void ListPanel::inlineRefreshListResult(KJob*)
1101 {
1102     if(inlineRefreshJob)
1103         disconnect(inlineRefreshJob, 0, this, 0);
1104     inlineRefreshJob = 0;
1105     // reenable everything
1106     status->setEnabled(true);
1107     urlNavigator->setEnabled(true);
1108     cdRootButton->setEnabled(true);
1109     cdHomeButton->setEnabled(true);
1110     cdUpButton->setEnabled(true);
1111     cdOtherButton->setEnabled(true);
1112     sidebarButton->setEnabled(true);
1113     if(sidebar)
1114         sidebar->setEnabled(true);
1115     bookmarksButton->setEnabled(true);
1116     historyButton->setEnabled(true);
1117     syncBrowseButton->setEnabled(true);
1118 
1119     if(!previewJob)
1120         cancelProgressButton->hide();
1121 }
1122 
jumpBack()1123 void ListPanel::jumpBack()
1124 {
1125     func->openUrl(_jumpBackURL);
1126 }
1127 
setJumpBack(QUrl url)1128 void ListPanel::setJumpBack(QUrl url)
1129 {
1130     _jumpBackURL = url;
1131 }
1132 
slotFilesystemError(QString msg)1133 void ListPanel::slotFilesystemError(QString msg)
1134 {
1135     refreshColors();
1136     fileSystemError->setText(i18n("Error: %1", msg));
1137     fileSystemError->show();
1138 }
1139 
showButtonMenu(QToolButton * b)1140 void ListPanel::showButtonMenu(QToolButton *b)
1141 {
1142     if(this != ACTIVE_PANEL)
1143         slotFocusOnMe();
1144 
1145     if(b->isHidden())
1146         b->menu()->exec(mapToGlobal(clientArea->pos()));
1147     else
1148         b->click();
1149 }
1150 
openBookmarks()1151 void ListPanel::openBookmarks()
1152 {
1153     showButtonMenu(bookmarksButton);
1154 }
1155 
openHistory()1156 void ListPanel::openHistory()
1157 {
1158     showButtonMenu(historyButton);
1159 }
1160 
openMedia()1161 void ListPanel::openMedia()
1162 {
1163     showButtonMenu(mediaButton);
1164 }
1165 
rightclickMenu()1166 void ListPanel::rightclickMenu()
1167 {
1168     if (view->getCurrentKrViewItem())
1169         popRightClickMenu(mapToGlobal(view->getCurrentKrViewItem()->itemRect().topLeft()));
1170 }
1171 
toggleSyncBrowse()1172 void ListPanel::toggleSyncBrowse()
1173 {
1174     syncBrowseButton->toggle();
1175 }
1176 
editLocation()1177 void ListPanel::editLocation()
1178 {
1179     urlNavigator->setUrlEditable(true);
1180     urlNavigator->setFocus();
1181     urlNavigator->editor()->lineEdit()->selectAll();
1182 }
1183 
showSearchBar()1184 void ListPanel::showSearchBar()
1185 {
1186     searchBar->showBar(KrSearchBar::MODE_SEARCH);
1187 }
1188 
showSearchBarSelection()1189 void ListPanel::showSearchBarSelection()
1190 {
1191     searchBar->showBar(KrSearchBar::MODE_SELECT);
1192 }
1193 
showSearchBarFilter()1194 void ListPanel::showSearchBarFilter()
1195 {
1196     searchBar->showBar(KrSearchBar::MODE_FILTER);
1197 }
1198 
saveSettings(KConfigGroup cfg,bool saveHistory)1199 void ListPanel::saveSettings(KConfigGroup cfg, bool saveHistory)
1200 {
1201     QUrl url = virtualPath();
1202     url.setPassword(QString()); // make sure no password is saved
1203     cfg.writeEntry("Url", url.toString());
1204     cfg.writeEntry("Type", getType());
1205     cfg.writeEntry("Properties", getProperties());
1206     cfg.writeEntry("PinnedUrl", pinnedUrl().toString());
1207     if(saveHistory)
1208         func->history->save(KConfigGroup(&cfg, "History"));
1209     view->saveSettings(KConfigGroup(&cfg, "View"));
1210 
1211     // splitter/sidebar state
1212     if (sidebar && !sidebar->isHidden()) {
1213         sidebar->saveSettings(KConfigGroup(&cfg, "PanelPopup"));
1214         cfg.writeEntry("PopupPosition", sidebarPosition());
1215         cfg.writeEntry("SplitterSizes", sidebarSplitter->saveState());
1216         cfg.writeEntry("PopupPage", sidebar->currentPage());
1217     } else {
1218         cfg.deleteEntry("PopupPosition");
1219         cfg.deleteEntry("SplitterSizes");
1220         cfg.deleteEntry("PopupPage");
1221     }
1222 }
1223 
restoreSettings(KConfigGroup cfg)1224 void ListPanel::restoreSettings(KConfigGroup cfg)
1225 {
1226     changeType(cfg.readEntry("Type", defaultPanelType()));
1227     view->restoreSettings(KConfigGroup(&cfg, "View"));
1228 
1229     // "locked" property must be set after URL path is restored!
1230     // This panel can be reused when loading a profile,
1231     // so we reset its properties before calling openUrl().
1232     setProperties(0);
1233 
1234     _lastLocalPath = ROOT_DIR;
1235     if(func->history->restore(KConfigGroup(&cfg, "History"))) {
1236         func->refresh();
1237     } else {
1238         QUrl url(cfg.readEntry("Url", "invalid"));
1239         if (!url.isValid())
1240             url = QUrl::fromLocalFile(ROOT_DIR);
1241         func->openUrl(url);
1242     }
1243 
1244     setJumpBack(func->history->currentUrl());
1245 
1246     setProperties(cfg.readEntry("Properties", 0));
1247 
1248     if (isPinned()) {
1249         QUrl pinnedUrl(cfg.readEntry("PinnedUrl", "invalid"));
1250         if (!pinnedUrl.isValid()) {
1251             pinnedUrl = func->history->currentUrl();
1252         }
1253         func->openUrl(pinnedUrl);
1254         setPinnedUrl(pinnedUrl);
1255     }
1256 
1257     if (cfg.hasKey("PopupPosition")) { // sidebar was visible, restore
1258         toggleSidebar(); // create and show
1259         sidebar->restoreSettings(KConfigGroup(&cfg, "PanelPopup"));
1260         setSidebarPosition(cfg.readEntry("PopupPosition", 42 /* dummy */));
1261         sidebarSplitter->restoreState(cfg.readEntry("SplitterSizes", QByteArray()));
1262         sidebar->setCurrentPage(cfg.readEntry("PopupPage", 0));
1263     }
1264 }
1265 
slotCurrentChanged(KrViewItem * item)1266 void ListPanel::slotCurrentChanged(KrViewItem *item)
1267 {
1268     // update status bar
1269     if (item)
1270         krApp->statusBarUpdate(item->description());
1271 
1272     // update sidebar; which panel to display on?
1273     Sidebar *p;
1274     if (sidebar && !sidebar->isHidden()) {
1275         p = sidebar;
1276     } else if(otherPanel()->gui->sidebar && !otherPanel()->gui->sidebar->isHidden()) {
1277         p = otherPanel()->gui->sidebar;
1278     } else {
1279         return;
1280     }
1281 
1282     p->update(item ? func->files()->getFileItem(item->name()) : nullptr);
1283 }
1284 
otherPanelChanged()1285 void ListPanel::otherPanelChanged()
1286 {
1287     func->syncURL = QUrl();
1288 }
1289 
getFocusCandidates(QVector<QWidget * > & widgets)1290 void ListPanel::getFocusCandidates(QVector<QWidget*> &widgets)
1291 {
1292     if(urlNavigator->editor()->isVisible())
1293         widgets << urlNavigator->editor();
1294     if(view->widget()->isVisible())
1295         widgets << view->widget();
1296     if(sidebar && sidebar->isVisible())
1297         widgets << sidebar;
1298 }
1299 
updateButtons()1300 void ListPanel::updateButtons()
1301 {
1302     backButton->setEnabled(func->history->canGoBack());
1303     forwardButton->setEnabled(func->history->canGoForward());
1304     historyButton->setEnabled(func->history->count() > 1);
1305     cdRootButton->setEnabled(!virtualPath().matches(QUrl::fromLocalFile(ROOT_DIR),
1306                                                     QUrl::StripTrailingSlash));
1307     cdUpButton->setEnabled(!func->files()->isRoot());
1308     cdHomeButton->setEnabled(!func->atHome());
1309 }
1310 
newTab(KrViewItem * it)1311 void ListPanel::newTab(KrViewItem *it)
1312 {
1313     if (!it)
1314         return;
1315     else if (it->name() == "..") {
1316         newTab(KIO::upUrl(virtualPath()), true);
1317     } else if (func->getFileItem(it)->isDir()) {
1318         QUrl url = virtualPath();
1319         url = url.adjusted(QUrl::StripTrailingSlash);
1320         url.setPath(url.path() + '/' + (it->name()));
1321         newTab(url, true);
1322     }
1323 }
1324 
newTab(const QUrl & url,bool nextToThis)1325 void ListPanel::newTab(const QUrl &url, bool nextToThis)
1326 {
1327     _manager->newTab(url, nextToThis ? this : 0);
1328 }
1329 
slotNavigatorUrlChanged(const QUrl & url)1330 void ListPanel::slotNavigatorUrlChanged(const QUrl &url)
1331 {
1332     if (url == _navigatorUrl)
1333         return; // this is the URL we just set ourself
1334 
1335     if (!isNavigatorEditModeSet()) {
1336         urlNavigator->setUrlEditable(false);
1337     }
1338 
1339     func->openUrl(KrServices::escapeFileUrl(url), QString(), true);
1340 }
1341 
resetNavigatorMode()1342 void ListPanel::resetNavigatorMode()
1343 {
1344     if (isNavigatorEditModeSet())
1345         return;
1346 
1347     // set to "navigate" mode if url wasn't changed
1348     if (urlNavigator->uncommittedUrl().matches(virtualPath(), QUrl::StripTrailingSlash)) {
1349         // NOTE: this also sets focus to the navigator
1350         urlNavigator->setUrlEditable(false);
1351         slotFocusOnMe();
1352     }
1353 }
1354 
sidebarPosition() const1355 int ListPanel::sidebarPosition() const
1356 {
1357     int pos = sidebarSplitter->orientation() == Qt::Vertical ? 1 : 0;
1358     return pos + (qobject_cast<Sidebar*>(sidebarSplitter->widget(0)) == NULL ? 2 : 0);
1359 }
1360 
setSidebarPosition(int pos)1361 void ListPanel::setSidebarPosition(int pos)
1362 {
1363     sidebarSplitter->setOrientation(pos % 2 == 0 ? Qt::Horizontal : Qt::Vertical);
1364     if ((pos < 2) != (qobject_cast<Sidebar*>(sidebarSplitter->widget(0)) != NULL)) {
1365         sidebarSplitter->insertWidget(0, sidebarSplitter->widget(1)); // swapping widgets in splitter
1366     }
1367 }
1368 
connectQuickSizeCalculator(SizeCalculator * sizeCalculator)1369 void ListPanel::connectQuickSizeCalculator(SizeCalculator *sizeCalculator)
1370 {
1371     connect(sizeCalculator, &SizeCalculator::started, this, [=]() {
1372         quickSizeCalcProgress->reset();
1373         quickSizeCalcProgress->show();
1374         cancelQuickSizeCalcButton->show();
1375     });
1376     connect(cancelQuickSizeCalcButton, &QToolButton::clicked, sizeCalculator, &SizeCalculator::cancel);
1377     connect(sizeCalculator, &SizeCalculator::progressChanged, quickSizeCalcProgress, &QProgressBar::setValue);
1378     connect(sizeCalculator, &SizeCalculator::finished, this, [=]() {
1379         cancelQuickSizeCalcButton->hide();
1380         quickSizeCalcProgress->hide();
1381     });
1382 }
1383