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