]> cloud.milkyroute.net Git - dolphin.git/blob - src/dolphinmainwindow.cpp
KIcon -> QIcon and KMimeType::iconNameForUrl() -> KIO::iconNameForUrl()
[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 "dolphinapplication.h"
25 #include "dolphindockwidget.h"
26 #include "dolphincontextmenu.h"
27 #include "dolphinnewfilemenu.h"
28 #include "dolphinviewcontainer.h"
29 #include "panels/folders/folderspanel.h"
30 #include "panels/places/placespanel.h"
31 #include "panels/information/informationpanel.h"
32 #include "settings/dolphinsettingsdialog.h"
33 #include "statusbar/dolphinstatusbar.h"
34 #include "views/dolphinviewactionhandler.h"
35 #include "views/dolphinremoteencoding.h"
36 #include "views/draganddrophelper.h"
37 #include "views/viewproperties.h"
38 #include "views/dolphinnewfilemenuobserver.h"
39
40 #ifndef Q_OS_WIN
41 #include "panels/terminal/terminalpanel.h"
42 #endif
43
44 #include "dolphin_generalsettings.h"
45
46 #include <KAcceleratorManager>
47 #include <KAction>
48 #include <KActionCollection>
49 #include <KActionMenu>
50 #include <KConfig>
51 #include <KDesktopFile>
52 #include <kdeversion.h>
53 #include <kdualaction.h>
54 #include <KFileDialog>
55 #include <KGlobal>
56 #include <KDialog>
57 #include <KJobWidgets>
58 #include <KLineEdit>
59 #include <KToolBar>
60 #include <KIconLoader>
61 #include <KIO/NetAccess>
62 #include <KIO/JobUiDelegate>
63 #include <KInputDialog>
64 #include <KLocale>
65 #include <KProtocolManager>
66 #include <KMenu>
67 #include <KMenuBar>
68 #include <KMessageBox>
69 #include <KFileItemListProperties>
70 #include <konqmimedata.h>
71 #include <KProtocolInfo>
72 #include <KRun>
73 #include <KShell>
74 #include <KStandardDirs>
75 #include <kstatusbar.h>
76 #include <KStandardAction>
77 #include <ktabbar.h>
78 #include <KToggleAction>
79 #include <KUrlNavigator>
80 #include <KUrl>
81 #include <KUrlComboBox>
82 #include <KToolInvocation>
83
84 #include <QDesktopWidget>
85 #include <QDBusMessage>
86 #include <QKeyEvent>
87 #include <QClipboard>
88 #include <QToolButton>
89 #include <QSplitter>
90 #include <QTimer>
91 #include <QPushButton>
92
93 namespace {
94 // Used for GeneralSettings::version() to determine whether
95 // an updated version of Dolphin is running.
96 const int CurrentDolphinVersion = 200;
97 };
98
99 /*
100 * Remembers the tab configuration if a tab has been closed.
101 * Each closed tab can be restored by the menu
102 * "Go -> Recently Closed Tabs".
103 */
104 struct ClosedTab
105 {
106 KUrl primaryUrl;
107 KUrl secondaryUrl;
108 bool isSplit;
109 };
110 Q_DECLARE_METATYPE(ClosedTab)
111
112 DolphinMainWindow::DolphinMainWindow() :
113 KXmlGuiWindow(0),
114 m_newFileMenu(0),
115 m_tabBar(0),
116 m_activeViewContainer(0),
117 m_centralWidgetLayout(0),
118 m_tabIndex(0),
119 m_viewTab(),
120 m_actionHandler(0),
121 m_remoteEncoding(0),
122 m_settingsDialog(),
123 m_controlButton(0),
124 m_updateToolBarTimer(0),
125 m_lastHandleUrlStatJob(0)
126 {
127 setObjectName("Dolphin#");
128
129 m_viewTab.append(ViewTab());
130 ViewTab& viewTab = m_viewTab[m_tabIndex];
131 viewTab.wasActive = true; // The first opened tab is automatically active
132
133 connect(&DolphinNewFileMenuObserver::instance(), &DolphinNewFileMenuObserver::errorMessage,
134 this, &DolphinMainWindow::showErrorMessage);
135
136 KIO::FileUndoManager* undoManager = KIO::FileUndoManager::self();
137 undoManager->setUiInterface(new UndoUiInterface());
138
139 connect(undoManager, static_cast<void(KIO::FileUndoManager::*)(bool)>(&KIO::FileUndoManager::undoAvailable),
140 this, &DolphinMainWindow::slotUndoAvailable);
141 connect(undoManager, &KIO::FileUndoManager::undoTextChanged,
142 this, &DolphinMainWindow::slotUndoTextChanged);
143 connect(undoManager, &KIO::FileUndoManager::jobRecordingStarted,
144 this, &DolphinMainWindow::clearStatusBar);
145 connect(undoManager, &KIO::FileUndoManager::jobRecordingFinished,
146 this, &DolphinMainWindow::showCommand);
147
148 GeneralSettings* generalSettings = GeneralSettings::self();
149 const bool firstRun = (generalSettings->version() < 200);
150 if (firstRun) {
151 generalSettings->setViewPropsTimestamp(QDateTime::currentDateTime());
152 }
153
154 setAcceptDrops(true);
155
156 viewTab.splitter = new QSplitter(this);
157 viewTab.splitter->setChildrenCollapsible(false);
158
159 setupActions();
160
161 const KUrl homeUrl(generalSettings->homeUrl());
162 setUrlAsCaption(homeUrl);
163 m_actionHandler = new DolphinViewActionHandler(actionCollection(), this);
164 connect(m_actionHandler, &DolphinViewActionHandler::actionBeingHandled, this, &DolphinMainWindow::clearStatusBar);
165 connect(m_actionHandler, &DolphinViewActionHandler::createDirectory, this, &DolphinMainWindow::createDirectory);
166
167 viewTab.primaryView = createViewContainer(homeUrl, viewTab.splitter);
168
169 m_activeViewContainer = viewTab.primaryView;
170 connectViewSignals(m_activeViewContainer);
171 DolphinView* view = m_activeViewContainer->view();
172 m_activeViewContainer->show();
173 m_actionHandler->setCurrentView(view);
174
175 m_remoteEncoding = new DolphinRemoteEncoding(this, m_actionHandler);
176 connect(this, &DolphinMainWindow::urlChanged,
177 m_remoteEncoding, &DolphinRemoteEncoding::slotAboutToOpenUrl);
178
179 m_tabBar = new KTabBar(this);
180 m_tabBar->setMovable(true);
181 m_tabBar->setTabsClosable(true);
182 connect(m_tabBar, &KTabBar::currentChanged,
183 this, &DolphinMainWindow::setActiveTab);
184 connect(m_tabBar, &KTabBar::tabCloseRequested,
185 this, static_cast<void(DolphinMainWindow::*)(int)>(&DolphinMainWindow::closeTab));
186 connect(m_tabBar, &KTabBar::contextMenu,
187 this, &DolphinMainWindow::openTabContextMenu);
188 connect(m_tabBar, &KTabBar::newTabRequest,
189 this, static_cast<void(DolphinMainWindow::*)()>(&DolphinMainWindow::openNewTab));
190 connect(m_tabBar, &KTabBar::testCanDecode,
191 this, &DolphinMainWindow::slotTestCanDecode);
192 connect(m_tabBar, &KTabBar::mouseMiddleClick,
193 this, static_cast<void(DolphinMainWindow::*)(int)>(&DolphinMainWindow::closeTab));
194 connect(m_tabBar, &KTabBar::tabMoved,
195 this, &DolphinMainWindow::slotTabMoved);
196 connect(m_tabBar, &KTabBar::receivedDropEvent,
197 this, &DolphinMainWindow::tabDropEvent);
198
199 m_tabBar->blockSignals(true); // signals get unblocked after at least 2 tabs are open
200
201 QWidget* centralWidget = new QWidget(this);
202 m_centralWidgetLayout = new QVBoxLayout(centralWidget);
203 m_centralWidgetLayout->setSpacing(0);
204 m_centralWidgetLayout->setMargin(0);
205 m_centralWidgetLayout->addWidget(m_tabBar);
206 m_centralWidgetLayout->addWidget(viewTab.splitter, 1);
207
208 setCentralWidget(centralWidget);
209 setupDockWidgets();
210 emit urlChanged(homeUrl);
211
212 setupGUI(Keys | Save | Create | ToolBar);
213 stateChanged("new_file");
214
215 QClipboard* clipboard = QApplication::clipboard();
216 connect(clipboard, &QClipboard::dataChanged,
217 this, &DolphinMainWindow::updatePasteAction);
218
219 if (generalSettings->splitView()) {
220 toggleSplitView();
221 }
222 updateEditActions();
223 updateViewActions();
224 updateGoActions();
225
226 QAction* showFilterBarAction = actionCollection()->action("show_filter_bar");
227 showFilterBarAction->setChecked(generalSettings->filterBar());
228
229 if (firstRun) {
230 menuBar()->setVisible(false);
231 // Assure a proper default size if Dolphin runs the first time
232 resize(750, 500);
233 }
234
235 const bool showMenu = !menuBar()->isHidden();
236 QAction* showMenuBarAction = actionCollection()->action(KStandardAction::name(KStandardAction::ShowMenubar));
237 showMenuBarAction->setChecked(showMenu); // workaround for bug #171080
238 if (!showMenu) {
239 createControlButton();
240 }
241 }
242
243 DolphinMainWindow::~DolphinMainWindow()
244 {
245 }
246
247 void DolphinMainWindow::openDirectories(const QList<KUrl>& dirs)
248 {
249 if (dirs.isEmpty()) {
250 return;
251 }
252
253 if (dirs.count() == 1) {
254 m_activeViewContainer->setUrl(dirs.first());
255 return;
256 }
257
258 const int oldOpenTabsCount = m_viewTab.count();
259
260 const bool hasSplitView = GeneralSettings::splitView();
261
262 // Open each directory inside a new tab. If the "split view" option has been enabled,
263 // always show two directories within one tab.
264 QList<KUrl>::const_iterator it = dirs.constBegin();
265 while (it != dirs.constEnd()) {
266 openNewTab(*it);
267 ++it;
268
269 if (hasSplitView && (it != dirs.constEnd())) {
270 const int tabIndex = m_viewTab.count() - 1;
271 m_viewTab[tabIndex].secondaryView->setUrl(*it);
272 ++it;
273 }
274 }
275
276 // Remove the previously opened tabs
277 for (int i = 0; i < oldOpenTabsCount; ++i) {
278 closeTab(0);
279 }
280 }
281
282 void DolphinMainWindow::openFiles(const QList<KUrl>& files)
283 {
284 if (files.isEmpty()) {
285 return;
286 }
287
288 // Get all distinct directories from 'files' and open a tab
289 // for each directory. If the "split view" option is enabled, two
290 // directories are shown inside one tab (see openDirectories()).
291 QList<KUrl> dirs;
292 foreach (const KUrl& url, files) {
293 const KUrl dir(url.directory());
294 if (!dirs.contains(dir)) {
295 dirs.append(dir);
296 }
297 }
298
299 openDirectories(dirs);
300
301 // Select the files. Although the files can be split between several
302 // tabs, there is no need to split 'files' accordingly, as
303 // the DolphinView will just ignore invalid selections.
304 const int tabCount = m_viewTab.count();
305 for (int i = 0; i < tabCount; ++i) {
306 m_viewTab[i].primaryView->view()->markUrlsAsSelected(files);
307 m_viewTab[i].primaryView->view()->markUrlAsCurrent(files.at(0));
308 if (m_viewTab[i].secondaryView) {
309 m_viewTab[i].secondaryView->view()->markUrlsAsSelected(files);
310 m_viewTab[i].secondaryView->view()->markUrlAsCurrent(files.at(0));
311 }
312 }
313 }
314
315 void DolphinMainWindow::showCommand(CommandType command)
316 {
317 DolphinStatusBar* statusBar = m_activeViewContainer->statusBar();
318 switch (command) {
319 case KIO::FileUndoManager::Copy:
320 statusBar->setText(i18nc("@info:status", "Successfully copied."));
321 break;
322 case KIO::FileUndoManager::Move:
323 statusBar->setText(i18nc("@info:status", "Successfully moved."));
324 break;
325 case KIO::FileUndoManager::Link:
326 statusBar->setText(i18nc("@info:status", "Successfully linked."));
327 break;
328 case KIO::FileUndoManager::Trash:
329 statusBar->setText(i18nc("@info:status", "Successfully moved to trash."));
330 break;
331 case KIO::FileUndoManager::Rename:
332 statusBar->setText(i18nc("@info:status", "Successfully renamed."));
333 break;
334
335 case KIO::FileUndoManager::Mkdir:
336 statusBar->setText(i18nc("@info:status", "Created folder."));
337 break;
338
339 default:
340 break;
341 }
342 }
343
344 void DolphinMainWindow::pasteIntoFolder()
345 {
346 m_activeViewContainer->view()->pasteIntoFolder();
347 }
348
349 void DolphinMainWindow::changeUrl(const KUrl& url)
350 {
351 if (!KProtocolManager::supportsListing(url)) {
352 // The URL navigator only checks for validity, not
353 // if the URL can be listed. An error message is
354 // shown due to DolphinViewContainer::restoreView().
355 return;
356 }
357
358 DolphinViewContainer* view = activeViewContainer();
359 if (view) {
360 view->setUrl(url);
361 updateEditActions();
362 updateViewActions();
363 updateGoActions();
364 setUrlAsCaption(url);
365 if (m_viewTab.count() > 1) {
366 m_tabBar->setTabText(m_tabIndex, squeezedText(tabName(m_activeViewContainer->url())));
367 }
368 const QString iconName = KIO::iconNameForUrl(url);
369 m_tabBar->setTabIcon(m_tabIndex, QIcon::fromTheme(iconName));
370 emit urlChanged(url);
371 }
372 }
373
374 void DolphinMainWindow::slotTerminalDirectoryChanged(const KUrl& url)
375 {
376 m_activeViewContainer->setAutoGrabFocus(false);
377 changeUrl(url);
378 m_activeViewContainer->setAutoGrabFocus(true);
379 }
380
381 void DolphinMainWindow::slotEditableStateChanged(bool editable)
382 {
383 KToggleAction* editableLocationAction =
384 static_cast<KToggleAction*>(actionCollection()->action("editable_location"));
385 editableLocationAction->setChecked(editable);
386 }
387
388 void DolphinMainWindow::slotSelectionChanged(const KFileItemList& selection)
389 {
390 updateEditActions();
391
392 Q_ASSERT(m_viewTab[m_tabIndex].primaryView);
393 int selectedUrlsCount = m_viewTab[m_tabIndex].primaryView->view()->selectedItemsCount();
394 if (m_viewTab[m_tabIndex].secondaryView) {
395 selectedUrlsCount += m_viewTab[m_tabIndex].secondaryView->view()->selectedItemsCount();
396 }
397
398 QAction* compareFilesAction = actionCollection()->action("compare_files");
399 if (selectedUrlsCount == 2) {
400 compareFilesAction->setEnabled(isKompareInstalled());
401 } else {
402 compareFilesAction->setEnabled(false);
403 }
404
405 emit selectionChanged(selection);
406 }
407
408 void DolphinMainWindow::slotRequestItemInfo(const KFileItem& item)
409 {
410 emit requestItemInfo(item);
411 }
412
413 void DolphinMainWindow::updateHistory()
414 {
415 const KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator();
416 const int index = urlNavigator->historyIndex();
417
418 QAction* backAction = actionCollection()->action("go_back");
419 if (backAction) {
420 backAction->setToolTip(i18nc("@info", "Go back"));
421 backAction->setEnabled(index < urlNavigator->historySize() - 1);
422 }
423
424 QAction* forwardAction = actionCollection()->action("go_forward");
425 if (forwardAction) {
426 forwardAction->setToolTip(i18nc("@info", "Go forward"));
427 forwardAction->setEnabled(index > 0);
428 }
429 }
430
431 void DolphinMainWindow::updateFilterBarAction(bool show)
432 {
433 QAction* showFilterBarAction = actionCollection()->action("show_filter_bar");
434 showFilterBarAction->setChecked(show);
435 }
436
437 void DolphinMainWindow::openNewMainWindow()
438 {
439 KRun::run("dolphin %u", KUrl::List(), this);
440 }
441
442 void DolphinMainWindow::openNewTab()
443 {
444 const bool isUrlEditable = m_activeViewContainer->urlNavigator()->isUrlEditable();
445
446 openNewTab(m_activeViewContainer->url());
447 m_tabBar->setCurrentIndex(m_viewTab.count() - 1);
448
449 // The URL navigator of the new tab should have the same editable state
450 // as the current tab
451 KUrlNavigator* navigator = m_activeViewContainer->urlNavigator();
452 navigator->setUrlEditable(isUrlEditable);
453
454 if (isUrlEditable) {
455 // If a new tab is opened and the URL is editable, assure that
456 // the user can edit the URL without manually setting the focus
457 navigator->setFocus();
458 }
459 }
460
461 void DolphinMainWindow::openNewTab(const KUrl& url)
462 {
463 QWidget* focusWidget = QApplication::focusWidget();
464
465 if (m_viewTab.count() == 1) {
466 // Only one view is open currently and hence no tab is shown at
467 // all. Before creating a tab for 'url', provide a tab for the current URL.
468 const KUrl currentUrl = m_activeViewContainer->url();
469 m_tabBar->addTab(QIcon::fromTheme(KIO::iconNameForUrl(currentUrl)),
470 squeezedText(tabName(currentUrl)));
471 m_tabBar->blockSignals(false);
472 }
473
474 m_tabBar->addTab(QIcon::fromTheme(KIO::iconNameForUrl(url)),
475 squeezedText(tabName(url)));
476
477 ViewTab viewTab;
478 viewTab.splitter = new QSplitter(this);
479 viewTab.splitter->setChildrenCollapsible(false);
480 viewTab.primaryView = createViewContainer(url, viewTab.splitter);
481 viewTab.primaryView->setActive(false);
482 connectViewSignals(viewTab.primaryView);
483
484 m_viewTab.append(viewTab);
485
486 actionCollection()->action("close_tab")->setEnabled(true);
487
488 // Provide a split view, if the startup settings are set this way
489 if (GeneralSettings::splitView()) {
490 const int newTabIndex = m_viewTab.count() - 1;
491 createSecondaryView(newTabIndex);
492 m_viewTab[newTabIndex].secondaryView->setActive(true);
493 m_viewTab[newTabIndex].isPrimaryViewActive = false;
494 }
495
496 if (focusWidget) {
497 // The DolphinViewContainer grabbed the keyboard focus. As the tab is opened
498 // in background, assure that the previous focused widget gets the focus back.
499 focusWidget->setFocus();
500 }
501 }
502
503 void DolphinMainWindow::openNewActivatedTab(const KUrl& url)
504 {
505 openNewTab(url);
506 m_tabBar->setCurrentIndex(m_viewTab.count() - 1);
507 }
508
509 void DolphinMainWindow::activateNextTab()
510 {
511 if (m_viewTab.count() >= 2) {
512 const int tabIndex = (m_tabBar->currentIndex() + 1) % m_tabBar->count();
513 m_tabBar->setCurrentIndex(tabIndex);
514 }
515 }
516
517 void DolphinMainWindow::activatePrevTab()
518 {
519 if (m_viewTab.count() >= 2) {
520 int tabIndex = m_tabBar->currentIndex() - 1;
521 if (tabIndex == -1) {
522 tabIndex = m_tabBar->count() - 1;
523 }
524 m_tabBar->setCurrentIndex(tabIndex);
525 }
526 }
527
528 void DolphinMainWindow::openInNewTab()
529 {
530 const KFileItemList& list = m_activeViewContainer->view()->selectedItems();
531 if (list.isEmpty()) {
532 openNewTab(m_activeViewContainer->url());
533 } else {
534 foreach (const KFileItem& item, list) {
535 const KUrl& url = DolphinView::openItemAsFolderUrl(item);
536 if (!url.isEmpty()) {
537 openNewTab(url);
538 }
539 }
540 }
541 }
542
543 void DolphinMainWindow::openInNewWindow()
544 {
545 KUrl newWindowUrl;
546
547 const KFileItemList list = m_activeViewContainer->view()->selectedItems();
548 if (list.isEmpty()) {
549 newWindowUrl = m_activeViewContainer->url();
550 } else if (list.count() == 1) {
551 const KFileItem& item = list.first();
552 newWindowUrl = DolphinView::openItemAsFolderUrl(item);
553 }
554
555 if (!newWindowUrl.isEmpty()) {
556 KRun::run("dolphin %u", QList<QUrl>() << newWindowUrl, this);
557 }
558 }
559
560 void DolphinMainWindow::toggleActiveView()
561 {
562 if (!m_viewTab[m_tabIndex].secondaryView) {
563 // only one view is available
564 return;
565 }
566
567 Q_ASSERT(m_activeViewContainer);
568 Q_ASSERT(m_viewTab[m_tabIndex].primaryView);
569
570 DolphinViewContainer* left = m_viewTab[m_tabIndex].primaryView;
571 DolphinViewContainer* right = m_viewTab[m_tabIndex].secondaryView;
572 setActiveViewContainer(m_activeViewContainer == right ? left : right);
573 }
574
575 void DolphinMainWindow::showEvent(QShowEvent* event)
576 {
577 KXmlGuiWindow::showEvent(event);
578 if (!event->spontaneous()) {
579 m_activeViewContainer->view()->setFocus();
580 }
581 }
582
583 void DolphinMainWindow::closeEvent(QCloseEvent* event)
584 {
585 // Find out if Dolphin is closed directly by the user or
586 // by the session manager because the session is closed
587 bool closedByUser = true;
588 DolphinApplication *application = qobject_cast<DolphinApplication*>(qApp);
589 if (application && application->sessionSaving()) {
590 closedByUser = false;
591 }
592
593 if (m_viewTab.count() > 1 && GeneralSettings::confirmClosingMultipleTabs() && closedByUser) {
594 // Ask the user if he really wants to quit and close all tabs.
595 // Open a confirmation dialog with 3 buttons:
596 // KDialog::Yes -> Quit
597 // KDialog::No -> Close only the current tab
598 // KDialog::Cancel -> do nothing
599 QDialog *dialog = new QDialog(this, Qt::Dialog);
600 dialog->setWindowTitle(i18nc("@title:window", "Confirmation"));
601 dialog->setModal(true);
602 QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Yes | QDialogButtonBox::No | QDialogButtonBox::Cancel);
603 KGuiItem::assign(buttons->button(QDialogButtonBox::Yes), KStandardGuiItem::quit());
604 KGuiItem::assign(buttons->button(QDialogButtonBox::No), KGuiItem(i18n("C&lose Current Tab"), QIcon::fromTheme("tab-close")));
605 KGuiItem::assign(buttons->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel());
606 buttons->button(QDialogButtonBox::Yes)->setDefault(true);
607
608 bool doNotAskAgainCheckboxResult = false;
609
610 const int result = KMessageBox::createKMessageBox(dialog,
611 buttons,
612 QMessageBox::Warning,
613 i18n("You have multiple tabs open in this window, are you sure you want to quit?"),
614 QStringList(),
615 i18n("Do not ask again"),
616 &doNotAskAgainCheckboxResult,
617 KMessageBox::Notify);
618
619 if (doNotAskAgainCheckboxResult) {
620 GeneralSettings::setConfirmClosingMultipleTabs(false);
621 }
622
623 switch (result) {
624 case KDialog::Yes:
625 // Quit
626 break;
627 case KDialog::No:
628 // Close only the current tab
629 closeTab();
630 default:
631 event->ignore();
632 return;
633 }
634 }
635
636 GeneralSettings::setVersion(CurrentDolphinVersion);
637 GeneralSettings::self()->writeConfig();
638
639 KXmlGuiWindow::closeEvent(event);
640 }
641
642 void DolphinMainWindow::saveProperties(KConfigGroup& group)
643 {
644 const int tabCount = m_viewTab.count();
645 group.writeEntry("Tab Count", tabCount);
646 group.writeEntry("Active Tab Index", m_tabBar->currentIndex());
647
648 for (int i = 0; i < tabCount; ++i) {
649 const DolphinViewContainer* cont = m_viewTab[i].primaryView;
650 group.writeEntry(tabProperty("Primary URL", i), cont->url().url());
651 group.writeEntry(tabProperty("Primary Editable", i),
652 cont->urlNavigator()->isUrlEditable());
653
654 cont = m_viewTab[i].secondaryView;
655 if (cont) {
656 group.writeEntry(tabProperty("Secondary URL", i), cont->url().url());
657 group.writeEntry(tabProperty("Secondary Editable", i),
658 cont->urlNavigator()->isUrlEditable());
659 }
660 }
661 }
662
663 void DolphinMainWindow::readProperties(const KConfigGroup& group)
664 {
665 const int tabCount = group.readEntry("Tab Count", 1);
666 for (int i = 0; i < tabCount; ++i) {
667 DolphinViewContainer* cont = m_viewTab[i].primaryView;
668
669 cont->setUrl(group.readEntry(tabProperty("Primary URL", i)));
670 const bool editable = group.readEntry(tabProperty("Primary Editable", i), false);
671 cont->urlNavigator()->setUrlEditable(editable);
672
673 cont = m_viewTab[i].secondaryView;
674 const QString secondaryUrl = group.readEntry(tabProperty("Secondary URL", i));
675 if (!secondaryUrl.isEmpty()) {
676 if (!cont) {
677 // a secondary view should be shown, but no one is available
678 // currently -> create a new view
679 toggleSplitView();
680 cont = m_viewTab[i].secondaryView;
681 Q_ASSERT(cont);
682 }
683
684 // The right view must be activated before the URL is set. Changing
685 // the URL in the right view will emit the right URL navigator's
686 // urlChanged(KUrl) signal, which is connected to the changeUrl(KUrl)
687 // slot. That slot will change the URL in the left view if it is still
688 // active. See https://bugs.kde.org/show_bug.cgi?id=330047.
689 setActiveViewContainer(cont);
690
691 cont->setUrl(secondaryUrl);
692 const bool editable = group.readEntry(tabProperty("Secondary Editable", i), false);
693 cont->urlNavigator()->setUrlEditable(editable);
694 } else if (cont) {
695 // no secondary view should be shown, but the default setting shows
696 // one already -> close the view
697 toggleSplitView();
698 }
699
700 // openNewTab() needs to be called only tabCount - 1 times
701 if (i != tabCount - 1) {
702 openNewTab();
703 }
704 }
705
706 const int index = group.readEntry("Active Tab Index", 0);
707 m_tabBar->setCurrentIndex(index);
708 }
709
710 void DolphinMainWindow::updateNewMenu()
711 {
712 m_newFileMenu->setViewShowsHiddenFiles(activeViewContainer()->view()->hiddenFilesShown());
713 m_newFileMenu->checkUpToDate();
714 m_newFileMenu->setPopupFiles(activeViewContainer()->url());
715 }
716
717 void DolphinMainWindow::createDirectory()
718 {
719 m_newFileMenu->setViewShowsHiddenFiles(activeViewContainer()->view()->hiddenFilesShown());
720 m_newFileMenu->setPopupFiles(activeViewContainer()->url());
721 m_newFileMenu->createDirectory();
722 }
723
724 void DolphinMainWindow::quit()
725 {
726 close();
727 }
728
729 void DolphinMainWindow::showErrorMessage(const QString& message)
730 {
731 m_activeViewContainer->showMessage(message, DolphinViewContainer::Error);
732 }
733
734 void DolphinMainWindow::slotUndoAvailable(bool available)
735 {
736 QAction* undoAction = actionCollection()->action(KStandardAction::name(KStandardAction::Undo));
737 if (undoAction) {
738 undoAction->setEnabled(available);
739 }
740 }
741
742 void DolphinMainWindow::restoreClosedTab(QAction* action)
743 {
744 if (action->data().toBool()) {
745 // clear all actions except the "Empty Recently Closed Tabs"
746 // action and the separator
747 QList<QAction*> actions = m_recentTabsMenu->menu()->actions();
748 const int count = actions.size();
749 for (int i = 2; i < count; ++i) {
750 m_recentTabsMenu->menu()->removeAction(actions.at(i));
751 }
752 } else {
753 const ClosedTab closedTab = action->data().value<ClosedTab>();
754 openNewTab(closedTab.primaryUrl);
755 m_tabBar->setCurrentIndex(m_viewTab.count() - 1);
756
757 if (closedTab.isSplit) {
758 // create secondary view
759 toggleSplitView();
760 m_viewTab[m_tabIndex].secondaryView->setUrl(closedTab.secondaryUrl);
761 }
762
763 m_recentTabsMenu->removeAction(action);
764 }
765
766 if (m_recentTabsMenu->menu()->actions().count() == 2) {
767 m_recentTabsMenu->setEnabled(false);
768 }
769 }
770
771 void DolphinMainWindow::slotUndoTextChanged(const QString& text)
772 {
773 QAction* undoAction = actionCollection()->action(KStandardAction::name(KStandardAction::Undo));
774 if (undoAction) {
775 undoAction->setText(text);
776 }
777 }
778
779 void DolphinMainWindow::undo()
780 {
781 clearStatusBar();
782 KIO::FileUndoManager::self()->uiInterface()->setParentWidget(this);
783 KIO::FileUndoManager::self()->undo();
784 }
785
786 void DolphinMainWindow::cut()
787 {
788 m_activeViewContainer->view()->cutSelectedItems();
789 }
790
791 void DolphinMainWindow::copy()
792 {
793 m_activeViewContainer->view()->copySelectedItems();
794 }
795
796 void DolphinMainWindow::paste()
797 {
798 m_activeViewContainer->view()->paste();
799 }
800
801 void DolphinMainWindow::find()
802 {
803 m_activeViewContainer->setSearchModeEnabled(true);
804 }
805
806 void DolphinMainWindow::updatePasteAction()
807 {
808 QAction* pasteAction = actionCollection()->action(KStandardAction::name(KStandardAction::Paste));
809 QPair<bool, QString> pasteInfo = m_activeViewContainer->view()->pasteInfo();
810 pasteAction->setEnabled(pasteInfo.first);
811 pasteAction->setText(pasteInfo.second);
812 }
813
814 void DolphinMainWindow::selectAll()
815 {
816 clearStatusBar();
817
818 // if the URL navigator is editable and focused, select the whole
819 // URL instead of all items of the view
820
821 KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator();
822 QLineEdit* lineEdit = urlNavigator->editor()->lineEdit(); // krazy:exclude=qclasses
823 const bool selectUrl = urlNavigator->isUrlEditable() &&
824 lineEdit->hasFocus();
825 if (selectUrl) {
826 lineEdit->selectAll();
827 } else {
828 m_activeViewContainer->view()->selectAll();
829 }
830 }
831
832 void DolphinMainWindow::invertSelection()
833 {
834 clearStatusBar();
835 m_activeViewContainer->view()->invertSelection();
836 }
837
838 void DolphinMainWindow::toggleSplitView()
839 {
840 if (!m_viewTab[m_tabIndex].secondaryView) {
841 createSecondaryView(m_tabIndex);
842 setActiveViewContainer(m_viewTab[m_tabIndex].secondaryView);
843 } else if (m_activeViewContainer == m_viewTab[m_tabIndex].secondaryView) {
844 // remove secondary view
845 m_viewTab[m_tabIndex].secondaryView->close();
846 m_viewTab[m_tabIndex].secondaryView->deleteLater();
847 m_viewTab[m_tabIndex].secondaryView = 0;
848
849 setActiveViewContainer(m_viewTab[m_tabIndex].primaryView);
850 } else {
851 // The primary view is active and should be closed. Hence from a users point of view
852 // the content of the secondary view should be moved to the primary view.
853 // From an implementation point of view it is more efficient to close
854 // the primary view and exchange the internal pointers afterwards.
855
856 m_viewTab[m_tabIndex].primaryView->close();
857 m_viewTab[m_tabIndex].primaryView->deleteLater();
858 m_viewTab[m_tabIndex].primaryView = m_viewTab[m_tabIndex].secondaryView;
859 m_viewTab[m_tabIndex].secondaryView = 0;
860
861 setActiveViewContainer(m_viewTab[m_tabIndex].primaryView);
862 }
863
864 updateViewActions();
865 }
866
867 void DolphinMainWindow::reloadView()
868 {
869 clearStatusBar();
870 m_activeViewContainer->view()->reload();
871 }
872
873 void DolphinMainWindow::stopLoading()
874 {
875 m_activeViewContainer->view()->stopLoading();
876 }
877
878 void DolphinMainWindow::enableStopAction()
879 {
880 actionCollection()->action("stop")->setEnabled(true);
881 }
882
883 void DolphinMainWindow::disableStopAction()
884 {
885 actionCollection()->action("stop")->setEnabled(false);
886 }
887
888 void DolphinMainWindow::showFilterBar()
889 {
890 m_activeViewContainer->setFilterBarVisible(true);
891 }
892
893 void DolphinMainWindow::toggleEditLocation()
894 {
895 clearStatusBar();
896
897 QAction* action = actionCollection()->action("editable_location");
898 KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator();
899 urlNavigator->setUrlEditable(action->isChecked());
900 }
901
902 void DolphinMainWindow::replaceLocation()
903 {
904 KUrlNavigator* navigator = m_activeViewContainer->urlNavigator();
905 navigator->setUrlEditable(true);
906 navigator->setFocus();
907
908 // select the whole text of the combo box editor
909 QLineEdit* lineEdit = navigator->editor()->lineEdit(); // krazy:exclude=qclasses
910 lineEdit->selectAll();
911 }
912
913 void DolphinMainWindow::togglePanelLockState()
914 {
915 const bool newLockState = !GeneralSettings::lockPanels();
916 foreach (QObject* child, children()) {
917 DolphinDockWidget* dock = qobject_cast<DolphinDockWidget*>(child);
918 if (dock) {
919 dock->setLocked(newLockState);
920 }
921 }
922
923 GeneralSettings::setLockPanels(newLockState);
924 }
925
926 void DolphinMainWindow::slotPlacesPanelVisibilityChanged(bool visible)
927 {
928 const int tabCount = m_viewTab.count();
929 for (int i = 0; i < tabCount; ++i) {
930 ViewTab& tab = m_viewTab[i];
931 Q_ASSERT(tab.primaryView);
932 tab.primaryView->urlNavigator()->setPlacesSelectorVisible(!visible);
933 if (tab.secondaryView) {
934 tab.secondaryView->urlNavigator()->setPlacesSelectorVisible(!visible);
935 }
936 }
937 }
938
939 void DolphinMainWindow::goBack()
940 {
941 KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator();
942 urlNavigator->goBack();
943
944 if (urlNavigator->locationState().isEmpty()) {
945 // An empty location state indicates a redirection URL,
946 // which must be skipped too
947 urlNavigator->goBack();
948 }
949 }
950
951 void DolphinMainWindow::goForward()
952 {
953 m_activeViewContainer->urlNavigator()->goForward();
954 }
955
956 void DolphinMainWindow::goUp()
957 {
958 m_activeViewContainer->urlNavigator()->goUp();
959 }
960
961 void DolphinMainWindow::goHome()
962 {
963 m_activeViewContainer->urlNavigator()->goHome();
964 }
965
966 void DolphinMainWindow::goBack(Qt::MouseButtons buttons)
967 {
968 // The default case (left button pressed) is handled in goBack().
969 if (buttons == Qt::MidButton) {
970 KUrlNavigator* urlNavigator = activeViewContainer()->urlNavigator();
971 const int index = urlNavigator->historyIndex() + 1;
972 openNewTab(urlNavigator->locationUrl(index));
973 }
974 }
975
976 void DolphinMainWindow::goForward(Qt::MouseButtons buttons)
977 {
978 // The default case (left button pressed) is handled in goForward().
979 if (buttons == Qt::MidButton) {
980 KUrlNavigator* urlNavigator = activeViewContainer()->urlNavigator();
981 const int index = urlNavigator->historyIndex() - 1;
982 openNewTab(urlNavigator->locationUrl(index));
983 }
984 }
985
986 void DolphinMainWindow::goUp(Qt::MouseButtons buttons)
987 {
988 // The default case (left button pressed) is handled in goUp().
989 if (buttons == Qt::MidButton) {
990 openNewTab(activeViewContainer()->url().upUrl());
991 }
992 }
993
994 void DolphinMainWindow::goHome(Qt::MouseButtons buttons)
995 {
996 // The default case (left button pressed) is handled in goHome().
997 if (buttons == Qt::MidButton) {
998 openNewTab(GeneralSettings::self()->homeUrl());
999 }
1000 }
1001
1002 void DolphinMainWindow::compareFiles()
1003 {
1004 const DolphinViewContainer* primaryViewContainer = m_viewTab[m_tabIndex].primaryView;
1005 Q_ASSERT(primaryViewContainer);
1006 KFileItemList items = primaryViewContainer->view()->selectedItems();
1007
1008 const DolphinViewContainer* secondaryViewContainer = m_viewTab[m_tabIndex].secondaryView;
1009 if (secondaryViewContainer) {
1010 items.append(secondaryViewContainer->view()->selectedItems());
1011 }
1012
1013 if (items.count() != 2) {
1014 // The action is disabled in this case, but it could have been triggered
1015 // via D-Bus, see https://bugs.kde.org/show_bug.cgi?id=325517
1016 return;
1017 }
1018
1019 KUrl urlA = items.at(0).url();
1020 KUrl urlB = items.at(1).url();
1021
1022 QString command("kompare -c \"");
1023 command.append(urlA.pathOrUrl());
1024 command.append("\" \"");
1025 command.append(urlB.pathOrUrl());
1026 command.append('\"');
1027 KRun::runCommand(command, "Kompare", "kompare", this);
1028 }
1029
1030 void DolphinMainWindow::toggleShowMenuBar()
1031 {
1032 const bool visible = menuBar()->isVisible();
1033 menuBar()->setVisible(!visible);
1034 if (visible) {
1035 createControlButton();
1036 } else {
1037 deleteControlButton();
1038 }
1039 }
1040
1041 void DolphinMainWindow::openTerminal()
1042 {
1043 QString dir(QDir::homePath());
1044
1045 // If the given directory is not local, it can still be the URL of an
1046 // ioslave using UDS_LOCAL_PATH which to be converted first.
1047 KUrl url = KIO::NetAccess::mostLocalUrl(m_activeViewContainer->url(), this);
1048
1049 //If the URL is local after the above conversion, set the directory.
1050 if (url.isLocalFile()) {
1051 dir = url.toLocalFile();
1052 }
1053
1054 KToolInvocation::invokeTerminal(QString(), dir);
1055 }
1056
1057 void DolphinMainWindow::editSettings()
1058 {
1059 if (!m_settingsDialog) {
1060 DolphinViewContainer* container = activeViewContainer();
1061 container->view()->writeSettings();
1062
1063 const KUrl url = container->url();
1064 DolphinSettingsDialog* settingsDialog = new DolphinSettingsDialog(url, this);
1065 connect(settingsDialog, &DolphinSettingsDialog::settingsChanged, this, &DolphinMainWindow::refreshViews);
1066 settingsDialog->setAttribute(Qt::WA_DeleteOnClose);
1067 settingsDialog->show();
1068 m_settingsDialog = settingsDialog;
1069 } else {
1070 m_settingsDialog.data()->raise();
1071 }
1072 }
1073
1074 void DolphinMainWindow::setActiveTab(int index)
1075 {
1076 Q_ASSERT(index >= 0);
1077 Q_ASSERT(index < m_viewTab.count());
1078 if (index == m_tabIndex) {
1079 return;
1080 }
1081
1082 // hide current tab content
1083 ViewTab& hiddenTab = m_viewTab[m_tabIndex];
1084 hiddenTab.isPrimaryViewActive = hiddenTab.primaryView->isActive();
1085 hiddenTab.primaryView->setActive(false);
1086 if (hiddenTab.secondaryView) {
1087 hiddenTab.secondaryView->setActive(false);
1088 }
1089 QSplitter* splitter = m_viewTab[m_tabIndex].splitter;
1090 splitter->hide();
1091 m_centralWidgetLayout->removeWidget(splitter);
1092
1093 // show active tab content
1094 m_tabIndex = index;
1095
1096 ViewTab& viewTab = m_viewTab[index];
1097 m_centralWidgetLayout->addWidget(viewTab.splitter, 1);
1098 viewTab.primaryView->show();
1099 if (viewTab.secondaryView) {
1100 viewTab.secondaryView->show();
1101 }
1102 viewTab.splitter->show();
1103
1104 if (!viewTab.wasActive) {
1105 viewTab.wasActive = true;
1106
1107 // If the tab has not been activated yet the size of the KItemListView is
1108 // undefined and results in an unwanted animation. To prevent this a
1109 // reloading of the directory gets triggered.
1110 viewTab.primaryView->view()->reload();
1111 if (viewTab.secondaryView) {
1112 viewTab.secondaryView->view()->reload();
1113 }
1114 }
1115
1116 setActiveViewContainer(viewTab.isPrimaryViewActive ? viewTab.primaryView :
1117 viewTab.secondaryView);
1118 }
1119
1120 void DolphinMainWindow::closeTab()
1121 {
1122 closeTab(m_tabBar->currentIndex());
1123 }
1124
1125 void DolphinMainWindow::closeTab(int index)
1126 {
1127 Q_ASSERT(index >= 0);
1128 Q_ASSERT(index < m_viewTab.count());
1129 if (m_viewTab.count() == 1) {
1130 // the last tab may never get closed
1131 return;
1132 }
1133
1134 if (index == m_tabIndex) {
1135 // The tab that should be closed is the active tab. Activate the
1136 // previous tab before closing the tab.
1137 m_tabBar->setCurrentIndex((index > 0) ? index - 1 : 1);
1138 }
1139 rememberClosedTab(index);
1140
1141 // delete tab
1142 m_viewTab[index].primaryView->deleteLater();
1143 if (m_viewTab[index].secondaryView) {
1144 m_viewTab[index].secondaryView->deleteLater();
1145 }
1146 m_viewTab[index].splitter->deleteLater();
1147 m_viewTab.erase(m_viewTab.begin() + index);
1148
1149 m_tabBar->blockSignals(true);
1150 m_tabBar->removeTab(index);
1151
1152 if (m_tabIndex > index) {
1153 m_tabIndex--;
1154 Q_ASSERT(m_tabIndex >= 0);
1155 }
1156
1157 // if only one tab is left, also remove the tab entry so that
1158 // closing the last tab is not possible
1159 if (m_viewTab.count() == 1) {
1160 m_tabBar->removeTab(0);
1161 actionCollection()->action("close_tab")->setEnabled(false);
1162 } else {
1163 m_tabBar->blockSignals(false);
1164 }
1165 }
1166
1167 void DolphinMainWindow::openTabContextMenu(int index, const QPoint& pos)
1168 {
1169 KMenu menu(this);
1170
1171 QAction* newTabAction = menu.addAction(QIcon::fromTheme("tab-new"), i18nc("@action:inmenu", "New Tab"));
1172 newTabAction->setShortcut(actionCollection()->action("new_tab")->shortcut());
1173
1174 QAction* detachTabAction = menu.addAction(QIcon::fromTheme("tab-detach"), i18nc("@action:inmenu", "Detach Tab"));
1175
1176 QAction* closeOtherTabsAction = menu.addAction(QIcon::fromTheme("tab-close-other"), i18nc("@action:inmenu", "Close Other Tabs"));
1177
1178 QAction* closeTabAction = menu.addAction(QIcon::fromTheme("tab-close"), i18nc("@action:inmenu", "Close Tab"));
1179 closeTabAction->setShortcut(actionCollection()->action("close_tab")->shortcut());
1180 QAction* selectedAction = menu.exec(pos);
1181 if (selectedAction == newTabAction) {
1182 const ViewTab& tab = m_viewTab[index];
1183 Q_ASSERT(tab.primaryView);
1184 const KUrl url = tab.secondaryView && tab.secondaryView->isActive() ?
1185 tab.secondaryView->url() : tab.primaryView->url();
1186 openNewTab(url);
1187 m_tabBar->setCurrentIndex(m_viewTab.count() - 1);
1188 } else if (selectedAction == detachTabAction) {
1189 const QString separator(QLatin1Char(' '));
1190 QString command = QLatin1String("dolphin");
1191
1192 const ViewTab& tab = m_viewTab[index];
1193 Q_ASSERT(tab.primaryView);
1194
1195 command += separator + tab.primaryView->url().url();
1196 if (tab.secondaryView) {
1197 command += separator + tab.secondaryView->url().url();
1198 command += separator + QLatin1String("-split");
1199 }
1200
1201 KRun::runCommand(command, this);
1202
1203 closeTab(index);
1204 } else if (selectedAction == closeOtherTabsAction) {
1205 const int count = m_tabBar->count();
1206 for (int i = 0; i < index; ++i) {
1207 closeTab(0);
1208 }
1209 for (int i = index + 1; i < count; ++i) {
1210 closeTab(1);
1211 }
1212 } else if (selectedAction == closeTabAction) {
1213 closeTab(index);
1214 }
1215 }
1216
1217 void DolphinMainWindow::slotTabMoved(int from, int to)
1218 {
1219 m_viewTab.move(from, to);
1220 m_tabIndex = m_tabBar->currentIndex();
1221 }
1222
1223 void DolphinMainWindow::slotTestCanDecode(const QDragMoveEvent* event, bool& canDecode)
1224 {
1225 canDecode = KUrl::List::canDecode(event->mimeData());
1226 }
1227
1228 void DolphinMainWindow::handleUrl(const KUrl& url)
1229 {
1230 delete m_lastHandleUrlStatJob;
1231 m_lastHandleUrlStatJob = 0;
1232
1233 if (url.isLocalFile() && QFileInfo(url.toLocalFile()).isDir()) {
1234 activeViewContainer()->setUrl(url);
1235 } else if (KProtocolManager::supportsListing(url)) {
1236 // stat the URL to see if it is a dir or not
1237 m_lastHandleUrlStatJob = KIO::stat(url, KIO::HideProgressInfo);
1238 if (m_lastHandleUrlStatJob->ui()) {
1239 KJobWidgets::setWindow(m_lastHandleUrlStatJob, this);
1240 }
1241 connect(m_lastHandleUrlStatJob, &KIO::Job::result,
1242 this, &DolphinMainWindow::slotHandleUrlStatFinished);
1243
1244 } else {
1245 new KRun(url, this); // Automatically deletes itself after being finished
1246 }
1247 }
1248
1249 void DolphinMainWindow::slotHandleUrlStatFinished(KJob* job)
1250 {
1251 m_lastHandleUrlStatJob = 0;
1252 const KIO::UDSEntry entry = static_cast<KIO::StatJob*>(job)->statResult();
1253 const KUrl url = static_cast<KIO::StatJob*>(job)->url();
1254 if (entry.isDir()) {
1255 activeViewContainer()->setUrl(url);
1256 } else {
1257 new KRun(url, this); // Automatically deletes itself after being finished
1258 }
1259 }
1260
1261 void DolphinMainWindow::tabDropEvent(int tab, QDropEvent* event)
1262 {
1263 const KUrl::List urls = KUrl::List::fromMimeData(event->mimeData());
1264 if (!urls.isEmpty() && tab != -1) {
1265 const ViewTab& viewTab = m_viewTab[tab];
1266 const DolphinView* view = viewTab.isPrimaryViewActive ? viewTab.primaryView->view()
1267 : viewTab.secondaryView->view();
1268 QString error;
1269 DragAndDropHelper::dropUrls(view->rootItem(), view->url(), event, error);
1270 if (!error.isEmpty()) {
1271 activeViewContainer()->showMessage(error, DolphinViewContainer::Error);
1272 }
1273 }
1274 }
1275
1276 void DolphinMainWindow::slotWriteStateChanged(bool isFolderWritable)
1277 {
1278 newFileMenu()->setEnabled(isFolderWritable);
1279 }
1280
1281 void DolphinMainWindow::openContextMenu(const QPoint& pos,
1282 const KFileItem& item,
1283 const KUrl& url,
1284 const QList<QAction*>& customActions)
1285 {
1286 QWeakPointer<DolphinContextMenu> contextMenu = new DolphinContextMenu(this, pos, item, url);
1287 contextMenu.data()->setCustomActions(customActions);
1288 const DolphinContextMenu::Command command = contextMenu.data()->open();
1289
1290 switch (command) {
1291 case DolphinContextMenu::OpenParentFolderInNewWindow: {
1292
1293 KRun::run("dolphin %u", QList<QUrl>() << KIO::upUrl(item.url()), this);
1294 break;
1295 }
1296
1297 case DolphinContextMenu::OpenParentFolderInNewTab:
1298 openNewTab(KIO::upUrl(item.url()));
1299 break;
1300
1301 case DolphinContextMenu::None:
1302 default:
1303 break;
1304 }
1305
1306 delete contextMenu.data();
1307 }
1308
1309 void DolphinMainWindow::updateControlMenu()
1310 {
1311 KMenu* menu = qobject_cast<KMenu*>(sender());
1312 Q_ASSERT(menu);
1313
1314 // All actions get cleared by KMenu::clear(). The sub-menus are deleted
1315 // by connecting to the aboutToHide() signal from the parent-menu.
1316 menu->clear();
1317
1318 KActionCollection* ac = actionCollection();
1319
1320 // Add "Edit" actions
1321 bool added = addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Undo)), menu) |
1322 addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Find)), menu) |
1323 addActionToMenu(ac->action("select_all"), menu) |
1324 addActionToMenu(ac->action("invert_selection"), menu);
1325
1326 if (added) {
1327 menu->addSeparator();
1328 }
1329
1330 // Add "View" actions
1331 if (!GeneralSettings::showZoomSlider()) {
1332 addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ZoomIn)), menu);
1333 addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ZoomOut)), menu);
1334 menu->addSeparator();
1335 }
1336
1337 added = addActionToMenu(ac->action("view_mode"), menu) |
1338 addActionToMenu(ac->action("sort"), menu) |
1339 addActionToMenu(ac->action("additional_info"), menu) |
1340 addActionToMenu(ac->action("show_preview"), menu) |
1341 addActionToMenu(ac->action("show_in_groups"), menu) |
1342 addActionToMenu(ac->action("show_hidden_files"), menu);
1343
1344 if (added) {
1345 menu->addSeparator();
1346 }
1347
1348 added = addActionToMenu(ac->action("split_view"), menu) |
1349 addActionToMenu(ac->action("reload"), menu) |
1350 addActionToMenu(ac->action("view_properties"), menu);
1351 if (added) {
1352 menu->addSeparator();
1353 }
1354
1355 addActionToMenu(ac->action("panels"), menu);
1356 KMenu* locationBarMenu = new KMenu(i18nc("@action:inmenu", "Location Bar"), menu);
1357 locationBarMenu->addAction(ac->action("editable_location"));
1358 locationBarMenu->addAction(ac->action("replace_location"));
1359 menu->addMenu(locationBarMenu);
1360
1361 menu->addSeparator();
1362
1363 // Add "Go" menu
1364 KMenu* goMenu = new KMenu(i18nc("@action:inmenu", "Go"), menu);
1365 connect(menu, &KMenu::aboutToHide, goMenu, &KMenu::deleteLater);
1366 goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Back)));
1367 goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Forward)));
1368 goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Up)));
1369 goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Home)));
1370 goMenu->addAction(ac->action("closed_tabs"));
1371 menu->addMenu(goMenu);
1372
1373 // Add "Tool" menu
1374 KMenu* toolsMenu = new KMenu(i18nc("@action:inmenu", "Tools"), menu);
1375 connect(menu, &KMenu::aboutToHide, toolsMenu, &KMenu::deleteLater);
1376 toolsMenu->addAction(ac->action("show_filter_bar"));
1377 toolsMenu->addAction(ac->action("compare_files"));
1378 toolsMenu->addAction(ac->action("open_terminal"));
1379 toolsMenu->addAction(ac->action("change_remote_encoding"));
1380 menu->addMenu(toolsMenu);
1381
1382 // Add "Settings" menu entries
1383 addActionToMenu(ac->action(KStandardAction::name(KStandardAction::KeyBindings)), menu);
1384 addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ConfigureToolbars)), menu);
1385 addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Preferences)), menu);
1386
1387 // Add "Help" menu
1388 KMenu* helpMenu = new KMenu(i18nc("@action:inmenu", "Help"), menu);
1389 connect(menu, &KMenu::aboutToHide, helpMenu, &KMenu::deleteLater);
1390 helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::HelpContents)));
1391 helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::WhatsThis)));
1392 helpMenu->addSeparator();
1393 helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::ReportBug)));
1394 helpMenu->addSeparator();
1395 helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::SwitchApplicationLanguage)));
1396 helpMenu->addSeparator();
1397 helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::AboutApp)));
1398 helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::AboutKDE)));
1399 menu->addMenu(helpMenu);
1400
1401 menu->addSeparator();
1402 addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ShowMenubar)), menu);
1403 }
1404
1405 void DolphinMainWindow::updateToolBar()
1406 {
1407 if (!menuBar()->isVisible()) {
1408 createControlButton();
1409 }
1410 }
1411
1412 void DolphinMainWindow::slotControlButtonDeleted()
1413 {
1414 m_controlButton = 0;
1415 m_updateToolBarTimer->start();
1416 }
1417
1418 void DolphinMainWindow::slotPanelErrorMessage(const QString& error)
1419 {
1420 activeViewContainer()->showMessage(error, DolphinViewContainer::Error);
1421 }
1422
1423 void DolphinMainWindow::slotPlaceActivated(const KUrl& url)
1424 {
1425 DolphinViewContainer* view = activeViewContainer();
1426
1427 if (view->url() == url) {
1428 // We can end up here if the user clicked a device in the Places Panel
1429 // which had been unmounted earlier, see https://bugs.kde.org/show_bug.cgi?id=161385.
1430 reloadView();
1431 } else {
1432 changeUrl(url);
1433 }
1434 }
1435
1436 void DolphinMainWindow::setActiveViewContainer(DolphinViewContainer* viewContainer)
1437 {
1438 Q_ASSERT(viewContainer);
1439 Q_ASSERT((viewContainer == m_viewTab[m_tabIndex].primaryView) ||
1440 (viewContainer == m_viewTab[m_tabIndex].secondaryView));
1441 if (m_activeViewContainer == viewContainer) {
1442 return;
1443 }
1444
1445 m_activeViewContainer->setActive(false);
1446 m_activeViewContainer = viewContainer;
1447
1448 // Activating the view container might trigger a recursive setActiveViewContainer() call
1449 // inside DolphinMainWindow::toggleActiveView() when having a split view. Temporary
1450 // disconnect the activated() signal in this case:
1451 disconnect(m_activeViewContainer->view(), &DolphinView::activated, this, &DolphinMainWindow::toggleActiveView);
1452 m_activeViewContainer->setActive(true);
1453 connect(m_activeViewContainer->view(), &DolphinView::activated, this, &DolphinMainWindow::toggleActiveView);
1454
1455 m_actionHandler->setCurrentView(viewContainer->view());
1456
1457 updateHistory();
1458 updateEditActions();
1459 updateViewActions();
1460 updateGoActions();
1461
1462 const KUrl url = m_activeViewContainer->url();
1463 setUrlAsCaption(url);
1464 if (m_viewTab.count() > 1) {
1465 m_tabBar->setTabText(m_tabIndex, tabName(url));
1466 m_tabBar->setTabIcon(m_tabIndex, QIcon::fromTheme(KIO::iconNameForUrl(url)));
1467 }
1468
1469 emit urlChanged(url);
1470 }
1471
1472 DolphinViewContainer* DolphinMainWindow::createViewContainer(const KUrl& url, QWidget* parent)
1473 {
1474 DolphinViewContainer* container = new DolphinViewContainer(url, parent);
1475
1476 // The places-selector from the URL navigator should only be shown
1477 // if the places dock is invisible
1478 QDockWidget* placesDock = findChild<QDockWidget*>("placesDock");
1479 container->urlNavigator()->setPlacesSelectorVisible(!placesDock || !placesDock->isVisible());
1480
1481 return container;
1482 }
1483
1484 void DolphinMainWindow::setupActions()
1485 {
1486 // setup 'File' menu
1487 m_newFileMenu = new DolphinNewFileMenu(actionCollection(), this);
1488 QMenu* menu = m_newFileMenu->menu();
1489 menu->setTitle(i18nc("@title:menu Create new folder, file, link, etc.", "Create New"));
1490 menu->setIcon(QIcon::fromTheme("document-new"));
1491 m_newFileMenu->setDelayed(false);
1492 connect(menu, &QMenu::aboutToShow,
1493 this, &DolphinMainWindow::updateNewMenu);
1494
1495 QAction* newWindow = actionCollection()->addAction("new_window");
1496 newWindow->setIcon(QIcon::fromTheme("window-new"));
1497 newWindow->setText(i18nc("@action:inmenu File", "New &Window"));
1498 newWindow->setShortcut(Qt::CTRL | Qt::Key_N);
1499 connect(newWindow, &QAction::triggered, this, &DolphinMainWindow::openNewMainWindow);
1500
1501 QAction* newTab = actionCollection()->addAction("new_tab");
1502 newTab->setIcon(QIcon::fromTheme("tab-new"));
1503 newTab->setText(i18nc("@action:inmenu File", "New Tab"));
1504 newTab->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL | Qt::Key_T) << QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_N));
1505 connect(newTab, &QAction::triggered, this, static_cast<void(DolphinMainWindow::*)()>(&DolphinMainWindow::openNewTab));
1506
1507 QAction* closeTab = actionCollection()->addAction("close_tab");
1508 closeTab->setIcon(QIcon::fromTheme("tab-close"));
1509 closeTab->setText(i18nc("@action:inmenu File", "Close Tab"));
1510 closeTab->setShortcut(Qt::CTRL | Qt::Key_W);
1511 closeTab->setEnabled(false);
1512 connect(closeTab, &QAction::triggered, this, static_cast<void(DolphinMainWindow::*)()>(&DolphinMainWindow::closeTab));
1513
1514 KStandardAction::quit(this, SLOT(quit()), actionCollection());
1515
1516 // setup 'Edit' menu
1517 KStandardAction::undo(this,
1518 SLOT(undo()),
1519 actionCollection());
1520
1521 // need to remove shift+del from cut action, else the shortcut for deletejob
1522 // doesn't work
1523 QAction* cut = KStandardAction::cut(this, SLOT(cut()), actionCollection());
1524 auto cutShortcuts = cut->shortcuts();
1525 cutShortcuts.removeAll(QKeySequence(Qt::SHIFT | Qt::Key_Delete));
1526 cut->setShortcuts(cutShortcuts);
1527 KStandardAction::copy(this, SLOT(copy()), actionCollection());
1528 QAction* paste = KStandardAction::paste(this, SLOT(paste()), actionCollection());
1529 // The text of the paste-action is modified dynamically by Dolphin
1530 // (e. g. to "Paste One Folder"). To prevent that the size of the toolbar changes
1531 // due to the long text, the text "Paste" is used:
1532 paste->setIconText(i18nc("@action:inmenu Edit", "Paste"));
1533
1534 KStandardAction::find(this, SLOT(find()), actionCollection());
1535
1536 QAction* selectAll = actionCollection()->addAction("select_all");
1537 selectAll->setText(i18nc("@action:inmenu Edit", "Select All"));
1538 selectAll->setShortcut(Qt::CTRL | Qt::Key_A);
1539 connect(selectAll, &QAction::triggered, this, &DolphinMainWindow::selectAll);
1540
1541 QAction* invertSelection = actionCollection()->addAction("invert_selection");
1542 invertSelection->setText(i18nc("@action:inmenu Edit", "Invert Selection"));
1543 invertSelection->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_A);
1544 connect(invertSelection, &QAction::triggered, this, &DolphinMainWindow::invertSelection);
1545
1546 // setup 'View' menu
1547 // (note that most of it is set up in DolphinViewActionHandler)
1548
1549 QAction* split = actionCollection()->addAction("split_view");
1550 split->setShortcut(Qt::Key_F3);
1551 updateSplitAction();
1552 connect(split, &QAction::triggered, this, &DolphinMainWindow::toggleSplitView);
1553
1554 QAction* reload = actionCollection()->addAction("reload");
1555 reload->setText(i18nc("@action:inmenu View", "Reload"));
1556 reload->setShortcut(Qt::Key_F5);
1557 reload->setIcon(QIcon::fromTheme("view-refresh"));
1558 connect(reload, &QAction::triggered, this, &DolphinMainWindow::reloadView);
1559
1560 QAction* stop = actionCollection()->addAction("stop");
1561 stop->setText(i18nc("@action:inmenu View", "Stop"));
1562 stop->setToolTip(i18nc("@info", "Stop loading"));
1563 stop->setIcon(QIcon::fromTheme("process-stop"));
1564 connect(stop, &QAction::triggered, this, &DolphinMainWindow::stopLoading);
1565
1566 KToggleAction* editableLocation = actionCollection()->add<KToggleAction>("editable_location");
1567 editableLocation->setText(i18nc("@action:inmenu Navigation Bar", "Editable Location"));
1568 editableLocation->setShortcut(Qt::Key_F6);
1569 connect(editableLocation, &KToggleAction::triggered, this, &DolphinMainWindow::toggleEditLocation);
1570
1571 QAction* replaceLocation = actionCollection()->addAction("replace_location");
1572 replaceLocation->setText(i18nc("@action:inmenu Navigation Bar", "Replace Location"));
1573 replaceLocation->setShortcut(Qt::CTRL | Qt::Key_L);
1574 connect(replaceLocation, &QAction::triggered, this, &DolphinMainWindow::replaceLocation);
1575
1576 // setup 'Go' menu
1577 QAction* backAction = KStandardAction::back(this, SLOT(goBack()), actionCollection());
1578 connect(backAction, &QAction::triggered, this, static_cast<void(DolphinMainWindow::*)()>(&DolphinMainWindow::goBack));
1579 auto backShortcuts = backAction->shortcuts();
1580 backShortcuts.append(QKeySequence(Qt::Key_Backspace));
1581 backAction->setShortcuts(backShortcuts);
1582
1583 m_recentTabsMenu = new KActionMenu(i18n("Recently Closed Tabs"), this);
1584 m_recentTabsMenu->setIcon(QIcon::fromTheme("edit-undo"));
1585 m_recentTabsMenu->setDelayed(false);
1586 actionCollection()->addAction("closed_tabs", m_recentTabsMenu);
1587 connect(m_recentTabsMenu->menu(), &QMenu::triggered,
1588 this, &DolphinMainWindow::restoreClosedTab);
1589
1590 QAction* action = new QAction(i18n("Empty Recently Closed Tabs"), m_recentTabsMenu);
1591 action->setIcon(QIcon::fromTheme("edit-clear-list"));
1592 action->setData(QVariant::fromValue(true));
1593 m_recentTabsMenu->addAction(action);
1594 m_recentTabsMenu->addSeparator();
1595 m_recentTabsMenu->setEnabled(false);
1596
1597 QAction* forwardAction = KStandardAction::forward(this, SLOT(goForward()), actionCollection());
1598 connect(forwardAction, &QAction::triggered, this, static_cast<void(DolphinMainWindow::*)()>(&DolphinMainWindow::goForward));
1599
1600 QAction* upAction = KStandardAction::up(this, SLOT(goUp()), actionCollection());
1601 connect(upAction, &QAction::triggered, this, static_cast<void(DolphinMainWindow::*)()>(&DolphinMainWindow::goUp));
1602
1603 QAction* homeAction = KStandardAction::home(this, SLOT(goHome()), actionCollection());
1604 connect(homeAction, &QAction::triggered, this, static_cast<void(DolphinMainWindow::*)()>(&DolphinMainWindow::goHome));
1605
1606 // setup 'Tools' menu
1607 QAction* showFilterBar = actionCollection()->addAction("show_filter_bar");
1608 showFilterBar->setText(i18nc("@action:inmenu Tools", "Show Filter Bar"));
1609 showFilterBar->setIcon(QIcon::fromTheme("view-filter"));
1610 showFilterBar->setShortcut(Qt::CTRL | Qt::Key_I);
1611 connect(showFilterBar, &QAction::triggered, this, &DolphinMainWindow::showFilterBar);
1612
1613 QAction* compareFiles = actionCollection()->addAction("compare_files");
1614 compareFiles->setText(i18nc("@action:inmenu Tools", "Compare Files"));
1615 compareFiles->setIcon(QIcon::fromTheme("kompare"));
1616 compareFiles->setEnabled(false);
1617 connect(compareFiles, &QAction::triggered, this, &DolphinMainWindow::compareFiles);
1618
1619 QAction* openTerminal = actionCollection()->addAction("open_terminal");
1620 openTerminal->setText(i18nc("@action:inmenu Tools", "Open Terminal"));
1621 openTerminal->setIcon(QIcon::fromTheme("utilities-terminal"));
1622 openTerminal->setShortcut(Qt::SHIFT | Qt::Key_F4);
1623 connect(openTerminal, &QAction::triggered, this, &DolphinMainWindow::openTerminal);
1624
1625 // setup 'Settings' menu
1626 KToggleAction* showMenuBar = KStandardAction::showMenubar(0, 0, actionCollection());
1627 connect(showMenuBar, &KToggleAction::triggered, // Fixes #286822
1628 this, &DolphinMainWindow::toggleShowMenuBar, Qt::QueuedConnection);
1629 KStandardAction::preferences(this, SLOT(editSettings()), actionCollection());
1630
1631 // not in menu actions
1632 QList<QKeySequence> nextTabKeys;
1633 nextTabKeys.append(KStandardShortcut::tabNext().first()); //TODO: is this correct
1634 nextTabKeys.append(QKeySequence(Qt::CTRL | Qt::Key_Tab));
1635
1636 QList<QKeySequence> prevTabKeys;
1637 prevTabKeys.append(KStandardShortcut::tabPrev().first()); //TODO: is this correct
1638 prevTabKeys.append(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_Tab));
1639
1640 QAction* activateNextTab = actionCollection()->addAction("activate_next_tab");
1641 activateNextTab->setText(i18nc("@action:inmenu", "Activate Next Tab"));
1642 connect(activateNextTab, &QAction::triggered, this, &DolphinMainWindow::activateNextTab);
1643 activateNextTab->setShortcuts(QApplication::isRightToLeft() ? prevTabKeys : nextTabKeys);
1644
1645 QAction* activatePrevTab = actionCollection()->addAction("activate_prev_tab");
1646 activatePrevTab->setText(i18nc("@action:inmenu", "Activate Previous Tab"));
1647 connect(activatePrevTab, &QAction::triggered, this, &DolphinMainWindow::activatePrevTab);
1648 activatePrevTab->setShortcuts(QApplication::isRightToLeft() ? nextTabKeys : prevTabKeys);
1649
1650 // for context menu
1651 QAction* openInNewTab = actionCollection()->addAction("open_in_new_tab");
1652 openInNewTab->setText(i18nc("@action:inmenu", "Open in New Tab"));
1653 openInNewTab->setIcon(QIcon::fromTheme("tab-new"));
1654 connect(openInNewTab, &QAction::triggered, this, &DolphinMainWindow::openInNewTab);
1655
1656 QAction* openInNewTabs = actionCollection()->addAction("open_in_new_tabs");
1657 openInNewTabs->setText(i18nc("@action:inmenu", "Open in New Tabs"));
1658 openInNewTabs->setIcon(QIcon::fromTheme("tab-new"));
1659 connect(openInNewTabs, &QAction::triggered, this, &DolphinMainWindow::openInNewTab);
1660
1661 QAction* openInNewWindow = actionCollection()->addAction("open_in_new_window");
1662 openInNewWindow->setText(i18nc("@action:inmenu", "Open in New Window"));
1663 openInNewWindow->setIcon(QIcon::fromTheme("window-new"));
1664 connect(openInNewWindow, &QAction::triggered, this, &DolphinMainWindow::openInNewWindow);
1665 }
1666
1667 void DolphinMainWindow::setupDockWidgets()
1668 {
1669 const bool lock = GeneralSettings::lockPanels();
1670
1671 KDualAction* lockLayoutAction = actionCollection()->add<KDualAction>("lock_panels");
1672 lockLayoutAction->setActiveText(i18nc("@action:inmenu Panels", "Unlock Panels"));
1673 lockLayoutAction->setActiveIcon(QIcon::fromTheme("object-unlocked"));
1674 lockLayoutAction->setInactiveText(i18nc("@action:inmenu Panels", "Lock Panels"));
1675 lockLayoutAction->setInactiveIcon(QIcon::fromTheme("object-locked"));
1676 lockLayoutAction->setActive(lock);
1677 connect(lockLayoutAction, &KDualAction::triggered, this, &DolphinMainWindow::togglePanelLockState);
1678
1679 // Setup "Information"
1680 DolphinDockWidget* infoDock = new DolphinDockWidget(i18nc("@title:window", "Information"));
1681 infoDock->setLocked(lock);
1682 infoDock->setObjectName("infoDock");
1683 infoDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
1684 InformationPanel* infoPanel = new InformationPanel(infoDock);
1685 infoPanel->setCustomContextMenuActions(QList<QAction*>() << lockLayoutAction);
1686 connect(infoPanel, &InformationPanel::urlActivated, this, &DolphinMainWindow::handleUrl);
1687 infoDock->setWidget(infoPanel);
1688
1689 QAction* infoAction = infoDock->toggleViewAction();
1690 createPanelAction(QIcon::fromTheme("dialog-information"), Qt::Key_F11, infoAction, "show_information_panel");
1691
1692 addDockWidget(Qt::RightDockWidgetArea, infoDock);
1693 connect(this, &DolphinMainWindow::urlChanged,
1694 infoPanel, &InformationPanel::setUrl);
1695 connect(this, &DolphinMainWindow::selectionChanged,
1696 infoPanel, &InformationPanel::setSelection);
1697 connect(this, &DolphinMainWindow::requestItemInfo,
1698 infoPanel, &InformationPanel::requestDelayedItemInfo);
1699
1700 // Setup "Folders"
1701 DolphinDockWidget* foldersDock = new DolphinDockWidget(i18nc("@title:window", "Folders"));
1702 foldersDock->setLocked(lock);
1703 foldersDock->setObjectName("foldersDock");
1704 foldersDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
1705 FoldersPanel* foldersPanel = new FoldersPanel(foldersDock);
1706 foldersPanel->setCustomContextMenuActions(QList<QAction*>() << lockLayoutAction);
1707 foldersDock->setWidget(foldersPanel);
1708
1709 QAction* foldersAction = foldersDock->toggleViewAction();
1710 createPanelAction(QIcon::fromTheme("folder"), Qt::Key_F7, foldersAction, "show_folders_panel");
1711
1712 addDockWidget(Qt::LeftDockWidgetArea, foldersDock);
1713 connect(this, &DolphinMainWindow::urlChanged,
1714 foldersPanel, &FoldersPanel::setUrl);
1715 connect(foldersPanel, &FoldersPanel::folderActivated,
1716 this, &DolphinMainWindow::changeUrl);
1717 connect(foldersPanel, &FoldersPanel::folderMiddleClicked,
1718 this, static_cast<void(DolphinMainWindow::*)(const KUrl&)>(&DolphinMainWindow::openNewTab));
1719 connect(foldersPanel, &FoldersPanel::errorMessage,
1720 this, &DolphinMainWindow::slotPanelErrorMessage);
1721
1722 // Setup "Terminal"
1723 #ifndef Q_OS_WIN
1724 DolphinDockWidget* terminalDock = new DolphinDockWidget(i18nc("@title:window Shell terminal", "Terminal"));
1725 terminalDock->setLocked(lock);
1726 terminalDock->setObjectName("terminalDock");
1727 terminalDock->setAllowedAreas(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea);
1728 TerminalPanel* terminalPanel = new TerminalPanel(terminalDock);
1729 terminalPanel->setCustomContextMenuActions(QList<QAction*>() << lockLayoutAction);
1730 terminalDock->setWidget(terminalPanel);
1731
1732 connect(terminalPanel, &TerminalPanel::hideTerminalPanel, terminalDock, &DolphinDockWidget::hide);
1733 connect(terminalPanel, &TerminalPanel::changeUrl, this, &DolphinMainWindow::slotTerminalDirectoryChanged);
1734 connect(terminalDock, &DolphinDockWidget::visibilityChanged,
1735 terminalPanel, &TerminalPanel::dockVisibilityChanged);
1736
1737 QAction* terminalAction = terminalDock->toggleViewAction();
1738 createPanelAction(QIcon::fromTheme("utilities-terminal"), Qt::Key_F4, terminalAction, "show_terminal_panel");
1739
1740 addDockWidget(Qt::BottomDockWidgetArea, terminalDock);
1741 connect(this, &DolphinMainWindow::urlChanged,
1742 terminalPanel, &TerminalPanel::setUrl);
1743 #endif
1744
1745 if (GeneralSettings::version() < 200) {
1746 infoDock->hide();
1747 foldersDock->hide();
1748 #ifndef Q_OS_WIN
1749 terminalDock->hide();
1750 #endif
1751 }
1752
1753 // Setup "Places"
1754 DolphinDockWidget* placesDock = new DolphinDockWidget(i18nc("@title:window", "Places"));
1755 placesDock->setLocked(lock);
1756 placesDock->setObjectName("placesDock");
1757 placesDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
1758
1759 PlacesPanel* placesPanel = new PlacesPanel(placesDock);
1760 placesPanel->setCustomContextMenuActions(QList<QAction*>() << lockLayoutAction);
1761 placesDock->setWidget(placesPanel);
1762
1763 QAction* placesAction = placesDock->toggleViewAction();
1764 createPanelAction(QIcon::fromTheme("bookmarks"), Qt::Key_F9, placesAction, "show_places_panel");
1765
1766 addDockWidget(Qt::LeftDockWidgetArea, placesDock);
1767 connect(placesPanel, &PlacesPanel::placeActivated,
1768 this, &DolphinMainWindow::slotPlaceActivated);
1769 connect(placesPanel, &PlacesPanel::placeMiddleClicked,
1770 this, static_cast<void(DolphinMainWindow::*)(const KUrl&)>(&DolphinMainWindow::openNewTab));
1771 connect(placesPanel, &PlacesPanel::errorMessage,
1772 this, &DolphinMainWindow::slotPanelErrorMessage);
1773 connect(this, &DolphinMainWindow::urlChanged,
1774 placesPanel, &PlacesPanel::setUrl);
1775 connect(placesDock, &DolphinDockWidget::visibilityChanged,
1776 this, &DolphinMainWindow::slotPlacesPanelVisibilityChanged);
1777 connect(this, &DolphinMainWindow::settingsChanged,
1778 placesPanel, &PlacesPanel::readSettings);
1779
1780 // Add actions into the "Panels" menu
1781 KActionMenu* panelsMenu = new KActionMenu(i18nc("@action:inmenu View", "Panels"), this);
1782 actionCollection()->addAction("panels", panelsMenu);
1783 panelsMenu->setDelayed(false);
1784 const KActionCollection* ac = actionCollection();
1785 panelsMenu->addAction(ac->action("show_places_panel"));
1786 panelsMenu->addAction(ac->action("show_information_panel"));
1787 panelsMenu->addAction(ac->action("show_folders_panel"));
1788 #ifndef Q_OS_WIN
1789 panelsMenu->addAction(ac->action("show_terminal_panel"));
1790 #endif
1791 panelsMenu->addSeparator();
1792 panelsMenu->addAction(lockLayoutAction);
1793 }
1794
1795 void DolphinMainWindow::updateEditActions()
1796 {
1797 const KFileItemList list = m_activeViewContainer->view()->selectedItems();
1798 if (list.isEmpty()) {
1799 stateChanged("has_no_selection");
1800 } else {
1801 stateChanged("has_selection");
1802
1803 KActionCollection* col = actionCollection();
1804 QAction* renameAction = col->action("rename");
1805 QAction* moveToTrashAction = col->action("move_to_trash");
1806 QAction* deleteAction = col->action("delete");
1807 QAction* cutAction = col->action(KStandardAction::name(KStandardAction::Cut));
1808 QAction* deleteWithTrashShortcut = col->action("delete_shortcut"); // see DolphinViewActionHandler
1809
1810 KFileItemListProperties capabilities(list);
1811 const bool enableMoveToTrash = capabilities.isLocal() && capabilities.supportsMoving();
1812
1813 renameAction->setEnabled(capabilities.supportsMoving());
1814 moveToTrashAction->setEnabled(enableMoveToTrash);
1815 deleteAction->setEnabled(capabilities.supportsDeleting());
1816 deleteWithTrashShortcut->setEnabled(capabilities.supportsDeleting() && !enableMoveToTrash);
1817 cutAction->setEnabled(capabilities.supportsMoving());
1818 }
1819 updatePasteAction();
1820 }
1821
1822 void DolphinMainWindow::updateViewActions()
1823 {
1824 m_actionHandler->updateViewActions();
1825
1826 QAction* showFilterBarAction = actionCollection()->action("show_filter_bar");
1827 showFilterBarAction->setChecked(m_activeViewContainer->isFilterBarVisible());
1828
1829 updateSplitAction();
1830
1831 QAction* editableLocactionAction = actionCollection()->action("editable_location");
1832 const KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator();
1833 editableLocactionAction->setChecked(urlNavigator->isUrlEditable());
1834 }
1835
1836 void DolphinMainWindow::updateGoActions()
1837 {
1838 QAction* goUpAction = actionCollection()->action(KStandardAction::name(KStandardAction::Up));
1839 const KUrl currentUrl = m_activeViewContainer->url();
1840 goUpAction->setEnabled(currentUrl.upUrl() != currentUrl);
1841 }
1842
1843 void DolphinMainWindow::createControlButton()
1844 {
1845 if (m_controlButton) {
1846 return;
1847 }
1848 Q_ASSERT(!m_controlButton);
1849
1850 m_controlButton = new QToolButton(this);
1851 m_controlButton->setIcon(QIcon::fromTheme("applications-system"));
1852 m_controlButton->setText(i18nc("@action", "Control"));
1853 m_controlButton->setPopupMode(QToolButton::InstantPopup);
1854 m_controlButton->setToolButtonStyle(toolBar()->toolButtonStyle());
1855
1856 KMenu* controlMenu = new KMenu(m_controlButton);
1857 connect(controlMenu, &KMenu::aboutToShow, this, &DolphinMainWindow::updateControlMenu);
1858
1859 m_controlButton->setMenu(controlMenu);
1860
1861 toolBar()->addWidget(m_controlButton);
1862 connect(toolBar(), &KToolBar::iconSizeChanged,
1863 m_controlButton, &QToolButton::setIconSize);
1864 connect(toolBar(), &KToolBar::toolButtonStyleChanged,
1865 m_controlButton, &QToolButton::setToolButtonStyle);
1866
1867 // The added widgets are owned by the toolbar and may get deleted when e.g. the toolbar
1868 // gets edited. In this case we must add them again. The adding is done asynchronously by
1869 // m_updateToolBarTimer.
1870 connect(m_controlButton, &QToolButton::destroyed, this, &DolphinMainWindow::slotControlButtonDeleted);
1871 m_updateToolBarTimer = new QTimer(this);
1872 m_updateToolBarTimer->setInterval(500);
1873 connect(m_updateToolBarTimer, &QTimer::timeout, this, &DolphinMainWindow::updateToolBar);
1874 }
1875
1876 void DolphinMainWindow::deleteControlButton()
1877 {
1878 delete m_controlButton;
1879 m_controlButton = 0;
1880
1881 delete m_updateToolBarTimer;
1882 m_updateToolBarTimer = 0;
1883 }
1884
1885 bool DolphinMainWindow::addActionToMenu(QAction* action, KMenu* menu)
1886 {
1887 Q_ASSERT(action);
1888 Q_ASSERT(menu);
1889
1890 const KToolBar* toolBarWidget = toolBar();
1891 foreach (const QWidget* widget, action->associatedWidgets()) {
1892 if (widget == toolBarWidget) {
1893 return false;
1894 }
1895 }
1896
1897 menu->addAction(action);
1898 return true;
1899 }
1900
1901 void DolphinMainWindow::rememberClosedTab(int index)
1902 {
1903 QMenu* tabsMenu = m_recentTabsMenu->menu();
1904
1905 const QString primaryPath = m_viewTab[index].primaryView->url().path();
1906 const QString iconName = KIO::iconNameForUrl(primaryPath);
1907
1908 QAction* action = new QAction(squeezedText(primaryPath), tabsMenu);
1909
1910 ClosedTab closedTab;
1911 closedTab.primaryUrl = m_viewTab[index].primaryView->url();
1912
1913 if (m_viewTab[index].secondaryView) {
1914 closedTab.secondaryUrl = m_viewTab[index].secondaryView->url();
1915 closedTab.isSplit = true;
1916 } else {
1917 closedTab.isSplit = false;
1918 }
1919
1920 action->setData(QVariant::fromValue(closedTab));
1921 action->setIcon(QIcon::fromTheme(iconName));
1922
1923 // add the closed tab menu entry after the separator and
1924 // "Empty Recently Closed Tabs" entry
1925 if (tabsMenu->actions().size() == 2) {
1926 tabsMenu->addAction(action);
1927 } else {
1928 tabsMenu->insertAction(tabsMenu->actions().at(2), action);
1929 }
1930
1931 // assure that only up to 8 closed tabs are shown in the menu
1932 if (tabsMenu->actions().size() > 8) {
1933 tabsMenu->removeAction(tabsMenu->actions().last());
1934 }
1935 actionCollection()->action("closed_tabs")->setEnabled(true);
1936 KAcceleratorManager::manage(tabsMenu);
1937 }
1938
1939 void DolphinMainWindow::refreshViews()
1940 {
1941 Q_ASSERT(m_viewTab[m_tabIndex].primaryView);
1942
1943 // remember the current active view, as because of
1944 // the refreshing the active view might change to
1945 // the secondary view
1946 DolphinViewContainer* activeViewContainer = m_activeViewContainer;
1947
1948 const int tabCount = m_viewTab.count();
1949 for (int i = 0; i < tabCount; ++i) {
1950 m_viewTab[i].primaryView->readSettings();
1951 if (m_viewTab[i].secondaryView) {
1952 m_viewTab[i].secondaryView->readSettings();
1953 }
1954 }
1955
1956 setActiveViewContainer(activeViewContainer);
1957
1958 if (GeneralSettings::modifiedStartupSettings()) {
1959 // The startup settings have been changed by the user (see bug #254947).
1960 // Synchronize the split-view setting with the active view:
1961 const bool splitView = GeneralSettings::splitView();
1962 const ViewTab& activeTab = m_viewTab[m_tabIndex];
1963 const bool toggle = ( splitView && !activeTab.secondaryView)
1964 || (!splitView && activeTab.secondaryView);
1965 if (toggle) {
1966 toggleSplitView();
1967 }
1968 }
1969
1970 emit settingsChanged();
1971 }
1972
1973 void DolphinMainWindow::clearStatusBar()
1974 {
1975 m_activeViewContainer->statusBar()->resetToDefaultText();
1976 }
1977
1978 void DolphinMainWindow::connectViewSignals(DolphinViewContainer* container)
1979 {
1980 connect(container, &DolphinViewContainer::showFilterBarChanged,
1981 this, &DolphinMainWindow::updateFilterBarAction);
1982 connect(container, &DolphinViewContainer::writeStateChanged,
1983 this, &DolphinMainWindow::slotWriteStateChanged);
1984
1985 DolphinView* view = container->view();
1986 connect(view, &DolphinView::selectionChanged,
1987 this, &DolphinMainWindow::slotSelectionChanged);
1988 connect(view, &DolphinView::requestItemInfo,
1989 this, &DolphinMainWindow::slotRequestItemInfo);
1990 connect(view, &DolphinView::activated,
1991 this, &DolphinMainWindow::toggleActiveView);
1992 connect(view, &DolphinView::tabRequested,
1993 this, static_cast<void(DolphinMainWindow::*)(const KUrl&)>(&DolphinMainWindow::openNewTab));
1994 connect(view, &DolphinView::requestContextMenu,
1995 this, &DolphinMainWindow::openContextMenu);
1996 connect(view, &DolphinView::directoryLoadingStarted,
1997 this, &DolphinMainWindow::enableStopAction);
1998 connect(view, &DolphinView::directoryLoadingCompleted,
1999 this, &DolphinMainWindow::disableStopAction);
2000 connect(view, &DolphinView::goBackRequested,
2001 this, static_cast<void(DolphinMainWindow::*)()>(&DolphinMainWindow::goBack));
2002 connect(view, &DolphinView::goForwardRequested,
2003 this, static_cast<void(DolphinMainWindow::*)()>(&DolphinMainWindow::goForward));
2004
2005 const KUrlNavigator* navigator = container->urlNavigator();
2006 connect(navigator, &KUrlNavigator::urlChanged,
2007 this, &DolphinMainWindow::changeUrl);
2008 connect(navigator, &KUrlNavigator::historyChanged,
2009 this, &DolphinMainWindow::updateHistory);
2010 connect(navigator, &KUrlNavigator::editableStateChanged,
2011 this, &DolphinMainWindow::slotEditableStateChanged);
2012 connect(navigator, &KUrlNavigator::tabRequested,
2013 this, static_cast<void(DolphinMainWindow::*)(const KUrl&)>(&DolphinMainWindow::openNewTab));
2014 }
2015
2016 void DolphinMainWindow::updateSplitAction()
2017 {
2018 QAction* splitAction = actionCollection()->action("split_view");
2019 if (m_viewTab[m_tabIndex].secondaryView) {
2020 if (m_activeViewContainer == m_viewTab[m_tabIndex].secondaryView) {
2021 splitAction->setText(i18nc("@action:intoolbar Close right view", "Close"));
2022 splitAction->setToolTip(i18nc("@info", "Close right view"));
2023 splitAction->setIcon(QIcon::fromTheme("view-right-close"));
2024 } else {
2025 splitAction->setText(i18nc("@action:intoolbar Close left view", "Close"));
2026 splitAction->setToolTip(i18nc("@info", "Close left view"));
2027 splitAction->setIcon(QIcon::fromTheme("view-left-close"));
2028 }
2029 } else {
2030 splitAction->setText(i18nc("@action:intoolbar Split view", "Split"));
2031 splitAction->setToolTip(i18nc("@info", "Split view"));
2032 splitAction->setIcon(QIcon::fromTheme("view-right-new"));
2033 }
2034 }
2035
2036 QString DolphinMainWindow::tabName(const KUrl& url) const
2037 {
2038 QString name;
2039 if (url.equals(KUrl("file:///"))) {
2040 name = '/';
2041 } else {
2042 name = url.fileName();
2043 if (name.isEmpty()) {
2044 name = url.protocol();
2045 } else {
2046 // Make sure that a '&' inside the directory name is displayed correctly
2047 // and not misinterpreted as a keyboard shortcut in QTabBar::setTabText()
2048 name.replace('&', "&&");
2049 }
2050 }
2051 return name;
2052 }
2053
2054 bool DolphinMainWindow::isKompareInstalled() const
2055 {
2056 static bool initialized = false;
2057 static bool installed = false;
2058 if (!initialized) {
2059 // TODO: maybe replace this approach later by using a menu
2060 // plugin like kdiff3plugin.cpp
2061 installed = !KGlobal::dirs()->findExe("kompare").isEmpty();
2062 initialized = true;
2063 }
2064 return installed;
2065 }
2066
2067 void DolphinMainWindow::createSecondaryView(int tabIndex)
2068 {
2069 ViewTab& viewTab = m_viewTab[tabIndex];
2070
2071 QSplitter* splitter = viewTab.splitter;
2072 const int newWidth = (viewTab.primaryView->width() - splitter->handleWidth()) / 2;
2073
2074 const DolphinView* view = viewTab.primaryView->view();
2075 // The final parent of the new view container will be set by adding it
2076 // to the splitter. However, we must make sure that the DolphinMainWindow
2077 // is a parent of the view container already when it is constructed
2078 // because this enables the container's KFileItemModel to assign its
2079 // dir lister to the right main window. The dir lister can then cache
2080 // authentication data.
2081 viewTab.secondaryView = createViewContainer(view->url(), this);
2082 splitter->addWidget(viewTab.secondaryView);
2083 splitter->setSizes(QList<int>() << newWidth << newWidth);
2084
2085 connectViewSignals(viewTab.secondaryView);
2086 viewTab.secondaryView->setActive(false);
2087 viewTab.secondaryView->resize(newWidth, viewTab.primaryView->height());
2088 viewTab.secondaryView->show();
2089 }
2090
2091 QString DolphinMainWindow::tabProperty(const QString& property, int tabIndex) const
2092 {
2093 return "Tab " + QString::number(tabIndex) + ' ' + property;
2094 }
2095
2096 void DolphinMainWindow::setUrlAsCaption(const KUrl& url)
2097 {
2098 QString caption;
2099 if (!url.isLocalFile()) {
2100 caption.append(url.protocol() + " - ");
2101 if (url.hasHost()) {
2102 caption.append(url.host() + " - ");
2103 }
2104 }
2105
2106 const QString fileName = url.fileName().isEmpty() ? "/" : url.fileName();
2107 caption.append(fileName);
2108
2109 setCaption(caption);
2110 }
2111
2112 QString DolphinMainWindow::squeezedText(const QString& text) const
2113 {
2114 const QFontMetrics fm = fontMetrics();
2115 return fm.elidedText(text, Qt::ElideMiddle, fm.maxWidth() * 10);
2116 }
2117
2118 void DolphinMainWindow::createPanelAction(const QIcon& icon,
2119 const QKeySequence& shortcut,
2120 QAction* dockAction,
2121 const QString& actionName)
2122 {
2123 QAction* panelAction = actionCollection()->addAction(actionName);
2124 panelAction->setCheckable(true);
2125 panelAction->setChecked(dockAction->isChecked());
2126 panelAction->setText(dockAction->text());
2127 panelAction->setIcon(icon);
2128 panelAction->setShortcut(shortcut);
2129
2130 connect(panelAction, &QAction::triggered, dockAction, &QAction::trigger);
2131 connect(dockAction, &QAction::toggled, panelAction, &QAction::setChecked);
2132 }
2133
2134 DolphinMainWindow::UndoUiInterface::UndoUiInterface() :
2135 KIO::FileUndoManager::UiInterface()
2136 {
2137 }
2138
2139 DolphinMainWindow::UndoUiInterface::~UndoUiInterface()
2140 {
2141 }
2142
2143 void DolphinMainWindow::UndoUiInterface::jobError(KIO::Job* job)
2144 {
2145 DolphinMainWindow* mainWin= qobject_cast<DolphinMainWindow *>(parentWidget());
2146 if (mainWin) {
2147 DolphinViewContainer* container = mainWin->activeViewContainer();
2148 container->showMessage(job->errorString(), DolphinViewContainer::Error);
2149 } else {
2150 KIO::FileUndoManager::UiInterface::jobError(job);
2151 }
2152 }
2153
2154 #include "dolphinmainwindow.moc"