]> cloud.milkyroute.net Git - dolphin.git/blob - src/dolphinmainwindow.cpp
da6c5319d6eb52c4a6830f2e02abc7ad84ca67a2
[dolphin.git] / src / dolphinmainwindow.cpp
1 /***************************************************************************
2 * Copyright (C) 2006 by Peter Penz <peter.penz19@gmail.com> *
3 * Copyright (C) 2006 by Stefan Monov <logixoul@gmail.com> *
4 * Copyright (C) 2006 by Cvetoslav Ludmiloff <ludmiloff@gmail.com> *
5 * *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the *
18 * Free Software Foundation, Inc., *
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
20 ***************************************************************************/
21
22 #include "dolphinmainwindow.h"
23
24 #include "dolphindockwidget.h"
25 #include "dolphincontextmenu.h"
26 #include "dolphinnewfilemenu.h"
27 #include "dolphinrecenttabsmenu.h"
28 #include "dolphintabwidget.h"
29 #include "dolphinviewcontainer.h"
30 #include "dolphintabpage.h"
31 #include "panels/folders/folderspanel.h"
32 #include "panels/places/placespanel.h"
33 #include "panels/information/informationpanel.h"
34 #include "settings/dolphinsettingsdialog.h"
35 #include "statusbar/dolphinstatusbar.h"
36 #include "views/dolphinviewactionhandler.h"
37 #include "views/dolphinremoteencoding.h"
38 #include "views/draganddrophelper.h"
39 #include "views/viewproperties.h"
40 #include "views/dolphinnewfilemenuobserver.h"
41
42 #ifndef Q_OS_WIN
43 #include "panels/terminal/terminalpanel.h"
44 #endif
45
46 #include "dolphin_generalsettings.h"
47
48 #include <KActionCollection>
49 #include <KActionMenu>
50 #include <KConfig>
51 #include <kdualaction.h>
52 #include <KJobWidgets>
53 #include <QLineEdit>
54 #include <KToolBar>
55 #include <KIO/JobUiDelegate>
56 #include <KLocalizedString>
57 #include <KProtocolManager>
58 #include <QMenu>
59 #include <KMessageBox>
60 #include <KFileItemListProperties>
61 #include <KRun>
62 #include <KShell>
63 #include <KStandardAction>
64 #include <KToggleAction>
65 #include <KUrlNavigator>
66 #include <KToolInvocation>
67 #include <KUrlComboBox>
68
69 #include <QApplication>
70 #include <QMenuBar>
71 #include <QClipboard>
72 #include <QToolButton>
73 #include <QTimer>
74 #include <QStandardPaths>
75 #include <QPushButton>
76 #include <QCloseEvent>
77 #include <QShowEvent>
78 #include <QDialog>
79
80 namespace {
81 // Used for GeneralSettings::version() to determine whether
82 // an updated version of Dolphin is running.
83 const int CurrentDolphinVersion = 200;
84 };
85
86 DolphinMainWindow::DolphinMainWindow() :
87 KXmlGuiWindow(0),
88 m_newFileMenu(0),
89 m_tabWidget(0),
90 m_activeViewContainer(0),
91 m_actionHandler(0),
92 m_remoteEncoding(0),
93 m_settingsDialog(),
94 m_controlButton(0),
95 m_updateToolBarTimer(0),
96 m_lastHandleUrlStatJob(0)
97 {
98 setObjectName("Dolphin#");
99
100 connect(&DolphinNewFileMenuObserver::instance(), &DolphinNewFileMenuObserver::errorMessage,
101 this, &DolphinMainWindow::showErrorMessage);
102
103 KIO::FileUndoManager* undoManager = KIO::FileUndoManager::self();
104 undoManager->setUiInterface(new UndoUiInterface());
105
106 connect(undoManager, static_cast<void(KIO::FileUndoManager::*)(bool)>(&KIO::FileUndoManager::undoAvailable),
107 this, &DolphinMainWindow::slotUndoAvailable);
108 connect(undoManager, &KIO::FileUndoManager::undoTextChanged,
109 this, &DolphinMainWindow::slotUndoTextChanged);
110 connect(undoManager, &KIO::FileUndoManager::jobRecordingStarted,
111 this, &DolphinMainWindow::clearStatusBar);
112 connect(undoManager, &KIO::FileUndoManager::jobRecordingFinished,
113 this, &DolphinMainWindow::showCommand);
114
115 GeneralSettings* generalSettings = GeneralSettings::self();
116 const bool firstRun = (generalSettings->version() < 200);
117 if (firstRun) {
118 generalSettings->setViewPropsTimestamp(QDateTime::currentDateTime());
119 }
120
121 setAcceptDrops(true);
122
123 m_tabWidget = new DolphinTabWidget(this);
124 connect(m_tabWidget, SIGNAL(activeViewChanged(DolphinViewContainer*)),
125 this, SLOT(activeViewChanged(DolphinViewContainer*)));
126 connect(m_tabWidget, SIGNAL(tabCountChanged(int)),
127 this, SLOT(tabCountChanged(int)));
128 connect(m_tabWidget, SIGNAL(currentUrlChanged(QUrl)),
129 this, SLOT(setUrlAsCaption(QUrl)));
130 setCentralWidget(m_tabWidget);
131
132 setupActions();
133
134 m_actionHandler = new DolphinViewActionHandler(actionCollection(), this);
135 connect(m_actionHandler, &DolphinViewActionHandler::actionBeingHandled, this, &DolphinMainWindow::clearStatusBar);
136 connect(m_actionHandler, &DolphinViewActionHandler::createDirectory, this, &DolphinMainWindow::createDirectory);
137
138 m_remoteEncoding = new DolphinRemoteEncoding(this, m_actionHandler);
139 connect(this, &DolphinMainWindow::urlChanged,
140 m_remoteEncoding, &DolphinRemoteEncoding::slotAboutToOpenUrl);
141
142 setupDockWidgets();
143
144 setupGUI(Keys | Save | Create | ToolBar);
145 stateChanged("new_file");
146
147 QClipboard* clipboard = QApplication::clipboard();
148 connect(clipboard, &QClipboard::dataChanged,
149 this, &DolphinMainWindow::updatePasteAction);
150
151 QAction* showFilterBarAction = actionCollection()->action("show_filter_bar");
152 showFilterBarAction->setChecked(generalSettings->filterBar());
153
154 if (firstRun) {
155 menuBar()->setVisible(false);
156 // Assure a proper default size if Dolphin runs the first time
157 resize(750, 500);
158 }
159
160 const bool showMenu = !menuBar()->isHidden();
161 QAction* showMenuBarAction = actionCollection()->action(KStandardAction::name(KStandardAction::ShowMenubar));
162 showMenuBarAction->setChecked(showMenu); // workaround for bug #171080
163 if (!showMenu) {
164 createControlButton();
165 }
166 }
167
168 DolphinMainWindow::~DolphinMainWindow()
169 {
170 }
171
172 void DolphinMainWindow::openDirectories(const QList<QUrl>& dirs)
173 {
174 m_tabWidget->openDirectories(dirs);
175 }
176
177 void DolphinMainWindow::openFiles(const QList<QUrl>& files)
178 {
179 m_tabWidget->openFiles(files);
180 }
181
182 void DolphinMainWindow::showCommand(CommandType command)
183 {
184 DolphinStatusBar* statusBar = m_activeViewContainer->statusBar();
185 switch (command) {
186 case KIO::FileUndoManager::Copy:
187 statusBar->setText(i18nc("@info:status", "Successfully copied."));
188 break;
189 case KIO::FileUndoManager::Move:
190 statusBar->setText(i18nc("@info:status", "Successfully moved."));
191 break;
192 case KIO::FileUndoManager::Link:
193 statusBar->setText(i18nc("@info:status", "Successfully linked."));
194 break;
195 case KIO::FileUndoManager::Trash:
196 statusBar->setText(i18nc("@info:status", "Successfully moved to trash."));
197 break;
198 case KIO::FileUndoManager::Rename:
199 statusBar->setText(i18nc("@info:status", "Successfully renamed."));
200 break;
201
202 case KIO::FileUndoManager::Mkdir:
203 statusBar->setText(i18nc("@info:status", "Created folder."));
204 break;
205
206 default:
207 break;
208 }
209 }
210
211 void DolphinMainWindow::pasteIntoFolder()
212 {
213 m_activeViewContainer->view()->pasteIntoFolder();
214 }
215
216 void DolphinMainWindow::changeUrl(const QUrl &url)
217 {
218 if (!KProtocolManager::supportsListing(url)) {
219 // The URL navigator only checks for validity, not
220 // if the URL can be listed. An error message is
221 // shown due to DolphinViewContainer::restoreView().
222 return;
223 }
224
225 m_activeViewContainer->setUrl(url);
226 updateEditActions();
227 updatePasteAction();
228 updateViewActions();
229 updateGoActions();
230
231 emit urlChanged(url);
232 }
233
234 void DolphinMainWindow::slotTerminalDirectoryChanged(const QUrl& url)
235 {
236 m_activeViewContainer->setAutoGrabFocus(false);
237 changeUrl(url);
238 m_activeViewContainer->setAutoGrabFocus(true);
239 }
240
241 void DolphinMainWindow::slotEditableStateChanged(bool editable)
242 {
243 KToggleAction* editableLocationAction =
244 static_cast<KToggleAction*>(actionCollection()->action("editable_location"));
245 editableLocationAction->setChecked(editable);
246 }
247
248 void DolphinMainWindow::slotSelectionChanged(const KFileItemList& selection)
249 {
250 updateEditActions();
251
252 const int selectedUrlsCount = m_tabWidget->currentTabPage()->selectedItemsCount();
253
254 QAction* compareFilesAction = actionCollection()->action("compare_files");
255 if (selectedUrlsCount == 2) {
256 compareFilesAction->setEnabled(isKompareInstalled());
257 } else {
258 compareFilesAction->setEnabled(false);
259 }
260
261 emit selectionChanged(selection);
262 }
263
264 void DolphinMainWindow::updateHistory()
265 {
266 const KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator();
267 const int index = urlNavigator->historyIndex();
268
269 QAction* backAction = actionCollection()->action("go_back");
270 if (backAction) {
271 backAction->setToolTip(i18nc("@info", "Go back"));
272 backAction->setEnabled(index < urlNavigator->historySize() - 1);
273 }
274
275 QAction* forwardAction = actionCollection()->action("go_forward");
276 if (forwardAction) {
277 forwardAction->setToolTip(i18nc("@info", "Go forward"));
278 forwardAction->setEnabled(index > 0);
279 }
280 }
281
282 void DolphinMainWindow::updateFilterBarAction(bool show)
283 {
284 QAction* showFilterBarAction = actionCollection()->action("show_filter_bar");
285 showFilterBarAction->setChecked(show);
286 }
287
288 void DolphinMainWindow::openNewMainWindow()
289 {
290 KRun::run("dolphin %u", QList<QUrl>(), this);
291 }
292
293 void DolphinMainWindow::openNewActivatedTab()
294 {
295 m_tabWidget->openNewActivatedTab();
296 }
297
298 void DolphinMainWindow::openNewTab(const QUrl& url)
299 {
300 m_tabWidget->openNewTab(url);
301 }
302
303 void DolphinMainWindow::openNewActivatedTab(const QUrl& url)
304 {
305 m_tabWidget->openNewActivatedTab(url);
306 }
307
308 void DolphinMainWindow::openInNewTab()
309 {
310 const KFileItemList& list = m_activeViewContainer->view()->selectedItems();
311 if (list.isEmpty()) {
312 openNewTab(m_activeViewContainer->url());
313 } else {
314 foreach (const KFileItem& item, list) {
315 const QUrl& url = DolphinView::openItemAsFolderUrl(item);
316 if (!url.isEmpty()) {
317 openNewTab(url);
318 }
319 }
320 }
321 }
322
323 void DolphinMainWindow::openInNewWindow()
324 {
325 QUrl newWindowUrl;
326
327 const KFileItemList list = m_activeViewContainer->view()->selectedItems();
328 if (list.isEmpty()) {
329 newWindowUrl = m_activeViewContainer->url();
330 } else if (list.count() == 1) {
331 const KFileItem& item = list.first();
332 newWindowUrl = DolphinView::openItemAsFolderUrl(item);
333 }
334
335 if (!newWindowUrl.isEmpty()) {
336 KRun::run("dolphin %u", {newWindowUrl}, this);
337 }
338 }
339
340 void DolphinMainWindow::showEvent(QShowEvent* event)
341 {
342 KXmlGuiWindow::showEvent(event);
343
344 if (!event->spontaneous()) {
345 m_activeViewContainer->view()->setFocus();
346 }
347 }
348
349 void DolphinMainWindow::closeEvent(QCloseEvent* event)
350 {
351 // Find out if Dolphin is closed directly by the user or
352 // by the session manager because the session is closed
353 bool closedByUser = true;
354 if (qApp->isSessionRestored()) {
355 closedByUser = false;
356 }
357
358 if (m_tabWidget->count() > 1 && GeneralSettings::confirmClosingMultipleTabs() && closedByUser) {
359 // Ask the user if he really wants to quit and close all tabs.
360 // Open a confirmation dialog with 3 buttons:
361 // QDialogButtonBox::Yes -> Quit
362 // QDialogButtonBox::No -> Close only the current tab
363 // QDialogButtonBox::Cancel -> do nothing
364 QDialog *dialog = new QDialog(this, Qt::Dialog);
365 dialog->setWindowTitle(i18nc("@title:window", "Confirmation"));
366 dialog->setModal(true);
367 QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Yes | QDialogButtonBox::No | QDialogButtonBox::Cancel);
368 KGuiItem::assign(buttons->button(QDialogButtonBox::Yes), KStandardGuiItem::quit());
369 KGuiItem::assign(buttons->button(QDialogButtonBox::No), KGuiItem(i18n("C&lose Current Tab"), QIcon::fromTheme("tab-close")));
370 KGuiItem::assign(buttons->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel());
371 buttons->button(QDialogButtonBox::Yes)->setDefault(true);
372
373 bool doNotAskAgainCheckboxResult = false;
374
375 const int result = KMessageBox::createKMessageBox(dialog,
376 buttons,
377 QMessageBox::Warning,
378 i18n("You have multiple tabs open in this window, are you sure you want to quit?"),
379 QStringList(),
380 i18n("Do not ask again"),
381 &doNotAskAgainCheckboxResult,
382 KMessageBox::Notify);
383
384 if (doNotAskAgainCheckboxResult) {
385 GeneralSettings::setConfirmClosingMultipleTabs(false);
386 }
387
388 switch (result) {
389 case QDialogButtonBox::Yes:
390 // Quit
391 break;
392 case QDialogButtonBox::No:
393 // Close only the current tab
394 m_tabWidget->closeTab();
395 default:
396 event->ignore();
397 return;
398 }
399 }
400
401 GeneralSettings::setVersion(CurrentDolphinVersion);
402 GeneralSettings::self()->save();
403
404 KXmlGuiWindow::closeEvent(event);
405 }
406
407 void DolphinMainWindow::saveProperties(KConfigGroup& group)
408 {
409 m_tabWidget->saveProperties(group);
410 }
411
412 void DolphinMainWindow::readProperties(const KConfigGroup& group)
413 {
414 m_tabWidget->readProperties(group);
415 }
416
417 void DolphinMainWindow::updateNewMenu()
418 {
419 m_newFileMenu->setViewShowsHiddenFiles(activeViewContainer()->view()->hiddenFilesShown());
420 m_newFileMenu->checkUpToDate();
421 m_newFileMenu->setPopupFiles(activeViewContainer()->url());
422 }
423
424 void DolphinMainWindow::createDirectory()
425 {
426 m_newFileMenu->setViewShowsHiddenFiles(activeViewContainer()->view()->hiddenFilesShown());
427 m_newFileMenu->setPopupFiles(activeViewContainer()->url());
428 m_newFileMenu->createDirectory();
429 }
430
431 void DolphinMainWindow::quit()
432 {
433 close();
434 }
435
436 void DolphinMainWindow::showErrorMessage(const QString& message)
437 {
438 m_activeViewContainer->showMessage(message, DolphinViewContainer::Error);
439 }
440
441 void DolphinMainWindow::slotUndoAvailable(bool available)
442 {
443 QAction* undoAction = actionCollection()->action(KStandardAction::name(KStandardAction::Undo));
444 if (undoAction) {
445 undoAction->setEnabled(available);
446 }
447 }
448
449 void DolphinMainWindow::slotUndoTextChanged(const QString& text)
450 {
451 QAction* undoAction = actionCollection()->action(KStandardAction::name(KStandardAction::Undo));
452 if (undoAction) {
453 undoAction->setText(text);
454 }
455 }
456
457 void DolphinMainWindow::undo()
458 {
459 clearStatusBar();
460 KIO::FileUndoManager::self()->uiInterface()->setParentWidget(this);
461 KIO::FileUndoManager::self()->undo();
462 }
463
464 void DolphinMainWindow::cut()
465 {
466 m_activeViewContainer->view()->cutSelectedItems();
467 }
468
469 void DolphinMainWindow::copy()
470 {
471 m_activeViewContainer->view()->copySelectedItems();
472 }
473
474 void DolphinMainWindow::paste()
475 {
476 m_activeViewContainer->view()->paste();
477 }
478
479 void DolphinMainWindow::find()
480 {
481 m_activeViewContainer->setSearchModeEnabled(true);
482 }
483
484 void DolphinMainWindow::updatePasteAction()
485 {
486 QAction* pasteAction = actionCollection()->action(KStandardAction::name(KStandardAction::Paste));
487 QPair<bool, QString> pasteInfo = m_activeViewContainer->view()->pasteInfo();
488 pasteAction->setEnabled(pasteInfo.first);
489 pasteAction->setText(pasteInfo.second);
490 }
491
492 void DolphinMainWindow::slotDirectoryLoadingCompleted()
493 {
494 updatePasteAction();
495 }
496
497 void DolphinMainWindow::selectAll()
498 {
499 clearStatusBar();
500
501 // if the URL navigator is editable and focused, select the whole
502 // URL instead of all items of the view
503
504 KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator();
505 QLineEdit* lineEdit = urlNavigator->editor()->lineEdit(); // krazy:exclude=qclasses
506 const bool selectUrl = urlNavigator->isUrlEditable() &&
507 lineEdit->hasFocus();
508 if (selectUrl) {
509 lineEdit->selectAll();
510 } else {
511 m_activeViewContainer->view()->selectAll();
512 }
513 }
514
515 void DolphinMainWindow::invertSelection()
516 {
517 clearStatusBar();
518 m_activeViewContainer->view()->invertSelection();
519 }
520
521 void DolphinMainWindow::toggleSplitView()
522 {
523 DolphinTabPage* tabPage = m_tabWidget->currentTabPage();
524 tabPage->setSplitViewEnabled(!tabPage->splitViewEnabled());
525
526 updateViewActions();
527 }
528
529 void DolphinMainWindow::reloadView()
530 {
531 clearStatusBar();
532 m_activeViewContainer->view()->reload();
533 }
534
535 void DolphinMainWindow::stopLoading()
536 {
537 m_activeViewContainer->view()->stopLoading();
538 }
539
540 void DolphinMainWindow::enableStopAction()
541 {
542 actionCollection()->action("stop")->setEnabled(true);
543 }
544
545 void DolphinMainWindow::disableStopAction()
546 {
547 actionCollection()->action("stop")->setEnabled(false);
548 }
549
550 void DolphinMainWindow::showFilterBar()
551 {
552 m_activeViewContainer->setFilterBarVisible(true);
553 }
554
555 void DolphinMainWindow::toggleEditLocation()
556 {
557 clearStatusBar();
558
559 QAction* action = actionCollection()->action("editable_location");
560 KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator();
561 urlNavigator->setUrlEditable(action->isChecked());
562 }
563
564 void DolphinMainWindow::replaceLocation()
565 {
566 KUrlNavigator* navigator = m_activeViewContainer->urlNavigator();
567 navigator->setUrlEditable(true);
568 navigator->setFocus();
569
570 // select the whole text of the combo box editor
571 QLineEdit* lineEdit = navigator->editor()->lineEdit(); // krazy:exclude=qclasses
572 lineEdit->selectAll();
573 }
574
575 void DolphinMainWindow::togglePanelLockState()
576 {
577 const bool newLockState = !GeneralSettings::lockPanels();
578 foreach (QObject* child, children()) {
579 DolphinDockWidget* dock = qobject_cast<DolphinDockWidget*>(child);
580 if (dock) {
581 dock->setLocked(newLockState);
582 }
583 }
584
585 GeneralSettings::setLockPanels(newLockState);
586 }
587
588 void DolphinMainWindow::goBack()
589 {
590 KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator();
591 urlNavigator->goBack();
592
593 if (urlNavigator->locationState().isEmpty()) {
594 // An empty location state indicates a redirection URL,
595 // which must be skipped too
596 urlNavigator->goBack();
597 }
598 }
599
600 void DolphinMainWindow::goForward()
601 {
602 m_activeViewContainer->urlNavigator()->goForward();
603 }
604
605 void DolphinMainWindow::goUp()
606 {
607 m_activeViewContainer->urlNavigator()->goUp();
608 }
609
610 void DolphinMainWindow::goHome()
611 {
612 m_activeViewContainer->urlNavigator()->goHome();
613 }
614
615 void DolphinMainWindow::goBack(Qt::MouseButtons buttons)
616 {
617 // The default case (left button pressed) is handled in goBack().
618 if (buttons == Qt::MiddleButton) {
619 KUrlNavigator* urlNavigator = activeViewContainer()->urlNavigator();
620 const int index = urlNavigator->historyIndex() + 1;
621 openNewTab(urlNavigator->locationUrl(index));
622 }
623 }
624
625 void DolphinMainWindow::goForward(Qt::MouseButtons buttons)
626 {
627 // The default case (left button pressed) is handled in goForward().
628 if (buttons == Qt::MiddleButton) {
629 KUrlNavigator* urlNavigator = activeViewContainer()->urlNavigator();
630 const int index = urlNavigator->historyIndex() - 1;
631 openNewTab(urlNavigator->locationUrl(index));
632 }
633 }
634
635 void DolphinMainWindow::goUp(Qt::MouseButtons buttons)
636 {
637 // The default case (left button pressed) is handled in goUp().
638 if (buttons == Qt::MiddleButton) {
639 openNewTab(KIO::upUrl(activeViewContainer()->url()));
640 }
641 }
642
643 void DolphinMainWindow::goHome(Qt::MouseButtons buttons)
644 {
645 // The default case (left button pressed) is handled in goHome().
646 if (buttons == Qt::MiddleButton) {
647 openNewTab(GeneralSettings::self()->homeUrl());
648 }
649 }
650
651 void DolphinMainWindow::compareFiles()
652 {
653 const KFileItemList items = m_tabWidget->currentTabPage()->selectedItems();
654 if (items.count() != 2) {
655 // The action is disabled in this case, but it could have been triggered
656 // via D-Bus, see https://bugs.kde.org/show_bug.cgi?id=325517
657 return;
658 }
659
660 QUrl urlA = items.at(0).url();
661 QUrl urlB = items.at(1).url();
662
663 QString command("kompare -c \"");
664 command.append(urlA.toDisplayString(QUrl::PreferLocalFile));
665 command.append("\" \"");
666 command.append(urlB.toDisplayString(QUrl::PreferLocalFile));
667 command.append('\"');
668 KRun::runCommand(command, "Kompare", "kompare", this);
669 }
670
671 void DolphinMainWindow::toggleShowMenuBar()
672 {
673 const bool visible = menuBar()->isVisible();
674 menuBar()->setVisible(!visible);
675 if (visible) {
676 createControlButton();
677 } else {
678 deleteControlButton();
679 }
680 }
681
682 void DolphinMainWindow::openTerminal()
683 {
684 QString dir(QDir::homePath());
685
686 // If the given directory is not local, it can still be the URL of an
687 // ioslave using UDS_LOCAL_PATH which to be converted first.
688 KIO::StatJob* statJob = KIO::mostLocalUrl(m_activeViewContainer->url());
689 KJobWidgets::setWindow(statJob, this);
690 statJob->exec();
691 QUrl url = statJob->mostLocalUrl();
692
693 //If the URL is local after the above conversion, set the directory.
694 if (url.isLocalFile()) {
695 dir = url.toLocalFile();
696 }
697
698 KToolInvocation::invokeTerminal(QString(), dir);
699 }
700
701 void DolphinMainWindow::editSettings()
702 {
703 if (!m_settingsDialog) {
704 DolphinViewContainer* container = activeViewContainer();
705 container->view()->writeSettings();
706
707 const QUrl url = container->url();
708 DolphinSettingsDialog* settingsDialog = new DolphinSettingsDialog(url, this);
709 connect(settingsDialog, &DolphinSettingsDialog::settingsChanged, this, &DolphinMainWindow::refreshViews);
710 settingsDialog->setAttribute(Qt::WA_DeleteOnClose);
711 settingsDialog->show();
712 m_settingsDialog = settingsDialog;
713 } else {
714 m_settingsDialog.data()->raise();
715 }
716 }
717
718 void DolphinMainWindow::handleUrl(const QUrl& url)
719 {
720 delete m_lastHandleUrlStatJob;
721 m_lastHandleUrlStatJob = 0;
722
723 if (url.isLocalFile() && QFileInfo(url.toLocalFile()).isDir()) {
724 activeViewContainer()->setUrl(url);
725 } else if (KProtocolManager::supportsListing(url)) {
726 // stat the URL to see if it is a dir or not
727 m_lastHandleUrlStatJob = KIO::stat(url, KIO::HideProgressInfo);
728 if (m_lastHandleUrlStatJob->ui()) {
729 KJobWidgets::setWindow(m_lastHandleUrlStatJob, this);
730 }
731 connect(m_lastHandleUrlStatJob, &KIO::Job::result,
732 this, &DolphinMainWindow::slotHandleUrlStatFinished);
733
734 } else {
735 new KRun(url, this); // Automatically deletes itself after being finished
736 }
737 }
738
739 void DolphinMainWindow::slotHandleUrlStatFinished(KJob* job)
740 {
741 m_lastHandleUrlStatJob = 0;
742 const KIO::UDSEntry entry = static_cast<KIO::StatJob*>(job)->statResult();
743 const QUrl url = static_cast<KIO::StatJob*>(job)->url();
744 if (entry.isDir()) {
745 activeViewContainer()->setUrl(url);
746 } else {
747 new KRun(url, this); // Automatically deletes itself after being finished
748 }
749 }
750
751 void DolphinMainWindow::slotWriteStateChanged(bool isFolderWritable)
752 {
753 newFileMenu()->setEnabled(isFolderWritable);
754 }
755
756 void DolphinMainWindow::openContextMenu(const QPoint& pos,
757 const KFileItem& item,
758 const QUrl& url,
759 const QList<QAction*>& customActions)
760 {
761 QPointer<DolphinContextMenu> contextMenu = new DolphinContextMenu(this, pos, item, url);
762 contextMenu.data()->setCustomActions(customActions);
763 const DolphinContextMenu::Command command = contextMenu.data()->open();
764
765 switch (command) {
766 case DolphinContextMenu::OpenParentFolder:
767 changeUrl(KIO::upUrl(item.url()));
768 break;
769
770 case DolphinContextMenu::OpenParentFolderInNewWindow: {
771
772 KRun::run("dolphin %u", {KIO::upUrl(item.url())}, this);
773 break;
774 }
775
776 case DolphinContextMenu::OpenParentFolderInNewTab:
777 openNewTab(KIO::upUrl(item.url()));
778 break;
779
780 case DolphinContextMenu::None:
781 default:
782 break;
783 }
784
785 // Delete the menu, unless it has been deleted in its own nested event loop already.
786 if (contextMenu) {
787 contextMenu->deleteLater();
788 }
789 }
790
791 void DolphinMainWindow::updateControlMenu()
792 {
793 QMenu* menu = qobject_cast<QMenu*>(sender());
794 Q_ASSERT(menu);
795
796 // All actions get cleared by QMenu::clear(). The sub-menus are deleted
797 // by connecting to the aboutToHide() signal from the parent-menu.
798 menu->clear();
799
800 KActionCollection* ac = actionCollection();
801
802 // Add "Edit" actions
803 bool added = addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Undo)), menu) |
804 addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Find)), menu) |
805 addActionToMenu(ac->action("select_all"), menu) |
806 addActionToMenu(ac->action("invert_selection"), menu);
807
808 if (added) {
809 menu->addSeparator();
810 }
811
812 // Add "View" actions
813 if (!GeneralSettings::showZoomSlider()) {
814 addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ZoomIn)), menu);
815 addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ZoomOut)), menu);
816 menu->addSeparator();
817 }
818
819 added = addActionToMenu(ac->action("view_mode"), menu) |
820 addActionToMenu(ac->action("sort"), menu) |
821 addActionToMenu(ac->action("additional_info"), menu) |
822 addActionToMenu(ac->action("show_preview"), menu) |
823 addActionToMenu(ac->action("show_in_groups"), menu) |
824 addActionToMenu(ac->action("show_hidden_files"), menu);
825
826 if (added) {
827 menu->addSeparator();
828 }
829
830 added = addActionToMenu(ac->action("split_view"), menu) |
831 addActionToMenu(ac->action("reload"), menu) |
832 addActionToMenu(ac->action("view_properties"), menu);
833 if (added) {
834 menu->addSeparator();
835 }
836
837 addActionToMenu(ac->action("panels"), menu);
838 QMenu* locationBarMenu = new QMenu(i18nc("@action:inmenu", "Location Bar"), menu);
839 locationBarMenu->addAction(ac->action("editable_location"));
840 locationBarMenu->addAction(ac->action("replace_location"));
841 menu->addMenu(locationBarMenu);
842
843 menu->addSeparator();
844
845 // Add "Go" menu
846 QMenu* goMenu = new QMenu(i18nc("@action:inmenu", "Go"), menu);
847 connect(menu, &QMenu::aboutToHide, goMenu, &QMenu::deleteLater);
848 goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Back)));
849 goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Forward)));
850 goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Up)));
851 goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Home)));
852 goMenu->addAction(ac->action("closed_tabs"));
853 menu->addMenu(goMenu);
854
855 // Add "Tool" menu
856 QMenu* toolsMenu = new QMenu(i18nc("@action:inmenu", "Tools"), menu);
857 connect(menu, &QMenu::aboutToHide, toolsMenu, &QMenu::deleteLater);
858 toolsMenu->addAction(ac->action("show_filter_bar"));
859 toolsMenu->addAction(ac->action("compare_files"));
860 toolsMenu->addAction(ac->action("open_terminal"));
861 toolsMenu->addAction(ac->action("change_remote_encoding"));
862 menu->addMenu(toolsMenu);
863
864 // Add "Settings" menu entries
865 addActionToMenu(ac->action(KStandardAction::name(KStandardAction::KeyBindings)), menu);
866 addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ConfigureToolbars)), menu);
867 addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Preferences)), menu);
868
869 // Add "Help" menu
870 QMenu* helpMenu = new QMenu(i18nc("@action:inmenu", "Help"), menu);
871 connect(menu, &QMenu::aboutToHide, helpMenu, &QMenu::deleteLater);
872 helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::HelpContents)));
873 helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::WhatsThis)));
874 helpMenu->addSeparator();
875 helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::ReportBug)));
876 helpMenu->addSeparator();
877 helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::SwitchApplicationLanguage)));
878 helpMenu->addSeparator();
879 helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::AboutApp)));
880 helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::AboutKDE)));
881 menu->addMenu(helpMenu);
882
883 menu->addSeparator();
884 addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ShowMenubar)), menu);
885 }
886
887 void DolphinMainWindow::updateToolBar()
888 {
889 if (!menuBar()->isVisible()) {
890 createControlButton();
891 }
892 }
893
894 void DolphinMainWindow::slotControlButtonDeleted()
895 {
896 m_controlButton = 0;
897 m_updateToolBarTimer->start();
898 }
899
900 void DolphinMainWindow::slotPlaceActivated(const QUrl& url)
901 {
902 DolphinViewContainer* view = activeViewContainer();
903
904 if (view->url() == url) {
905 // We can end up here if the user clicked a device in the Places Panel
906 // which had been unmounted earlier, see https://bugs.kde.org/show_bug.cgi?id=161385.
907 reloadView();
908 } else {
909 changeUrl(url);
910 }
911 }
912
913 void DolphinMainWindow::closedTabsCountChanged(unsigned int count)
914 {
915 actionCollection()->action("undo_close_tab")->setEnabled(count > 0);
916 }
917
918 void DolphinMainWindow::activeViewChanged(DolphinViewContainer* viewContainer)
919 {
920 DolphinViewContainer* oldViewContainer = m_activeViewContainer;
921 Q_ASSERT(viewContainer);
922
923 m_activeViewContainer = viewContainer;
924
925 if (oldViewContainer) {
926 // Disconnect all signals between the old view container (container,
927 // view and url navigator) and main window.
928 oldViewContainer->disconnect(this);
929 oldViewContainer->view()->disconnect(this);
930 oldViewContainer->urlNavigator()->disconnect(this);
931 }
932
933 connectViewSignals(viewContainer);
934
935 m_actionHandler->setCurrentView(viewContainer->view());
936
937 updateHistory();
938 updateEditActions();
939 updatePasteAction();
940 updateViewActions();
941 updateGoActions();
942
943 const QUrl url = viewContainer->url();
944 emit urlChanged(url);
945 }
946
947 void DolphinMainWindow::tabCountChanged(int count)
948 {
949 const bool enableTabActions = (count > 1);
950 actionCollection()->action("close_tab")->setEnabled(enableTabActions);
951 actionCollection()->action("activate_next_tab")->setEnabled(enableTabActions);
952 actionCollection()->action("activate_prev_tab")->setEnabled(enableTabActions);
953 }
954
955 void DolphinMainWindow::setUrlAsCaption(const QUrl& url)
956 {
957 QString caption;
958 if (!url.isLocalFile()) {
959 caption.append(url.scheme() + " - ");
960 if (!url.host().isEmpty()) {
961 caption.append(url.host() + " - ");
962 }
963 }
964
965 QString fileName = url.adjusted(QUrl::StripTrailingSlash).fileName();
966 if (fileName.isEmpty()) {
967 fileName = '/';
968 }
969
970 caption.append(fileName);
971
972 setWindowTitle(caption);
973 }
974
975 void DolphinMainWindow::setupActions()
976 {
977 // setup 'File' menu
978 m_newFileMenu = new DolphinNewFileMenu(actionCollection(), this);
979 QMenu* menu = m_newFileMenu->menu();
980 menu->setTitle(i18nc("@title:menu Create new folder, file, link, etc.", "Create New"));
981 menu->setIcon(QIcon::fromTheme("document-new"));
982 m_newFileMenu->setDelayed(false);
983 connect(menu, &QMenu::aboutToShow,
984 this, &DolphinMainWindow::updateNewMenu);
985
986 QAction* newWindow = actionCollection()->addAction("new_window");
987 newWindow->setIcon(QIcon::fromTheme("window-new"));
988 newWindow->setText(i18nc("@action:inmenu File", "New &Window"));
989 actionCollection()->setDefaultShortcut(newWindow, Qt::CTRL | Qt::Key_N);
990 connect(newWindow, &QAction::triggered, this, &DolphinMainWindow::openNewMainWindow);
991
992 QAction* newTab = actionCollection()->addAction("new_tab");
993 newTab->setIcon(QIcon::fromTheme("tab-new"));
994 newTab->setText(i18nc("@action:inmenu File", "New Tab"));
995 actionCollection()->setDefaultShortcuts(newTab, {Qt::CTRL | Qt::Key_T, Qt::CTRL | Qt::SHIFT | Qt::Key_N});
996 connect(newTab, &QAction::triggered, this, static_cast<void(DolphinMainWindow::*)()>(&DolphinMainWindow::openNewActivatedTab));
997
998 QAction* closeTab = actionCollection()->addAction("close_tab");
999 closeTab->setIcon(QIcon::fromTheme("tab-close"));
1000 closeTab->setText(i18nc("@action:inmenu File", "Close Tab"));
1001 actionCollection()->setDefaultShortcut(closeTab, Qt::CTRL | Qt::Key_W);
1002 closeTab->setEnabled(false);
1003 connect(closeTab, &QAction::triggered, m_tabWidget, static_cast<void(DolphinTabWidget::*)()>(&DolphinTabWidget::closeTab));
1004
1005 KStandardAction::quit(this, SLOT(quit()), actionCollection());
1006
1007 // setup 'Edit' menu
1008 KStandardAction::undo(this,
1009 SLOT(undo()),
1010 actionCollection());
1011
1012 // need to remove shift+del from cut action, else the shortcut for deletejob
1013 // doesn't work
1014 QAction* cut = KStandardAction::cut(this, SLOT(cut()), actionCollection());
1015 auto cutShortcuts = cut->shortcuts();
1016 cutShortcuts.removeAll(QKeySequence(Qt::SHIFT | Qt::Key_Delete));
1017 actionCollection()->setDefaultShortcuts(cut, cutShortcuts);
1018 KStandardAction::copy(this, SLOT(copy()), actionCollection());
1019 QAction* paste = KStandardAction::paste(this, SLOT(paste()), actionCollection());
1020 // The text of the paste-action is modified dynamically by Dolphin
1021 // (e. g. to "Paste One Folder"). To prevent that the size of the toolbar changes
1022 // due to the long text, the text "Paste" is used:
1023 paste->setIconText(i18nc("@action:inmenu Edit", "Paste"));
1024
1025 KStandardAction::find(this, SLOT(find()), actionCollection());
1026
1027 QAction* selectAll = actionCollection()->addAction("select_all");
1028 selectAll->setText(i18nc("@action:inmenu Edit", "Select All"));
1029 actionCollection()->setDefaultShortcut(selectAll, Qt::CTRL | Qt::Key_A);
1030 connect(selectAll, &QAction::triggered, this, &DolphinMainWindow::selectAll);
1031
1032 QAction* invertSelection = actionCollection()->addAction("invert_selection");
1033 invertSelection->setText(i18nc("@action:inmenu Edit", "Invert Selection"));
1034 actionCollection()->setDefaultShortcut(invertSelection, Qt::CTRL | Qt::SHIFT | Qt::Key_A);
1035 connect(invertSelection, &QAction::triggered, this, &DolphinMainWindow::invertSelection);
1036
1037 // setup 'View' menu
1038 // (note that most of it is set up in DolphinViewActionHandler)
1039
1040 QAction* split = actionCollection()->addAction("split_view");
1041 actionCollection()->setDefaultShortcut(split, Qt::Key_F3);
1042 connect(split, &QAction::triggered, this, &DolphinMainWindow::toggleSplitView);
1043
1044 QAction* reload = actionCollection()->addAction("reload");
1045 reload->setText(i18nc("@action:inmenu View", "Reload"));
1046 actionCollection()->setDefaultShortcut(reload, Qt::Key_F5);
1047 reload->setIcon(QIcon::fromTheme("view-refresh"));
1048 connect(reload, &QAction::triggered, this, &DolphinMainWindow::reloadView);
1049
1050 QAction* stop = actionCollection()->addAction("stop");
1051 stop->setText(i18nc("@action:inmenu View", "Stop"));
1052 stop->setToolTip(i18nc("@info", "Stop loading"));
1053 stop->setIcon(QIcon::fromTheme("process-stop"));
1054 connect(stop, &QAction::triggered, this, &DolphinMainWindow::stopLoading);
1055
1056 KToggleAction* editableLocation = actionCollection()->add<KToggleAction>("editable_location");
1057 editableLocation->setText(i18nc("@action:inmenu Navigation Bar", "Editable Location"));
1058 actionCollection()->setDefaultShortcut(editableLocation, Qt::Key_F6);
1059 connect(editableLocation, &KToggleAction::triggered, this, &DolphinMainWindow::toggleEditLocation);
1060
1061 QAction* replaceLocation = actionCollection()->addAction("replace_location");
1062 replaceLocation->setText(i18nc("@action:inmenu Navigation Bar", "Replace Location"));
1063 actionCollection()->setDefaultShortcut(replaceLocation, Qt::CTRL | Qt::Key_L);
1064 connect(replaceLocation, &QAction::triggered, this, &DolphinMainWindow::replaceLocation);
1065
1066 // setup 'Go' menu
1067 QAction* backAction = KStandardAction::back(this, SLOT(goBack()), actionCollection());
1068 auto backShortcuts = backAction->shortcuts();
1069 backShortcuts.append(QKeySequence(Qt::Key_Backspace));
1070 actionCollection()->setDefaultShortcuts(backAction, backShortcuts);
1071
1072 DolphinRecentTabsMenu* recentTabsMenu = new DolphinRecentTabsMenu(this);
1073 actionCollection()->addAction("closed_tabs", recentTabsMenu);
1074 connect(m_tabWidget, SIGNAL(rememberClosedTab(QUrl,QByteArray)),
1075 recentTabsMenu, SLOT(rememberClosedTab(QUrl,QByteArray)));
1076 connect(recentTabsMenu, SIGNAL(restoreClosedTab(QByteArray)),
1077 m_tabWidget, SLOT(restoreClosedTab(QByteArray)));
1078 connect(recentTabsMenu, SIGNAL(closedTabsCountChanged(uint)),
1079 this, SLOT(closedTabsCountChanged(uint)));
1080
1081 QAction* undoCloseTab = actionCollection()->addAction("undo_close_tab");
1082 undoCloseTab->setText(i18nc("@action:inmenu File", "Undo close tab"));
1083 actionCollection()->setDefaultShortcut(undoCloseTab, Qt::CTRL | Qt::SHIFT | Qt::Key_T);
1084 undoCloseTab->setIcon(QIcon::fromTheme("edit-undo"));
1085 undoCloseTab->setEnabled(false);
1086 connect(undoCloseTab, SIGNAL(triggered()), recentTabsMenu, SLOT(undoCloseTab()));
1087
1088 auto undoAction = actionCollection()->action(KStandardAction::name(KStandardAction::Undo));
1089 undoAction->setEnabled(false); // undo should be disabled by default
1090
1091 KStandardAction::forward(this, SLOT(goForward()), actionCollection());
1092 KStandardAction::up(this, SLOT(goUp()), actionCollection());
1093 KStandardAction::home(this, SLOT(goHome()), actionCollection());
1094
1095 // setup 'Tools' menu
1096 QAction* showFilterBar = actionCollection()->addAction("show_filter_bar");
1097 showFilterBar->setText(i18nc("@action:inmenu Tools", "Show Filter Bar"));
1098 showFilterBar->setIcon(QIcon::fromTheme("view-filter"));
1099 actionCollection()->setDefaultShortcut(showFilterBar, Qt::CTRL | Qt::Key_I);
1100 connect(showFilterBar, &QAction::triggered, this, &DolphinMainWindow::showFilterBar);
1101
1102 QAction* compareFiles = actionCollection()->addAction("compare_files");
1103 compareFiles->setText(i18nc("@action:inmenu Tools", "Compare Files"));
1104 compareFiles->setIcon(QIcon::fromTheme("kompare"));
1105 compareFiles->setEnabled(false);
1106 connect(compareFiles, &QAction::triggered, this, &DolphinMainWindow::compareFiles);
1107
1108 QAction* openTerminal = actionCollection()->addAction("open_terminal");
1109 openTerminal->setText(i18nc("@action:inmenu Tools", "Open Terminal"));
1110 openTerminal->setIcon(QIcon::fromTheme("utilities-terminal"));
1111 actionCollection()->setDefaultShortcut(openTerminal, Qt::SHIFT | Qt::Key_F4);
1112 connect(openTerminal, &QAction::triggered, this, &DolphinMainWindow::openTerminal);
1113
1114 // setup 'Settings' menu
1115 KToggleAction* showMenuBar = KStandardAction::showMenubar(0, 0, actionCollection());
1116 connect(showMenuBar, &KToggleAction::triggered, // Fixes #286822
1117 this, &DolphinMainWindow::toggleShowMenuBar, Qt::QueuedConnection);
1118 KStandardAction::preferences(this, SLOT(editSettings()), actionCollection());
1119
1120 // not in menu actions
1121 QList<QKeySequence> nextTabKeys = KStandardShortcut::tabNext();
1122 nextTabKeys.append(QKeySequence(Qt::CTRL | Qt::Key_Tab));
1123
1124 QList<QKeySequence> prevTabKeys = KStandardShortcut::tabPrev();
1125 prevTabKeys.append(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_Tab));
1126
1127 QAction* activateNextTab = actionCollection()->addAction("activate_next_tab");
1128 activateNextTab->setIconText(i18nc("@action:inmenu", "Next Tab"));
1129 activateNextTab->setText(i18nc("@action:inmenu", "Activate Next Tab"));
1130 activateNextTab->setEnabled(false);
1131 connect(activateNextTab, &QAction::triggered, m_tabWidget, &DolphinTabWidget::activateNextTab);
1132 actionCollection()->setDefaultShortcuts(activateNextTab, QApplication::isRightToLeft() ? prevTabKeys : nextTabKeys);
1133
1134 QAction* activatePrevTab = actionCollection()->addAction("activate_prev_tab");
1135 activatePrevTab->setIconText(i18nc("@action:inmenu", "Previous Tab"));
1136 activatePrevTab->setText(i18nc("@action:inmenu", "Activate Previous Tab"));
1137 activatePrevTab->setEnabled(false);
1138 connect(activatePrevTab, &QAction::triggered, m_tabWidget, &DolphinTabWidget::activatePrevTab);
1139 actionCollection()->setDefaultShortcuts(activatePrevTab, QApplication::isRightToLeft() ? nextTabKeys : prevTabKeys);
1140
1141 // for context menu
1142 QAction* openInNewTab = actionCollection()->addAction("open_in_new_tab");
1143 openInNewTab->setText(i18nc("@action:inmenu", "Open in New Tab"));
1144 openInNewTab->setIcon(QIcon::fromTheme("tab-new"));
1145 connect(openInNewTab, &QAction::triggered, this, &DolphinMainWindow::openInNewTab);
1146
1147 QAction* openInNewTabs = actionCollection()->addAction("open_in_new_tabs");
1148 openInNewTabs->setText(i18nc("@action:inmenu", "Open in New Tabs"));
1149 openInNewTabs->setIcon(QIcon::fromTheme("tab-new"));
1150 connect(openInNewTabs, &QAction::triggered, this, &DolphinMainWindow::openInNewTab);
1151
1152 QAction* openInNewWindow = actionCollection()->addAction("open_in_new_window");
1153 openInNewWindow->setText(i18nc("@action:inmenu", "Open in New Window"));
1154 openInNewWindow->setIcon(QIcon::fromTheme("window-new"));
1155 connect(openInNewWindow, &QAction::triggered, this, &DolphinMainWindow::openInNewWindow);
1156 }
1157
1158 void DolphinMainWindow::setupDockWidgets()
1159 {
1160 const bool lock = GeneralSettings::lockPanels();
1161
1162 KDualAction* lockLayoutAction = actionCollection()->add<KDualAction>("lock_panels");
1163 lockLayoutAction->setActiveText(i18nc("@action:inmenu Panels", "Unlock Panels"));
1164 lockLayoutAction->setActiveIcon(QIcon::fromTheme("object-unlocked"));
1165 lockLayoutAction->setInactiveText(i18nc("@action:inmenu Panels", "Lock Panels"));
1166 lockLayoutAction->setInactiveIcon(QIcon::fromTheme("object-locked"));
1167 lockLayoutAction->setActive(lock);
1168 connect(lockLayoutAction, &KDualAction::triggered, this, &DolphinMainWindow::togglePanelLockState);
1169
1170 // Setup "Information"
1171 DolphinDockWidget* infoDock = new DolphinDockWidget(i18nc("@title:window", "Information"));
1172 infoDock->setLocked(lock);
1173 infoDock->setObjectName("infoDock");
1174 infoDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
1175 InformationPanel* infoPanel = new InformationPanel(infoDock);
1176 infoPanel->setCustomContextMenuActions({lockLayoutAction});
1177 connect(infoPanel, &InformationPanel::urlActivated, this, &DolphinMainWindow::handleUrl);
1178 infoDock->setWidget(infoPanel);
1179
1180 QAction* infoAction = infoDock->toggleViewAction();
1181 createPanelAction(QIcon::fromTheme("dialog-information"), Qt::Key_F11, infoAction, "show_information_panel");
1182
1183 addDockWidget(Qt::RightDockWidgetArea, infoDock);
1184 connect(this, &DolphinMainWindow::urlChanged,
1185 infoPanel, &InformationPanel::setUrl);
1186 connect(this, &DolphinMainWindow::selectionChanged,
1187 infoPanel, &InformationPanel::setSelection);
1188 connect(this, &DolphinMainWindow::requestItemInfo,
1189 infoPanel, &InformationPanel::requestDelayedItemInfo);
1190
1191 // Setup "Folders"
1192 DolphinDockWidget* foldersDock = new DolphinDockWidget(i18nc("@title:window", "Folders"));
1193 foldersDock->setLocked(lock);
1194 foldersDock->setObjectName("foldersDock");
1195 foldersDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
1196 FoldersPanel* foldersPanel = new FoldersPanel(foldersDock);
1197 foldersPanel->setCustomContextMenuActions({lockLayoutAction});
1198 foldersDock->setWidget(foldersPanel);
1199
1200 QAction* foldersAction = foldersDock->toggleViewAction();
1201 createPanelAction(QIcon::fromTheme("folder"), Qt::Key_F7, foldersAction, "show_folders_panel");
1202
1203 addDockWidget(Qt::LeftDockWidgetArea, foldersDock);
1204 connect(this, &DolphinMainWindow::urlChanged,
1205 foldersPanel, &FoldersPanel::setUrl);
1206 connect(foldersPanel, &FoldersPanel::folderActivated,
1207 this, &DolphinMainWindow::changeUrl);
1208 connect(foldersPanel, &FoldersPanel::folderMiddleClicked,
1209 this, &DolphinMainWindow::openNewTab);
1210 connect(foldersPanel, &FoldersPanel::errorMessage,
1211 this, &DolphinMainWindow::showErrorMessage);
1212
1213 // Setup "Terminal"
1214 #ifndef Q_OS_WIN
1215 DolphinDockWidget* terminalDock = new DolphinDockWidget(i18nc("@title:window Shell terminal", "Terminal"));
1216 terminalDock->setLocked(lock);
1217 terminalDock->setObjectName("terminalDock");
1218 terminalDock->setAllowedAreas(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea);
1219 TerminalPanel* terminalPanel = new TerminalPanel(terminalDock);
1220 terminalPanel->setCustomContextMenuActions({lockLayoutAction});
1221 terminalDock->setWidget(terminalPanel);
1222
1223 connect(terminalPanel, &TerminalPanel::hideTerminalPanel, terminalDock, &DolphinDockWidget::hide);
1224 connect(terminalPanel, &TerminalPanel::changeUrl, this, &DolphinMainWindow::slotTerminalDirectoryChanged);
1225 connect(terminalDock, &DolphinDockWidget::visibilityChanged,
1226 terminalPanel, &TerminalPanel::dockVisibilityChanged);
1227
1228 QAction* terminalAction = terminalDock->toggleViewAction();
1229 createPanelAction(QIcon::fromTheme("utilities-terminal"), Qt::Key_F4, terminalAction, "show_terminal_panel");
1230
1231 addDockWidget(Qt::BottomDockWidgetArea, terminalDock);
1232 connect(this, &DolphinMainWindow::urlChanged,
1233 terminalPanel, &TerminalPanel::setUrl);
1234 #endif
1235
1236 if (GeneralSettings::version() < 200) {
1237 infoDock->hide();
1238 foldersDock->hide();
1239 #ifndef Q_OS_WIN
1240 terminalDock->hide();
1241 #endif
1242 }
1243
1244 // Setup "Places"
1245 DolphinDockWidget* placesDock = new DolphinDockWidget(i18nc("@title:window", "Places"));
1246 placesDock->setLocked(lock);
1247 placesDock->setObjectName("placesDock");
1248 placesDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
1249
1250 PlacesPanel* placesPanel = new PlacesPanel(placesDock);
1251 placesPanel->setCustomContextMenuActions({lockLayoutAction});
1252 placesDock->setWidget(placesPanel);
1253
1254 QAction* placesAction = placesDock->toggleViewAction();
1255 createPanelAction(QIcon::fromTheme("bookmarks"), Qt::Key_F9, placesAction, "show_places_panel");
1256
1257 addDockWidget(Qt::LeftDockWidgetArea, placesDock);
1258 connect(placesPanel, &PlacesPanel::placeActivated,
1259 this, &DolphinMainWindow::slotPlaceActivated);
1260 connect(placesPanel, SIGNAL(placeMiddleClicked(QUrl)),
1261 this, SLOT(openNewTab(QUrl)));
1262 connect(placesPanel, &PlacesPanel::errorMessage,
1263 this, &DolphinMainWindow::showErrorMessage);
1264 connect(this, &DolphinMainWindow::urlChanged,
1265 placesPanel, &PlacesPanel::setUrl);
1266 connect(placesDock, &DolphinDockWidget::visibilityChanged,
1267 m_tabWidget, &DolphinTabWidget::slotPlacesPanelVisibilityChanged);
1268 connect(this, &DolphinMainWindow::settingsChanged,
1269 placesPanel, &PlacesPanel::readSettings);
1270
1271 m_tabWidget->slotPlacesPanelVisibilityChanged(placesPanel->isVisible());
1272
1273 // Add actions into the "Panels" menu
1274 KActionMenu* panelsMenu = new KActionMenu(i18nc("@action:inmenu View", "Panels"), this);
1275 actionCollection()->addAction("panels", panelsMenu);
1276 panelsMenu->setDelayed(false);
1277 const KActionCollection* ac = actionCollection();
1278 panelsMenu->addAction(ac->action("show_places_panel"));
1279 panelsMenu->addAction(ac->action("show_information_panel"));
1280 panelsMenu->addAction(ac->action("show_folders_panel"));
1281 #ifndef Q_OS_WIN
1282 panelsMenu->addAction(ac->action("show_terminal_panel"));
1283 #endif
1284 panelsMenu->addSeparator();
1285 panelsMenu->addAction(lockLayoutAction);
1286 }
1287
1288 void DolphinMainWindow::updateEditActions()
1289 {
1290 const KFileItemList list = m_activeViewContainer->view()->selectedItems();
1291 if (list.isEmpty()) {
1292 stateChanged("has_no_selection");
1293 } else {
1294 stateChanged("has_selection");
1295
1296 KActionCollection* col = actionCollection();
1297 QAction* renameAction = col->action("rename");
1298 QAction* moveToTrashAction = col->action("move_to_trash");
1299 QAction* deleteAction = col->action("delete");
1300 QAction* cutAction = col->action(KStandardAction::name(KStandardAction::Cut));
1301 QAction* deleteWithTrashShortcut = col->action("delete_shortcut"); // see DolphinViewActionHandler
1302
1303 KFileItemListProperties capabilities(list);
1304 const bool enableMoveToTrash = capabilities.isLocal() && capabilities.supportsMoving();
1305
1306 renameAction->setEnabled(capabilities.supportsMoving());
1307 moveToTrashAction->setEnabled(enableMoveToTrash);
1308 deleteAction->setEnabled(capabilities.supportsDeleting());
1309 deleteWithTrashShortcut->setEnabled(capabilities.supportsDeleting() && !enableMoveToTrash);
1310 cutAction->setEnabled(capabilities.supportsMoving());
1311 }
1312 }
1313
1314 void DolphinMainWindow::updateViewActions()
1315 {
1316 m_actionHandler->updateViewActions();
1317
1318 QAction* showFilterBarAction = actionCollection()->action("show_filter_bar");
1319 showFilterBarAction->setChecked(m_activeViewContainer->isFilterBarVisible());
1320
1321 updateSplitAction();
1322
1323 QAction* editableLocactionAction = actionCollection()->action("editable_location");
1324 const KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator();
1325 editableLocactionAction->setChecked(urlNavigator->isUrlEditable());
1326 }
1327
1328 void DolphinMainWindow::updateGoActions()
1329 {
1330 QAction* goUpAction = actionCollection()->action(KStandardAction::name(KStandardAction::Up));
1331 const QUrl currentUrl = m_activeViewContainer->url();
1332 goUpAction->setEnabled(KIO::upUrl(currentUrl) != currentUrl);
1333 }
1334
1335 void DolphinMainWindow::createControlButton()
1336 {
1337 if (m_controlButton) {
1338 return;
1339 }
1340 Q_ASSERT(!m_controlButton);
1341
1342 m_controlButton = new QToolButton(this);
1343 m_controlButton->setIcon(QIcon::fromTheme("applications-system"));
1344 m_controlButton->setText(i18nc("@action", "Control"));
1345 m_controlButton->setPopupMode(QToolButton::InstantPopup);
1346 m_controlButton->setToolButtonStyle(toolBar()->toolButtonStyle());
1347
1348 QMenu* controlMenu = new QMenu(m_controlButton);
1349 connect(controlMenu, &QMenu::aboutToShow, this, &DolphinMainWindow::updateControlMenu);
1350
1351 m_controlButton->setMenu(controlMenu);
1352
1353 toolBar()->addWidget(m_controlButton);
1354 connect(toolBar(), &KToolBar::iconSizeChanged,
1355 m_controlButton, &QToolButton::setIconSize);
1356 connect(toolBar(), &KToolBar::toolButtonStyleChanged,
1357 m_controlButton, &QToolButton::setToolButtonStyle);
1358
1359 // The added widgets are owned by the toolbar and may get deleted when e.g. the toolbar
1360 // gets edited. In this case we must add them again. The adding is done asynchronously by
1361 // m_updateToolBarTimer.
1362 connect(m_controlButton, &QToolButton::destroyed, this, &DolphinMainWindow::slotControlButtonDeleted);
1363 m_updateToolBarTimer = new QTimer(this);
1364 m_updateToolBarTimer->setInterval(500);
1365 connect(m_updateToolBarTimer, &QTimer::timeout, this, &DolphinMainWindow::updateToolBar);
1366 }
1367
1368 void DolphinMainWindow::deleteControlButton()
1369 {
1370 delete m_controlButton;
1371 m_controlButton = 0;
1372
1373 delete m_updateToolBarTimer;
1374 m_updateToolBarTimer = 0;
1375 }
1376
1377 bool DolphinMainWindow::addActionToMenu(QAction* action, QMenu* menu)
1378 {
1379 Q_ASSERT(action);
1380 Q_ASSERT(menu);
1381
1382 const KToolBar* toolBarWidget = toolBar();
1383 foreach (const QWidget* widget, action->associatedWidgets()) {
1384 if (widget == toolBarWidget) {
1385 return false;
1386 }
1387 }
1388
1389 menu->addAction(action);
1390 return true;
1391 }
1392
1393 void DolphinMainWindow::refreshViews()
1394 {
1395 m_tabWidget->refreshViews();
1396
1397 if (GeneralSettings::modifiedStartupSettings()) {
1398 // The startup settings have been changed by the user (see bug #254947).
1399 // Synchronize the split-view setting with the active view:
1400 const bool splitView = GeneralSettings::splitView();
1401 m_tabWidget->currentTabPage()->setSplitViewEnabled(splitView);
1402 updateSplitAction();
1403 }
1404
1405 emit settingsChanged();
1406 }
1407
1408 void DolphinMainWindow::clearStatusBar()
1409 {
1410 m_activeViewContainer->statusBar()->resetToDefaultText();
1411 }
1412
1413 void DolphinMainWindow::connectViewSignals(DolphinViewContainer* container)
1414 {
1415 connect(container, &DolphinViewContainer::showFilterBarChanged,
1416 this, &DolphinMainWindow::updateFilterBarAction);
1417 connect(container, &DolphinViewContainer::writeStateChanged,
1418 this, &DolphinMainWindow::slotWriteStateChanged);
1419
1420 const DolphinView* view = container->view();
1421 connect(view, &DolphinView::selectionChanged,
1422 this, &DolphinMainWindow::slotSelectionChanged);
1423 connect(view, &DolphinView::requestItemInfo,
1424 this, &DolphinMainWindow::requestItemInfo);
1425 connect(view, &DolphinView::tabRequested,
1426 this, &DolphinMainWindow::openNewTab);
1427 connect(view, &DolphinView::requestContextMenu,
1428 this, &DolphinMainWindow::openContextMenu);
1429 connect(view, &DolphinView::directoryLoadingStarted,
1430 this, &DolphinMainWindow::enableStopAction);
1431 connect(view, &DolphinView::directoryLoadingCompleted,
1432 this, &DolphinMainWindow::disableStopAction);
1433 connect(view, &DolphinView::directoryLoadingCompleted,
1434 this, &DolphinMainWindow::slotDirectoryLoadingCompleted);
1435 connect(view, &DolphinView::goBackRequested,
1436 this, static_cast<void(DolphinMainWindow::*)()>(&DolphinMainWindow::goBack));
1437 connect(view, &DolphinView::goForwardRequested,
1438 this, static_cast<void(DolphinMainWindow::*)()>(&DolphinMainWindow::goForward));
1439
1440 const KUrlNavigator* navigator = container->urlNavigator();
1441 connect(navigator, &KUrlNavigator::urlChanged,
1442 this, &DolphinMainWindow::changeUrl);
1443 connect(navigator, &KUrlNavigator::historyChanged,
1444 this, &DolphinMainWindow::updateHistory);
1445 connect(navigator, &KUrlNavigator::editableStateChanged,
1446 this, &DolphinMainWindow::slotEditableStateChanged);
1447 connect(navigator, &KUrlNavigator::tabRequested,
1448 this, &DolphinMainWindow::openNewTab);
1449 }
1450
1451 void DolphinMainWindow::updateSplitAction()
1452 {
1453 QAction* splitAction = actionCollection()->action("split_view");
1454 const DolphinTabPage* tabPage = m_tabWidget->currentTabPage();
1455 if (tabPage->splitViewEnabled()) {
1456 if (tabPage->primaryViewActive()) {
1457 splitAction->setText(i18nc("@action:intoolbar Close left view", "Close"));
1458 splitAction->setToolTip(i18nc("@info", "Close left view"));
1459 splitAction->setIcon(QIcon::fromTheme("view-left-close"));
1460 } else {
1461 splitAction->setText(i18nc("@action:intoolbar Close right view", "Close"));
1462 splitAction->setToolTip(i18nc("@info", "Close right view"));
1463 splitAction->setIcon(QIcon::fromTheme("view-right-close"));
1464 }
1465 } else {
1466 splitAction->setText(i18nc("@action:intoolbar Split view", "Split"));
1467 splitAction->setToolTip(i18nc("@info", "Split view"));
1468 splitAction->setIcon(QIcon::fromTheme("view-right-new"));
1469 }
1470 }
1471
1472 bool DolphinMainWindow::isKompareInstalled() const
1473 {
1474 static bool initialized = false;
1475 static bool installed = false;
1476 if (!initialized) {
1477 // TODO: maybe replace this approach later by using a menu
1478 // plugin like kdiff3plugin.cpp
1479 installed = !QStandardPaths::findExecutable(QStringLiteral("kompare")).isEmpty();
1480 initialized = true;
1481 }
1482 return installed;
1483 }
1484
1485 void DolphinMainWindow::createPanelAction(const QIcon& icon,
1486 const QKeySequence& shortcut,
1487 QAction* dockAction,
1488 const QString& actionName)
1489 {
1490 QAction* panelAction = actionCollection()->addAction(actionName);
1491 panelAction->setCheckable(true);
1492 panelAction->setChecked(dockAction->isChecked());
1493 panelAction->setText(dockAction->text());
1494 panelAction->setIcon(icon);
1495 actionCollection()->setDefaultShortcut(panelAction, shortcut);
1496
1497 connect(panelAction, &QAction::triggered, dockAction, &QAction::trigger);
1498 connect(dockAction, &QAction::toggled, panelAction, &QAction::setChecked);
1499 }
1500
1501 DolphinMainWindow::UndoUiInterface::UndoUiInterface() :
1502 KIO::FileUndoManager::UiInterface()
1503 {
1504 }
1505
1506 DolphinMainWindow::UndoUiInterface::~UndoUiInterface()
1507 {
1508 }
1509
1510 void DolphinMainWindow::UndoUiInterface::jobError(KIO::Job* job)
1511 {
1512 DolphinMainWindow* mainWin= qobject_cast<DolphinMainWindow *>(parentWidget());
1513 if (mainWin) {
1514 DolphinViewContainer* container = mainWin->activeViewContainer();
1515 container->showMessage(job->errorString(), DolphinViewContainer::Error);
1516 } else {
1517 KIO::FileUndoManager::UiInterface::jobError(job);
1518 }
1519 }
1520