]> cloud.milkyroute.net Git - dolphin.git/blob - src/dolphinmainwindow.cpp
Merge remote-tracking branch 'origin/KDE/4.13'
[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 <KLineEdit>
57 #include <KToolBar>
58 #include <KIcon>
59 #include <KIconLoader>
60 #include <KIO/NetAccess>
61 #include <KIO/JobUiDelegate>
62 #include <KInputDialog>
63 #include <KLocale>
64 #include <KProtocolManager>
65 #include <KMenu>
66 #include <KMenuBar>
67 #include <KMessageBox>
68 #include <KFileItemListProperties>
69 #include <konqmimedata.h>
70 #include <KProtocolInfo>
71 #include <KRun>
72 #include <KShell>
73 #include <KStandardDirs>
74 #include <kstatusbar.h>
75 #include <KStandardAction>
76 #include <ktabbar.h>
77 #include <KToggleAction>
78 #include <KUrlNavigator>
79 #include <KUrl>
80 #include <KUrlComboBox>
81 #include <KToolInvocation>
82
83 #include <QDesktopWidget>
84 #include <QDBusMessage>
85 #include <QKeyEvent>
86 #include <QClipboard>
87 #include <QToolButton>
88 #include <QSplitter>
89
90 namespace {
91 // Used for GeneralSettings::version() to determine whether
92 // an updated version of Dolphin is running.
93 const int CurrentDolphinVersion = 200;
94 };
95
96 /*
97 * Remembers the tab configuration if a tab has been closed.
98 * Each closed tab can be restored by the menu
99 * "Go -> Recently Closed Tabs".
100 */
101 struct ClosedTab
102 {
103 KUrl primaryUrl;
104 KUrl secondaryUrl;
105 bool isSplit;
106 };
107 Q_DECLARE_METATYPE(ClosedTab)
108
109 DolphinMainWindow::DolphinMainWindow() :
110 KXmlGuiWindow(0),
111 m_newFileMenu(0),
112 m_tabBar(0),
113 m_activeViewContainer(0),
114 m_centralWidgetLayout(0),
115 m_tabIndex(0),
116 m_viewTab(),
117 m_actionHandler(0),
118 m_remoteEncoding(0),
119 m_settingsDialog(),
120 m_controlButton(0),
121 m_updateToolBarTimer(0),
122 m_lastHandleUrlStatJob(0)
123 {
124 setObjectName("Dolphin#");
125
126 m_viewTab.append(ViewTab());
127 ViewTab& viewTab = m_viewTab[m_tabIndex];
128 viewTab.wasActive = true; // The first opened tab is automatically active
129
130 connect(&DolphinNewFileMenuObserver::instance(), SIGNAL(errorMessage(QString)),
131 this, SLOT(showErrorMessage(QString)));
132
133 KIO::FileUndoManager* undoManager = KIO::FileUndoManager::self();
134 undoManager->setUiInterface(new UndoUiInterface());
135
136 connect(undoManager, SIGNAL(undoAvailable(bool)),
137 this, SLOT(slotUndoAvailable(bool)));
138 connect(undoManager, SIGNAL(undoTextChanged(QString)),
139 this, SLOT(slotUndoTextChanged(QString)));
140 connect(undoManager, SIGNAL(jobRecordingStarted(CommandType)),
141 this, SLOT(clearStatusBar()));
142 connect(undoManager, SIGNAL(jobRecordingFinished(CommandType)),
143 this, SLOT(showCommand(CommandType)));
144
145 GeneralSettings* generalSettings = GeneralSettings::self();
146 const bool firstRun = (generalSettings->version() < 200);
147 if (firstRun) {
148 generalSettings->setViewPropsTimestamp(QDateTime::currentDateTime());
149 }
150
151 setAcceptDrops(true);
152
153 viewTab.splitter = new QSplitter(this);
154 viewTab.splitter->setChildrenCollapsible(false);
155
156 setupActions();
157
158 const KUrl homeUrl(generalSettings->homeUrl());
159 setUrlAsCaption(homeUrl);
160 m_actionHandler = new DolphinViewActionHandler(actionCollection(), this);
161 connect(m_actionHandler, SIGNAL(actionBeingHandled()), SLOT(clearStatusBar()));
162 connect(m_actionHandler, SIGNAL(createDirectory()), SLOT(createDirectory()));
163
164 viewTab.primaryView = createViewContainer(homeUrl, viewTab.splitter);
165
166 m_activeViewContainer = viewTab.primaryView;
167 connectViewSignals(m_activeViewContainer);
168 DolphinView* view = m_activeViewContainer->view();
169 m_activeViewContainer->show();
170 m_actionHandler->setCurrentView(view);
171
172 m_remoteEncoding = new DolphinRemoteEncoding(this, m_actionHandler);
173 connect(this, SIGNAL(urlChanged(KUrl)),
174 m_remoteEncoding, SLOT(slotAboutToOpenUrl()));
175
176 m_tabBar = new KTabBar(this);
177 m_tabBar->setMovable(true);
178 m_tabBar->setTabsClosable(true);
179 connect(m_tabBar, SIGNAL(currentChanged(int)),
180 this, SLOT(setActiveTab(int)));
181 connect(m_tabBar, SIGNAL(tabCloseRequested(int)),
182 this, SLOT(closeTab(int)));
183 connect(m_tabBar, SIGNAL(contextMenu(int,QPoint)),
184 this, SLOT(openTabContextMenu(int,QPoint)));
185 connect(m_tabBar, SIGNAL(newTabRequest()),
186 this, SLOT(openNewTab()));
187 connect(m_tabBar, SIGNAL(testCanDecode(const QDragMoveEvent*,bool&)),
188 this, SLOT(slotTestCanDecode(const QDragMoveEvent*,bool&)));
189 connect(m_tabBar, SIGNAL(mouseMiddleClick(int)),
190 this, SLOT(closeTab(int)));
191 connect(m_tabBar, SIGNAL(tabMoved(int,int)),
192 this, SLOT(slotTabMoved(int,int)));
193 connect(m_tabBar, SIGNAL(receivedDropEvent(int,QDropEvent*)),
194 this, SLOT(tabDropEvent(int,QDropEvent*)));
195
196 m_tabBar->blockSignals(true); // signals get unblocked after at least 2 tabs are open
197
198 QWidget* centralWidget = new QWidget(this);
199 m_centralWidgetLayout = new QVBoxLayout(centralWidget);
200 m_centralWidgetLayout->setSpacing(0);
201 m_centralWidgetLayout->setMargin(0);
202 m_centralWidgetLayout->addWidget(m_tabBar);
203 m_centralWidgetLayout->addWidget(viewTab.splitter, 1);
204
205 setCentralWidget(centralWidget);
206 setupDockWidgets();
207 emit urlChanged(homeUrl);
208
209 setupGUI(Keys | Save | Create | ToolBar);
210 stateChanged("new_file");
211
212 QClipboard* clipboard = QApplication::clipboard();
213 connect(clipboard, SIGNAL(dataChanged()),
214 this, SLOT(updatePasteAction()));
215
216 if (generalSettings->splitView()) {
217 toggleSplitView();
218 }
219 updateEditActions();
220 updatePasteAction();
221 updateViewActions();
222 updateGoActions();
223
224 QAction* showFilterBarAction = actionCollection()->action("show_filter_bar");
225 showFilterBarAction->setChecked(generalSettings->filterBar());
226
227 if (firstRun) {
228 menuBar()->setVisible(false);
229 // Assure a proper default size if Dolphin runs the first time
230 resize(750, 500);
231 }
232
233 const bool showMenu = !menuBar()->isHidden();
234 QAction* showMenuBarAction = actionCollection()->action(KStandardAction::name(KStandardAction::ShowMenubar));
235 showMenuBarAction->setChecked(showMenu); // workaround for bug #171080
236 if (!showMenu) {
237 createControlButton();
238 }
239 }
240
241 DolphinMainWindow::~DolphinMainWindow()
242 {
243 }
244
245 void DolphinMainWindow::openDirectories(const QList<KUrl>& dirs)
246 {
247 if (dirs.isEmpty()) {
248 return;
249 }
250
251 if (dirs.count() == 1) {
252 m_activeViewContainer->setUrl(dirs.first());
253 return;
254 }
255
256 const int oldOpenTabsCount = m_viewTab.count();
257
258 const bool hasSplitView = GeneralSettings::splitView();
259
260 // Open each directory inside a new tab. If the "split view" option has been enabled,
261 // always show two directories within one tab.
262 QList<KUrl>::const_iterator it = dirs.constBegin();
263 while (it != dirs.constEnd()) {
264 openNewTab(*it);
265 ++it;
266
267 if (hasSplitView && (it != dirs.constEnd())) {
268 const int tabIndex = m_viewTab.count() - 1;
269 m_viewTab[tabIndex].secondaryView->setUrl(*it);
270 ++it;
271 }
272 }
273
274 // Remove the previously opened tabs
275 for (int i = 0; i < oldOpenTabsCount; ++i) {
276 closeTab(0);
277 }
278 }
279
280 void DolphinMainWindow::openFiles(const QList<KUrl>& files)
281 {
282 if (files.isEmpty()) {
283 return;
284 }
285
286 // Get all distinct directories from 'files' and open a tab
287 // for each directory. If the "split view" option is enabled, two
288 // directories are shown inside one tab (see openDirectories()).
289 QList<KUrl> dirs;
290 foreach (const KUrl& url, files) {
291 const KUrl dir(url.directory());
292 if (!dirs.contains(dir)) {
293 dirs.append(dir);
294 }
295 }
296
297 openDirectories(dirs);
298
299 // Select the files. Although the files can be split between several
300 // tabs, there is no need to split 'files' accordingly, as
301 // the DolphinView will just ignore invalid selections.
302 const int tabCount = m_viewTab.count();
303 for (int i = 0; i < tabCount; ++i) {
304 m_viewTab[i].primaryView->view()->markUrlsAsSelected(files);
305 m_viewTab[i].primaryView->view()->markUrlAsCurrent(files.at(0));
306 if (m_viewTab[i].secondaryView) {
307 m_viewTab[i].secondaryView->view()->markUrlsAsSelected(files);
308 m_viewTab[i].secondaryView->view()->markUrlAsCurrent(files.at(0));
309 }
310 }
311 }
312
313 void DolphinMainWindow::showCommand(CommandType command)
314 {
315 DolphinStatusBar* statusBar = m_activeViewContainer->statusBar();
316 switch (command) {
317 case KIO::FileUndoManager::Copy:
318 statusBar->setText(i18nc("@info:status", "Successfully copied."));
319 break;
320 case KIO::FileUndoManager::Move:
321 statusBar->setText(i18nc("@info:status", "Successfully moved."));
322 break;
323 case KIO::FileUndoManager::Link:
324 statusBar->setText(i18nc("@info:status", "Successfully linked."));
325 break;
326 case KIO::FileUndoManager::Trash:
327 statusBar->setText(i18nc("@info:status", "Successfully moved to trash."));
328 break;
329 case KIO::FileUndoManager::Rename:
330 statusBar->setText(i18nc("@info:status", "Successfully renamed."));
331 break;
332
333 case KIO::FileUndoManager::Mkdir:
334 statusBar->setText(i18nc("@info:status", "Created folder."));
335 break;
336
337 default:
338 break;
339 }
340 }
341
342 void DolphinMainWindow::pasteIntoFolder()
343 {
344 m_activeViewContainer->view()->pasteIntoFolder();
345 }
346
347 void DolphinMainWindow::changeUrl(const KUrl& url)
348 {
349 if (!KProtocolManager::supportsListing(url)) {
350 // The URL navigator only checks for validity, not
351 // if the URL can be listed. An error message is
352 // shown due to DolphinViewContainer::restoreView().
353 return;
354 }
355
356 DolphinViewContainer* view = activeViewContainer();
357 if (view) {
358 view->setUrl(url);
359 updateEditActions();
360 updatePasteAction();
361 updateViewActions();
362 updateGoActions();
363 setUrlAsCaption(url);
364 if (m_viewTab.count() > 1) {
365 m_tabBar->setTabText(m_tabIndex, squeezedText(tabName(m_activeViewContainer->url())));
366 }
367 const QString iconName = KMimeType::iconNameForUrl(url);
368 m_tabBar->setTabIcon(m_tabIndex, KIcon(iconName));
369 emit urlChanged(url);
370 }
371 }
372
373 void DolphinMainWindow::slotTerminalDirectoryChanged(const KUrl& url)
374 {
375 m_activeViewContainer->setAutoGrabFocus(false);
376 changeUrl(url);
377 m_activeViewContainer->setAutoGrabFocus(true);
378 }
379
380 void DolphinMainWindow::slotEditableStateChanged(bool editable)
381 {
382 KToggleAction* editableLocationAction =
383 static_cast<KToggleAction*>(actionCollection()->action("editable_location"));
384 editableLocationAction->setChecked(editable);
385 }
386
387 void DolphinMainWindow::slotSelectionChanged(const KFileItemList& selection)
388 {
389 updateEditActions();
390
391 Q_ASSERT(m_viewTab[m_tabIndex].primaryView);
392 int selectedUrlsCount = m_viewTab[m_tabIndex].primaryView->view()->selectedItemsCount();
393 if (m_viewTab[m_tabIndex].secondaryView) {
394 selectedUrlsCount += m_viewTab[m_tabIndex].secondaryView->view()->selectedItemsCount();
395 }
396
397 QAction* compareFilesAction = actionCollection()->action("compare_files");
398 if (selectedUrlsCount == 2) {
399 compareFilesAction->setEnabled(isKompareInstalled());
400 } else {
401 compareFilesAction->setEnabled(false);
402 }
403
404 emit selectionChanged(selection);
405 }
406
407 void DolphinMainWindow::slotRequestItemInfo(const KFileItem& item)
408 {
409 emit requestItemInfo(item);
410 }
411
412 void DolphinMainWindow::updateHistory()
413 {
414 const KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator();
415 const int index = urlNavigator->historyIndex();
416
417 QAction* backAction = actionCollection()->action("go_back");
418 if (backAction) {
419 backAction->setToolTip(i18nc("@info", "Go back"));
420 backAction->setEnabled(index < urlNavigator->historySize() - 1);
421 }
422
423 QAction* forwardAction = actionCollection()->action("go_forward");
424 if (forwardAction) {
425 forwardAction->setToolTip(i18nc("@info", "Go forward"));
426 forwardAction->setEnabled(index > 0);
427 }
428 }
429
430 void DolphinMainWindow::updateFilterBarAction(bool show)
431 {
432 QAction* showFilterBarAction = actionCollection()->action("show_filter_bar");
433 showFilterBarAction->setChecked(show);
434 }
435
436 void DolphinMainWindow::openNewMainWindow()
437 {
438 KRun::run("dolphin %u", KUrl::List(), this);
439 }
440
441 void DolphinMainWindow::openNewTab()
442 {
443 const bool isUrlEditable = m_activeViewContainer->urlNavigator()->isUrlEditable();
444
445 openNewTab(m_activeViewContainer->url());
446 m_tabBar->setCurrentIndex(m_viewTab.count() - 1);
447
448 // The URL navigator of the new tab should have the same editable state
449 // as the current tab
450 KUrlNavigator* navigator = m_activeViewContainer->urlNavigator();
451 navigator->setUrlEditable(isUrlEditable);
452
453 if (isUrlEditable) {
454 // If a new tab is opened and the URL is editable, assure that
455 // the user can edit the URL without manually setting the focus
456 navigator->setFocus();
457 }
458 }
459
460 void DolphinMainWindow::openNewTab(const KUrl& url)
461 {
462 QWidget* focusWidget = QApplication::focusWidget();
463
464 if (m_viewTab.count() == 1) {
465 // Only one view is open currently and hence no tab is shown at
466 // all. Before creating a tab for 'url', provide a tab for the current URL.
467 const KUrl currentUrl = m_activeViewContainer->url();
468 m_tabBar->addTab(KIcon(KMimeType::iconNameForUrl(currentUrl)),
469 squeezedText(tabName(currentUrl)));
470 m_tabBar->blockSignals(false);
471 }
472
473 m_tabBar->addTab(KIcon(KMimeType::iconNameForUrl(url)),
474 squeezedText(tabName(url)));
475
476 ViewTab viewTab;
477 viewTab.splitter = new QSplitter(this);
478 viewTab.splitter->setChildrenCollapsible(false);
479 viewTab.primaryView = createViewContainer(url, viewTab.splitter);
480 viewTab.primaryView->setActive(false);
481 connectViewSignals(viewTab.primaryView);
482
483 m_viewTab.append(viewTab);
484
485 actionCollection()->action("close_tab")->setEnabled(true);
486 actionCollection()->action("activate_prev_tab")->setEnabled(true);
487 actionCollection()->action("activate_next_tab")->setEnabled(true);
488
489 // Provide a split view, if the startup settings are set this way
490 if (GeneralSettings::splitView()) {
491 const int newTabIndex = m_viewTab.count() - 1;
492 createSecondaryView(newTabIndex);
493 m_viewTab[newTabIndex].secondaryView->setActive(true);
494 m_viewTab[newTabIndex].isPrimaryViewActive = false;
495 }
496
497 if (focusWidget) {
498 // The DolphinViewContainer grabbed the keyboard focus. As the tab is opened
499 // in background, assure that the previous focused widget gets the focus back.
500 focusWidget->setFocus();
501 }
502 }
503
504 void DolphinMainWindow::openNewActivatedTab(const KUrl& url)
505 {
506 openNewTab(url);
507 m_tabBar->setCurrentIndex(m_viewTab.count() - 1);
508 }
509
510 void DolphinMainWindow::activateNextTab()
511 {
512 if (m_viewTab.count() >= 2) {
513 const int tabIndex = (m_tabBar->currentIndex() + 1) % m_tabBar->count();
514 m_tabBar->setCurrentIndex(tabIndex);
515 }
516 }
517
518 void DolphinMainWindow::activatePrevTab()
519 {
520 if (m_viewTab.count() >= 2) {
521 int tabIndex = m_tabBar->currentIndex() - 1;
522 if (tabIndex == -1) {
523 tabIndex = m_tabBar->count() - 1;
524 }
525 m_tabBar->setCurrentIndex(tabIndex);
526 }
527 }
528
529 void DolphinMainWindow::openInNewTab()
530 {
531 const KFileItemList& list = m_activeViewContainer->view()->selectedItems();
532 if (list.isEmpty()) {
533 openNewTab(m_activeViewContainer->url());
534 } else {
535 foreach (const KFileItem& item, list) {
536 const KUrl& url = DolphinView::openItemAsFolderUrl(item);
537 if (!url.isEmpty()) {
538 openNewTab(url);
539 }
540 }
541 }
542 }
543
544 void DolphinMainWindow::openInNewWindow()
545 {
546 KUrl newWindowUrl;
547
548 const KFileItemList list = m_activeViewContainer->view()->selectedItems();
549 if (list.isEmpty()) {
550 newWindowUrl = m_activeViewContainer->url();
551 } else if (list.count() == 1) {
552 const KFileItem& item = list.first();
553 newWindowUrl = DolphinView::openItemAsFolderUrl(item);
554 }
555
556 if (!newWindowUrl.isEmpty()) {
557 KRun::run("dolphin %u", KUrl::List() << newWindowUrl, this);
558 }
559 }
560
561 void DolphinMainWindow::toggleActiveView()
562 {
563 if (!m_viewTab[m_tabIndex].secondaryView) {
564 // only one view is available
565 return;
566 }
567
568 Q_ASSERT(m_activeViewContainer);
569 Q_ASSERT(m_viewTab[m_tabIndex].primaryView);
570
571 DolphinViewContainer* left = m_viewTab[m_tabIndex].primaryView;
572 DolphinViewContainer* right = m_viewTab[m_tabIndex].secondaryView;
573 setActiveViewContainer(m_activeViewContainer == right ? left : right);
574 }
575
576 void DolphinMainWindow::showEvent(QShowEvent* event)
577 {
578 KXmlGuiWindow::showEvent(event);
579 if (!event->spontaneous()) {
580 m_activeViewContainer->view()->setFocus();
581 }
582 }
583
584 void DolphinMainWindow::closeEvent(QCloseEvent* event)
585 {
586 // Find out if Dolphin is closed directly by the user or
587 // by the session manager because the session is closed
588 bool closedByUser = true;
589 DolphinApplication *application = qobject_cast<DolphinApplication*>(qApp);
590 if (application && application->sessionSaving()) {
591 closedByUser = false;
592 }
593
594 if (m_viewTab.count() > 1 && GeneralSettings::confirmClosingMultipleTabs() && closedByUser) {
595 // Ask the user if he really wants to quit and close all tabs.
596 // Open a confirmation dialog with 3 buttons:
597 // KDialog::Yes -> Quit
598 // KDialog::No -> Close only the current tab
599 // KDialog::Cancel -> do nothing
600 KDialog *dialog = new KDialog(this, Qt::Dialog);
601 dialog->setCaption(i18nc("@title:window", "Confirmation"));
602 dialog->setButtons(KDialog::Yes | KDialog::No | KDialog::Cancel);
603 dialog->setModal(true);
604 dialog->setButtonGuiItem(KDialog::Yes, KStandardGuiItem::quit());
605 dialog->setButtonGuiItem(KDialog::No, KGuiItem(i18n("C&lose Current Tab"), KIcon("tab-close")));
606 dialog->setButtonGuiItem(KDialog::Cancel, KStandardGuiItem::cancel());
607 dialog->setDefaultButton(KDialog::Yes);
608
609 bool doNotAskAgainCheckboxResult = false;
610
611 const int result = KMessageBox::createKMessageBox(dialog,
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, SIGNAL(settingsChanged()), this, SLOT(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 actionCollection()->action("activate_prev_tab")->setEnabled(false);
1163 actionCollection()->action("activate_next_tab")->setEnabled(false);
1164 } else {
1165 m_tabBar->blockSignals(false);
1166 }
1167 }
1168
1169 void DolphinMainWindow::openTabContextMenu(int index, const QPoint& pos)
1170 {
1171 KMenu menu(this);
1172
1173 QAction* newTabAction = menu.addAction(KIcon("tab-new"), i18nc("@action:inmenu", "New Tab"));
1174 newTabAction->setShortcut(actionCollection()->action("new_tab")->shortcut());
1175
1176 QAction* detachTabAction = menu.addAction(KIcon("tab-detach"), i18nc("@action:inmenu", "Detach Tab"));
1177
1178 QAction* closeOtherTabsAction = menu.addAction(KIcon("tab-close-other"), i18nc("@action:inmenu", "Close Other Tabs"));
1179
1180 QAction* closeTabAction = menu.addAction(KIcon("tab-close"), i18nc("@action:inmenu", "Close Tab"));
1181 closeTabAction->setShortcut(actionCollection()->action("close_tab")->shortcut());
1182 QAction* selectedAction = menu.exec(pos);
1183 if (selectedAction == newTabAction) {
1184 const ViewTab& tab = m_viewTab[index];
1185 Q_ASSERT(tab.primaryView);
1186 const KUrl url = tab.secondaryView && tab.secondaryView->isActive() ?
1187 tab.secondaryView->url() : tab.primaryView->url();
1188 openNewTab(url);
1189 m_tabBar->setCurrentIndex(m_viewTab.count() - 1);
1190 } else if (selectedAction == detachTabAction) {
1191 const QString separator(QLatin1Char(' '));
1192 QString command = QLatin1String("dolphin");
1193
1194 const ViewTab& tab = m_viewTab[index];
1195 Q_ASSERT(tab.primaryView);
1196
1197 command += separator + tab.primaryView->url().url();
1198 if (tab.secondaryView) {
1199 command += separator + tab.secondaryView->url().url();
1200 command += separator + QLatin1String("-split");
1201 }
1202
1203 KRun::runCommand(command, this);
1204
1205 closeTab(index);
1206 } else if (selectedAction == closeOtherTabsAction) {
1207 const int count = m_tabBar->count();
1208 for (int i = 0; i < index; ++i) {
1209 closeTab(0);
1210 }
1211 for (int i = index + 1; i < count; ++i) {
1212 closeTab(1);
1213 }
1214 } else if (selectedAction == closeTabAction) {
1215 closeTab(index);
1216 }
1217 }
1218
1219 void DolphinMainWindow::slotTabMoved(int from, int to)
1220 {
1221 m_viewTab.move(from, to);
1222 m_tabIndex = m_tabBar->currentIndex();
1223 }
1224
1225 void DolphinMainWindow::slotTestCanDecode(const QDragMoveEvent* event, bool& canDecode)
1226 {
1227 canDecode = KUrl::List::canDecode(event->mimeData());
1228 }
1229
1230 void DolphinMainWindow::handleUrl(const KUrl& url)
1231 {
1232 delete m_lastHandleUrlStatJob;
1233 m_lastHandleUrlStatJob = 0;
1234
1235 if (url.isLocalFile() && QFileInfo(url.toLocalFile()).isDir()) {
1236 activeViewContainer()->setUrl(url);
1237 } else if (KProtocolManager::supportsListing(url)) {
1238 // stat the URL to see if it is a dir or not
1239 m_lastHandleUrlStatJob = KIO::stat(url, KIO::HideProgressInfo);
1240 if (m_lastHandleUrlStatJob->ui()) {
1241 m_lastHandleUrlStatJob->ui()->setWindow(this);
1242 }
1243 connect(m_lastHandleUrlStatJob, SIGNAL(result(KJob*)),
1244 this, SLOT(slotHandleUrlStatFinished(KJob*)));
1245
1246 } else {
1247 new KRun(url, this); // Automatically deletes itself after being finished
1248 }
1249 }
1250
1251 void DolphinMainWindow::slotHandleUrlStatFinished(KJob* job)
1252 {
1253 m_lastHandleUrlStatJob = 0;
1254 const KIO::UDSEntry entry = static_cast<KIO::StatJob*>(job)->statResult();
1255 const KUrl url = static_cast<KIO::StatJob*>(job)->url();
1256 if (entry.isDir()) {
1257 activeViewContainer()->setUrl(url);
1258 } else {
1259 new KRun(url, this); // Automatically deletes itself after being finished
1260 }
1261 }
1262
1263 void DolphinMainWindow::tabDropEvent(int tab, QDropEvent* event)
1264 {
1265 const KUrl::List urls = KUrl::List::fromMimeData(event->mimeData());
1266 if (!urls.isEmpty() && tab != -1) {
1267 const ViewTab& viewTab = m_viewTab[tab];
1268 const DolphinView* view = viewTab.isPrimaryViewActive ? viewTab.primaryView->view()
1269 : viewTab.secondaryView->view();
1270 QString error;
1271 DragAndDropHelper::dropUrls(view->rootItem(), view->url(), event, error);
1272 if (!error.isEmpty()) {
1273 activeViewContainer()->showMessage(error, DolphinViewContainer::Error);
1274 }
1275 }
1276 }
1277
1278 void DolphinMainWindow::slotWriteStateChanged(bool isFolderWritable)
1279 {
1280 newFileMenu()->setEnabled(isFolderWritable);
1281 }
1282
1283 void DolphinMainWindow::openContextMenu(const QPoint& pos,
1284 const KFileItem& item,
1285 const KUrl& url,
1286 const QList<QAction*>& customActions)
1287 {
1288 QWeakPointer<DolphinContextMenu> contextMenu = new DolphinContextMenu(this, pos, item, url);
1289 contextMenu.data()->setCustomActions(customActions);
1290 const DolphinContextMenu::Command command = contextMenu.data()->open();
1291
1292 switch (command) {
1293 case DolphinContextMenu::OpenParentFolderInNewWindow: {
1294 KRun::run("dolphin %u", KUrl::List() << item.url().upUrl(), this);
1295 break;
1296 }
1297
1298 case DolphinContextMenu::OpenParentFolderInNewTab:
1299 openNewTab(item.url().upUrl());
1300 break;
1301
1302 case DolphinContextMenu::None:
1303 default:
1304 break;
1305 }
1306
1307 delete contextMenu.data();
1308 }
1309
1310 void DolphinMainWindow::updateControlMenu()
1311 {
1312 KMenu* menu = qobject_cast<KMenu*>(sender());
1313 Q_ASSERT(menu);
1314
1315 // All actions get cleared by KMenu::clear(). The sub-menus are deleted
1316 // by connecting to the aboutToHide() signal from the parent-menu.
1317 menu->clear();
1318
1319 KActionCollection* ac = actionCollection();
1320
1321 // Add "Edit" actions
1322 bool added = addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Undo)), menu) |
1323 addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Find)), menu) |
1324 addActionToMenu(ac->action("select_all"), menu) |
1325 addActionToMenu(ac->action("invert_selection"), menu);
1326
1327 if (added) {
1328 menu->addSeparator();
1329 }
1330
1331 // Add "View" actions
1332 if (!GeneralSettings::showZoomSlider()) {
1333 addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ZoomIn)), menu);
1334 addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ZoomOut)), menu);
1335 menu->addSeparator();
1336 }
1337
1338 added = addActionToMenu(ac->action("view_mode"), menu) |
1339 addActionToMenu(ac->action("sort"), menu) |
1340 addActionToMenu(ac->action("additional_info"), menu) |
1341 addActionToMenu(ac->action("show_preview"), menu) |
1342 addActionToMenu(ac->action("show_in_groups"), menu) |
1343 addActionToMenu(ac->action("show_hidden_files"), menu);
1344
1345 if (added) {
1346 menu->addSeparator();
1347 }
1348
1349 added = addActionToMenu(ac->action("split_view"), menu) |
1350 addActionToMenu(ac->action("reload"), menu) |
1351 addActionToMenu(ac->action("view_properties"), menu);
1352 if (added) {
1353 menu->addSeparator();
1354 }
1355
1356 addActionToMenu(ac->action("panels"), menu);
1357 KMenu* locationBarMenu = new KMenu(i18nc("@action:inmenu", "Location Bar"), menu);
1358 locationBarMenu->addAction(ac->action("editable_location"));
1359 locationBarMenu->addAction(ac->action("replace_location"));
1360 menu->addMenu(locationBarMenu);
1361
1362 menu->addSeparator();
1363
1364 // Add "Go" menu
1365 KMenu* goMenu = new KMenu(i18nc("@action:inmenu", "Go"), menu);
1366 connect(menu, SIGNAL(aboutToHide()), goMenu, SLOT(deleteLater()));
1367 goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Back)));
1368 goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Forward)));
1369 goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Up)));
1370 goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Home)));
1371 goMenu->addAction(ac->action("closed_tabs"));
1372 menu->addMenu(goMenu);
1373
1374 // Add "Tool" menu
1375 KMenu* toolsMenu = new KMenu(i18nc("@action:inmenu", "Tools"), menu);
1376 connect(menu, SIGNAL(aboutToHide()), toolsMenu, SLOT(deleteLater()));
1377 toolsMenu->addAction(ac->action("show_filter_bar"));
1378 toolsMenu->addAction(ac->action("compare_files"));
1379 toolsMenu->addAction(ac->action("open_terminal"));
1380 toolsMenu->addAction(ac->action("change_remote_encoding"));
1381 menu->addMenu(toolsMenu);
1382
1383 // Add "Settings" menu entries
1384 addActionToMenu(ac->action(KStandardAction::name(KStandardAction::KeyBindings)), menu);
1385 addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ConfigureToolbars)), menu);
1386 addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Preferences)), menu);
1387
1388 // Add "Help" menu
1389 KMenu* helpMenu = new KMenu(i18nc("@action:inmenu", "Help"), menu);
1390 connect(menu, SIGNAL(aboutToHide()), helpMenu, SLOT(deleteLater()));
1391 helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::HelpContents)));
1392 helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::WhatsThis)));
1393 helpMenu->addSeparator();
1394 helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::ReportBug)));
1395 helpMenu->addSeparator();
1396 helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::SwitchApplicationLanguage)));
1397 helpMenu->addSeparator();
1398 helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::AboutApp)));
1399 helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::AboutKDE)));
1400 menu->addMenu(helpMenu);
1401
1402 menu->addSeparator();
1403 addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ShowMenubar)), menu);
1404 }
1405
1406 void DolphinMainWindow::updateToolBar()
1407 {
1408 if (!menuBar()->isVisible()) {
1409 createControlButton();
1410 }
1411 }
1412
1413 void DolphinMainWindow::slotControlButtonDeleted()
1414 {
1415 m_controlButton = 0;
1416 m_updateToolBarTimer->start();
1417 }
1418
1419 void DolphinMainWindow::slotPanelErrorMessage(const QString& error)
1420 {
1421 activeViewContainer()->showMessage(error, DolphinViewContainer::Error);
1422 }
1423
1424 void DolphinMainWindow::slotPlaceActivated(const KUrl& url)
1425 {
1426 DolphinViewContainer* view = activeViewContainer();
1427
1428 if (view->url() == url) {
1429 // We can end up here if the user clicked a device in the Places Panel
1430 // which had been unmounted earlier, see https://bugs.kde.org/show_bug.cgi?id=161385.
1431 reloadView();
1432 } else {
1433 changeUrl(url);
1434 }
1435 }
1436
1437 void DolphinMainWindow::setActiveViewContainer(DolphinViewContainer* viewContainer)
1438 {
1439 Q_ASSERT(viewContainer);
1440 Q_ASSERT((viewContainer == m_viewTab[m_tabIndex].primaryView) ||
1441 (viewContainer == m_viewTab[m_tabIndex].secondaryView));
1442 if (m_activeViewContainer == viewContainer) {
1443 return;
1444 }
1445
1446 m_activeViewContainer->setActive(false);
1447 m_activeViewContainer = viewContainer;
1448
1449 // Activating the view container might trigger a recursive setActiveViewContainer() call
1450 // inside DolphinMainWindow::toggleActiveView() when having a split view. Temporary
1451 // disconnect the activated() signal in this case:
1452 disconnect(m_activeViewContainer->view(), SIGNAL(activated()), this, SLOT(toggleActiveView()));
1453 m_activeViewContainer->setActive(true);
1454 connect(m_activeViewContainer->view(), SIGNAL(activated()), this, SLOT(toggleActiveView()));
1455
1456 m_actionHandler->setCurrentView(viewContainer->view());
1457
1458 updateHistory();
1459 updateEditActions();
1460 updatePasteAction();
1461 updateViewActions();
1462 updateGoActions();
1463
1464 const KUrl url = m_activeViewContainer->url();
1465 setUrlAsCaption(url);
1466 if (m_viewTab.count() > 1) {
1467 m_tabBar->setTabText(m_tabIndex, tabName(url));
1468 m_tabBar->setTabIcon(m_tabIndex, KIcon(KMimeType::iconNameForUrl(url)));
1469 }
1470
1471 emit urlChanged(url);
1472 }
1473
1474 DolphinViewContainer* DolphinMainWindow::createViewContainer(const KUrl& url, QWidget* parent)
1475 {
1476 DolphinViewContainer* container = new DolphinViewContainer(url, parent);
1477
1478 // The places-selector from the URL navigator should only be shown
1479 // if the places dock is invisible
1480 QDockWidget* placesDock = findChild<QDockWidget*>("placesDock");
1481 container->urlNavigator()->setPlacesSelectorVisible(!placesDock || !placesDock->isVisible());
1482
1483 return container;
1484 }
1485
1486 void DolphinMainWindow::setupActions()
1487 {
1488 // setup 'File' menu
1489 m_newFileMenu = new DolphinNewFileMenu(actionCollection(), this);
1490 KMenu* menu = m_newFileMenu->menu();
1491 menu->setTitle(i18nc("@title:menu Create new folder, file, link, etc.", "Create New"));
1492 menu->setIcon(KIcon("document-new"));
1493 m_newFileMenu->setDelayed(false);
1494 connect(menu, SIGNAL(aboutToShow()),
1495 this, SLOT(updateNewMenu()));
1496
1497 KAction* newWindow = actionCollection()->addAction("new_window");
1498 newWindow->setIcon(KIcon("window-new"));
1499 newWindow->setText(i18nc("@action:inmenu File", "New &Window"));
1500 newWindow->setShortcut(Qt::CTRL | Qt::Key_N);
1501 connect(newWindow, SIGNAL(triggered()), this, SLOT(openNewMainWindow()));
1502
1503 KAction* newTab = actionCollection()->addAction("new_tab");
1504 newTab->setIcon(KIcon("tab-new"));
1505 newTab->setText(i18nc("@action:inmenu File", "New Tab"));
1506 newTab->setShortcut(KShortcut(Qt::CTRL | Qt::Key_T, Qt::CTRL | Qt::SHIFT | Qt::Key_N));
1507 connect(newTab, SIGNAL(triggered()), this, SLOT(openNewTab()));
1508
1509 KAction* closeTab = actionCollection()->addAction("close_tab");
1510 closeTab->setIcon(KIcon("tab-close"));
1511 closeTab->setText(i18nc("@action:inmenu File", "Close Tab"));
1512 closeTab->setShortcut(Qt::CTRL | Qt::Key_W);
1513 closeTab->setEnabled(false);
1514 connect(closeTab, SIGNAL(triggered()), this, SLOT(closeTab()));
1515
1516 KStandardAction::quit(this, SLOT(quit()), actionCollection());
1517
1518 // setup 'Edit' menu
1519 KStandardAction::undo(this,
1520 SLOT(undo()),
1521 actionCollection());
1522
1523 // need to remove shift+del from cut action, else the shortcut for deletejob
1524 // doesn't work
1525 KAction* cut = KStandardAction::cut(this, SLOT(cut()), actionCollection());
1526 KShortcut cutShortcut = cut->shortcut();
1527 cutShortcut.remove(Qt::SHIFT | Qt::Key_Delete, KShortcut::KeepEmpty);
1528 cut->setShortcut(cutShortcut);
1529 KStandardAction::copy(this, SLOT(copy()), actionCollection());
1530 KAction* paste = KStandardAction::paste(this, SLOT(paste()), actionCollection());
1531 // The text of the paste-action is modified dynamically by Dolphin
1532 // (e. g. to "Paste One Folder"). To prevent that the size of the toolbar changes
1533 // due to the long text, the text "Paste" is used:
1534 paste->setIconText(i18nc("@action:inmenu Edit", "Paste"));
1535
1536 KStandardAction::find(this, SLOT(find()), actionCollection());
1537
1538 KAction* selectAll = actionCollection()->addAction("select_all");
1539 selectAll->setText(i18nc("@action:inmenu Edit", "Select All"));
1540 selectAll->setShortcut(Qt::CTRL | Qt::Key_A);
1541 connect(selectAll, SIGNAL(triggered()), this, SLOT(selectAll()));
1542
1543 KAction* invertSelection = actionCollection()->addAction("invert_selection");
1544 invertSelection->setText(i18nc("@action:inmenu Edit", "Invert Selection"));
1545 invertSelection->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_A);
1546 connect(invertSelection, SIGNAL(triggered()), this, SLOT(invertSelection()));
1547
1548 // setup 'View' menu
1549 // (note that most of it is set up in DolphinViewActionHandler)
1550
1551 KAction* split = actionCollection()->addAction("split_view");
1552 split->setShortcut(Qt::Key_F3);
1553 updateSplitAction();
1554 connect(split, SIGNAL(triggered()), this, SLOT(toggleSplitView()));
1555
1556 KAction* reload = actionCollection()->addAction("reload");
1557 reload->setText(i18nc("@action:inmenu View", "Reload"));
1558 reload->setShortcut(Qt::Key_F5);
1559 reload->setIcon(KIcon("view-refresh"));
1560 connect(reload, SIGNAL(triggered()), this, SLOT(reloadView()));
1561
1562 KAction* stop = actionCollection()->addAction("stop");
1563 stop->setText(i18nc("@action:inmenu View", "Stop"));
1564 stop->setToolTip(i18nc("@info", "Stop loading"));
1565 stop->setIcon(KIcon("process-stop"));
1566 connect(stop, SIGNAL(triggered()), this, SLOT(stopLoading()));
1567
1568 KToggleAction* editableLocation = actionCollection()->add<KToggleAction>("editable_location");
1569 editableLocation->setText(i18nc("@action:inmenu Navigation Bar", "Editable Location"));
1570 editableLocation->setShortcut(Qt::Key_F6);
1571 connect(editableLocation, SIGNAL(triggered()), this, SLOT(toggleEditLocation()));
1572
1573 KAction* replaceLocation = actionCollection()->addAction("replace_location");
1574 replaceLocation->setText(i18nc("@action:inmenu Navigation Bar", "Replace Location"));
1575 replaceLocation->setShortcut(Qt::CTRL | Qt::Key_L);
1576 connect(replaceLocation, SIGNAL(triggered()), this, SLOT(replaceLocation()));
1577
1578 // setup 'Go' menu
1579 KAction* backAction = KStandardAction::back(this, SLOT(goBack()), actionCollection());
1580 connect(backAction, SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)), this, SLOT(goBack(Qt::MouseButtons)));
1581 KShortcut backShortcut = backAction->shortcut();
1582 backShortcut.setAlternate(Qt::Key_Backspace);
1583 backAction->setShortcut(backShortcut);
1584
1585 m_recentTabsMenu = new KActionMenu(i18n("Recently Closed Tabs"), this);
1586 m_recentTabsMenu->setIcon(KIcon("edit-undo"));
1587 m_recentTabsMenu->setDelayed(false);
1588 actionCollection()->addAction("closed_tabs", m_recentTabsMenu);
1589 connect(m_recentTabsMenu->menu(), SIGNAL(triggered(QAction*)),
1590 this, SLOT(restoreClosedTab(QAction*)));
1591
1592 QAction* action = new QAction(i18n("Empty Recently Closed Tabs"), m_recentTabsMenu);
1593 action->setIcon(KIcon("edit-clear-list"));
1594 action->setData(QVariant::fromValue(true));
1595 m_recentTabsMenu->addAction(action);
1596 m_recentTabsMenu->addSeparator();
1597 m_recentTabsMenu->setEnabled(false);
1598
1599 KAction* forwardAction = KStandardAction::forward(this, SLOT(goForward()), actionCollection());
1600 connect(forwardAction, SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)), this, SLOT(goForward(Qt::MouseButtons)));
1601
1602 KAction* upAction = KStandardAction::up(this, SLOT(goUp()), actionCollection());
1603 connect(upAction, SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)), this, SLOT(goUp(Qt::MouseButtons)));
1604
1605 KAction* homeAction = KStandardAction::home(this, SLOT(goHome()), actionCollection());
1606 connect(homeAction, SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)), this, SLOT(goHome(Qt::MouseButtons)));
1607
1608 // setup 'Tools' menu
1609 KAction* showFilterBar = actionCollection()->addAction("show_filter_bar");
1610 showFilterBar->setText(i18nc("@action:inmenu Tools", "Show Filter Bar"));
1611 showFilterBar->setIcon(KIcon("view-filter"));
1612 showFilterBar->setShortcut(Qt::CTRL | Qt::Key_I);
1613 connect(showFilterBar, SIGNAL(triggered()), this, SLOT(showFilterBar()));
1614
1615 KAction* compareFiles = actionCollection()->addAction("compare_files");
1616 compareFiles->setText(i18nc("@action:inmenu Tools", "Compare Files"));
1617 compareFiles->setIcon(KIcon("kompare"));
1618 compareFiles->setEnabled(false);
1619 connect(compareFiles, SIGNAL(triggered()), this, SLOT(compareFiles()));
1620
1621 KAction* openTerminal = actionCollection()->addAction("open_terminal");
1622 openTerminal->setText(i18nc("@action:inmenu Tools", "Open Terminal"));
1623 openTerminal->setIcon(KIcon("utilities-terminal"));
1624 openTerminal->setShortcut(Qt::SHIFT | Qt::Key_F4);
1625 connect(openTerminal, SIGNAL(triggered()), this, SLOT(openTerminal()));
1626
1627 // setup 'Settings' menu
1628 KToggleAction* showMenuBar = KStandardAction::showMenubar(0, 0, actionCollection());
1629 connect(showMenuBar, SIGNAL(triggered(bool)), // Fixes #286822
1630 this, SLOT(toggleShowMenuBar()), Qt::QueuedConnection);
1631 KStandardAction::preferences(this, SLOT(editSettings()), actionCollection());
1632
1633 // not in menu actions
1634 QList<QKeySequence> nextTabKeys;
1635 nextTabKeys.append(KStandardShortcut::tabNext().primary());
1636 nextTabKeys.append(QKeySequence(Qt::CTRL | Qt::Key_Tab));
1637
1638 QList<QKeySequence> prevTabKeys;
1639 prevTabKeys.append(KStandardShortcut::tabPrev().primary());
1640 prevTabKeys.append(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_Tab));
1641
1642 KAction* activateNextTab = actionCollection()->addAction("activate_next_tab");
1643 activateNextTab->setIconText(i18nc("@action:inmenu", "Next Tab"));
1644 activateNextTab->setText(i18nc("@action:inmenu", "Activate Next Tab"));
1645 activateNextTab->setEnabled(false);
1646 connect(activateNextTab, SIGNAL(triggered()), SLOT(activateNextTab()));
1647 activateNextTab->setShortcuts(QApplication::isRightToLeft() ? prevTabKeys : nextTabKeys);
1648
1649 KAction* activatePrevTab = actionCollection()->addAction("activate_prev_tab");
1650 activatePrevTab->setIconText(i18nc("@action:inmenu", "Previous Tab"));
1651 activatePrevTab->setText(i18nc("@action:inmenu", "Activate Previous Tab"));
1652 activatePrevTab->setEnabled(false);
1653 connect(activatePrevTab, SIGNAL(triggered()), SLOT(activatePrevTab()));
1654 activatePrevTab->setShortcuts(QApplication::isRightToLeft() ? nextTabKeys : prevTabKeys);
1655
1656 // for context menu
1657 KAction* openInNewTab = actionCollection()->addAction("open_in_new_tab");
1658 openInNewTab->setText(i18nc("@action:inmenu", "Open in New Tab"));
1659 openInNewTab->setIcon(KIcon("tab-new"));
1660 connect(openInNewTab, SIGNAL(triggered()), this, SLOT(openInNewTab()));
1661
1662 KAction* openInNewTabs = actionCollection()->addAction("open_in_new_tabs");
1663 openInNewTabs->setText(i18nc("@action:inmenu", "Open in New Tabs"));
1664 openInNewTabs->setIcon(KIcon("tab-new"));
1665 connect(openInNewTabs, SIGNAL(triggered()), this, SLOT(openInNewTab()));
1666
1667 KAction* openInNewWindow = actionCollection()->addAction("open_in_new_window");
1668 openInNewWindow->setText(i18nc("@action:inmenu", "Open in New Window"));
1669 openInNewWindow->setIcon(KIcon("window-new"));
1670 connect(openInNewWindow, SIGNAL(triggered()), this, SLOT(openInNewWindow()));
1671 }
1672
1673 void DolphinMainWindow::setupDockWidgets()
1674 {
1675 const bool lock = GeneralSettings::lockPanels();
1676
1677 KDualAction* lockLayoutAction = actionCollection()->add<KDualAction>("lock_panels");
1678 lockLayoutAction->setActiveText(i18nc("@action:inmenu Panels", "Unlock Panels"));
1679 lockLayoutAction->setActiveIcon(KIcon("object-unlocked"));
1680 lockLayoutAction->setInactiveText(i18nc("@action:inmenu Panels", "Lock Panels"));
1681 lockLayoutAction->setInactiveIcon(KIcon("object-locked"));
1682 lockLayoutAction->setActive(lock);
1683 connect(lockLayoutAction, SIGNAL(triggered()), this, SLOT(togglePanelLockState()));
1684
1685 // Setup "Information"
1686 DolphinDockWidget* infoDock = new DolphinDockWidget(i18nc("@title:window", "Information"));
1687 infoDock->setLocked(lock);
1688 infoDock->setObjectName("infoDock");
1689 infoDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
1690 Panel* infoPanel = new InformationPanel(infoDock);
1691 infoPanel->setCustomContextMenuActions(QList<QAction*>() << lockLayoutAction);
1692 connect(infoPanel, SIGNAL(urlActivated(KUrl)), this, SLOT(handleUrl(KUrl)));
1693 infoDock->setWidget(infoPanel);
1694
1695 QAction* infoAction = infoDock->toggleViewAction();
1696 createPanelAction(KIcon("dialog-information"), Qt::Key_F11, infoAction, "show_information_panel");
1697
1698 addDockWidget(Qt::RightDockWidgetArea, infoDock);
1699 connect(this, SIGNAL(urlChanged(KUrl)),
1700 infoPanel, SLOT(setUrl(KUrl)));
1701 connect(this, SIGNAL(selectionChanged(KFileItemList)),
1702 infoPanel, SLOT(setSelection(KFileItemList)));
1703 connect(this, SIGNAL(requestItemInfo(KFileItem)),
1704 infoPanel, SLOT(requestDelayedItemInfo(KFileItem)));
1705
1706 // Setup "Folders"
1707 DolphinDockWidget* foldersDock = new DolphinDockWidget(i18nc("@title:window", "Folders"));
1708 foldersDock->setLocked(lock);
1709 foldersDock->setObjectName("foldersDock");
1710 foldersDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
1711 FoldersPanel* foldersPanel = new FoldersPanel(foldersDock);
1712 foldersPanel->setCustomContextMenuActions(QList<QAction*>() << lockLayoutAction);
1713 foldersDock->setWidget(foldersPanel);
1714
1715 QAction* foldersAction = foldersDock->toggleViewAction();
1716 createPanelAction(KIcon("folder"), Qt::Key_F7, foldersAction, "show_folders_panel");
1717
1718 addDockWidget(Qt::LeftDockWidgetArea, foldersDock);
1719 connect(this, SIGNAL(urlChanged(KUrl)),
1720 foldersPanel, SLOT(setUrl(KUrl)));
1721 connect(foldersPanel, SIGNAL(folderActivated(KUrl)),
1722 this, SLOT(changeUrl(KUrl)));
1723 connect(foldersPanel, SIGNAL(folderMiddleClicked(KUrl)),
1724 this, SLOT(openNewTab(KUrl)));
1725 connect(foldersPanel, SIGNAL(errorMessage(QString)),
1726 this, SLOT(slotPanelErrorMessage(QString)));
1727
1728 // Setup "Terminal"
1729 #ifndef Q_OS_WIN
1730 DolphinDockWidget* terminalDock = new DolphinDockWidget(i18nc("@title:window Shell terminal", "Terminal"));
1731 terminalDock->setLocked(lock);
1732 terminalDock->setObjectName("terminalDock");
1733 terminalDock->setAllowedAreas(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea);
1734 Panel* terminalPanel = new TerminalPanel(terminalDock);
1735 terminalPanel->setCustomContextMenuActions(QList<QAction*>() << lockLayoutAction);
1736 terminalDock->setWidget(terminalPanel);
1737
1738 connect(terminalPanel, SIGNAL(hideTerminalPanel()), terminalDock, SLOT(hide()));
1739 connect(terminalPanel, SIGNAL(changeUrl(KUrl)), this, SLOT(slotTerminalDirectoryChanged(KUrl)));
1740 connect(terminalDock, SIGNAL(visibilityChanged(bool)),
1741 terminalPanel, SLOT(dockVisibilityChanged()));
1742
1743 QAction* terminalAction = terminalDock->toggleViewAction();
1744 createPanelAction(KIcon("utilities-terminal"), Qt::Key_F4, terminalAction, "show_terminal_panel");
1745
1746 addDockWidget(Qt::BottomDockWidgetArea, terminalDock);
1747 connect(this, SIGNAL(urlChanged(KUrl)),
1748 terminalPanel, SLOT(setUrl(KUrl)));
1749 #endif
1750
1751 if (GeneralSettings::version() < 200) {
1752 infoDock->hide();
1753 foldersDock->hide();
1754 #ifndef Q_OS_WIN
1755 terminalDock->hide();
1756 #endif
1757 }
1758
1759 // Setup "Places"
1760 DolphinDockWidget* placesDock = new DolphinDockWidget(i18nc("@title:window", "Places"));
1761 placesDock->setLocked(lock);
1762 placesDock->setObjectName("placesDock");
1763 placesDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
1764
1765 PlacesPanel* placesPanel = new PlacesPanel(placesDock);
1766 placesPanel->setCustomContextMenuActions(QList<QAction*>() << lockLayoutAction);
1767 placesDock->setWidget(placesPanel);
1768
1769 QAction* placesAction = placesDock->toggleViewAction();
1770 createPanelAction(KIcon("bookmarks"), Qt::Key_F9, placesAction, "show_places_panel");
1771
1772 addDockWidget(Qt::LeftDockWidgetArea, placesDock);
1773 connect(placesPanel, SIGNAL(placeActivated(KUrl)),
1774 this, SLOT(slotPlaceActivated(KUrl)));
1775 connect(placesPanel, SIGNAL(placeMiddleClicked(KUrl)),
1776 this, SLOT(openNewTab(KUrl)));
1777 connect(placesPanel, SIGNAL(errorMessage(QString)),
1778 this, SLOT(slotPanelErrorMessage(QString)));
1779 connect(this, SIGNAL(urlChanged(KUrl)),
1780 placesPanel, SLOT(setUrl(KUrl)));
1781 connect(placesDock, SIGNAL(visibilityChanged(bool)),
1782 this, SLOT(slotPlacesPanelVisibilityChanged(bool)));
1783 connect(this, SIGNAL(settingsChanged()),
1784 placesPanel, SLOT(readSettings()));
1785
1786 // Add actions into the "Panels" menu
1787 KActionMenu* panelsMenu = new KActionMenu(i18nc("@action:inmenu View", "Panels"), this);
1788 actionCollection()->addAction("panels", panelsMenu);
1789 panelsMenu->setDelayed(false);
1790 const KActionCollection* ac = actionCollection();
1791 panelsMenu->addAction(ac->action("show_places_panel"));
1792 panelsMenu->addAction(ac->action("show_information_panel"));
1793 panelsMenu->addAction(ac->action("show_folders_panel"));
1794 #ifndef Q_OS_WIN
1795 panelsMenu->addAction(ac->action("show_terminal_panel"));
1796 #endif
1797 panelsMenu->addSeparator();
1798 panelsMenu->addAction(lockLayoutAction);
1799 }
1800
1801 void DolphinMainWindow::updateEditActions()
1802 {
1803 const KFileItemList list = m_activeViewContainer->view()->selectedItems();
1804 if (list.isEmpty()) {
1805 stateChanged("has_no_selection");
1806 } else {
1807 stateChanged("has_selection");
1808
1809 KActionCollection* col = actionCollection();
1810 QAction* renameAction = col->action("rename");
1811 QAction* moveToTrashAction = col->action("move_to_trash");
1812 QAction* deleteAction = col->action("delete");
1813 QAction* cutAction = col->action(KStandardAction::name(KStandardAction::Cut));
1814 QAction* deleteWithTrashShortcut = col->action("delete_shortcut"); // see DolphinViewActionHandler
1815
1816 KFileItemListProperties capabilities(list);
1817 const bool enableMoveToTrash = capabilities.isLocal() && capabilities.supportsMoving();
1818
1819 renameAction->setEnabled(capabilities.supportsMoving());
1820 moveToTrashAction->setEnabled(enableMoveToTrash);
1821 deleteAction->setEnabled(capabilities.supportsDeleting());
1822 deleteWithTrashShortcut->setEnabled(capabilities.supportsDeleting() && !enableMoveToTrash);
1823 cutAction->setEnabled(capabilities.supportsMoving());
1824 }
1825 }
1826
1827 void DolphinMainWindow::updateViewActions()
1828 {
1829 m_actionHandler->updateViewActions();
1830
1831 QAction* showFilterBarAction = actionCollection()->action("show_filter_bar");
1832 showFilterBarAction->setChecked(m_activeViewContainer->isFilterBarVisible());
1833
1834 updateSplitAction();
1835
1836 QAction* editableLocactionAction = actionCollection()->action("editable_location");
1837 const KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator();
1838 editableLocactionAction->setChecked(urlNavigator->isUrlEditable());
1839 }
1840
1841 void DolphinMainWindow::updateGoActions()
1842 {
1843 QAction* goUpAction = actionCollection()->action(KStandardAction::name(KStandardAction::Up));
1844 const KUrl currentUrl = m_activeViewContainer->url();
1845 goUpAction->setEnabled(currentUrl.upUrl() != currentUrl);
1846 }
1847
1848 void DolphinMainWindow::createControlButton()
1849 {
1850 if (m_controlButton) {
1851 return;
1852 }
1853 Q_ASSERT(!m_controlButton);
1854
1855 m_controlButton = new QToolButton(this);
1856 m_controlButton->setIcon(KIcon("applications-system"));
1857 m_controlButton->setText(i18nc("@action", "Control"));
1858 m_controlButton->setPopupMode(QToolButton::InstantPopup);
1859 m_controlButton->setToolButtonStyle(toolBar()->toolButtonStyle());
1860
1861 KMenu* controlMenu = new KMenu(m_controlButton);
1862 connect(controlMenu, SIGNAL(aboutToShow()), this, SLOT(updateControlMenu()));
1863
1864 m_controlButton->setMenu(controlMenu);
1865
1866 toolBar()->addWidget(m_controlButton);
1867 connect(toolBar(), SIGNAL(iconSizeChanged(QSize)),
1868 m_controlButton, SLOT(setIconSize(QSize)));
1869 connect(toolBar(), SIGNAL(toolButtonStyleChanged(Qt::ToolButtonStyle)),
1870 m_controlButton, SLOT(setToolButtonStyle(Qt::ToolButtonStyle)));
1871
1872 // The added widgets are owned by the toolbar and may get deleted when e.g. the toolbar
1873 // gets edited. In this case we must add them again. The adding is done asynchronously by
1874 // m_updateToolBarTimer.
1875 connect(m_controlButton, SIGNAL(destroyed()), this, SLOT(slotControlButtonDeleted()));
1876 m_updateToolBarTimer = new QTimer(this);
1877 m_updateToolBarTimer->setInterval(500);
1878 connect(m_updateToolBarTimer, SIGNAL(timeout()), this, SLOT(updateToolBar()));
1879 }
1880
1881 void DolphinMainWindow::deleteControlButton()
1882 {
1883 delete m_controlButton;
1884 m_controlButton = 0;
1885
1886 delete m_updateToolBarTimer;
1887 m_updateToolBarTimer = 0;
1888 }
1889
1890 bool DolphinMainWindow::addActionToMenu(QAction* action, KMenu* menu)
1891 {
1892 Q_ASSERT(action);
1893 Q_ASSERT(menu);
1894
1895 const KToolBar* toolBarWidget = toolBar();
1896 foreach (const QWidget* widget, action->associatedWidgets()) {
1897 if (widget == toolBarWidget) {
1898 return false;
1899 }
1900 }
1901
1902 menu->addAction(action);
1903 return true;
1904 }
1905
1906 void DolphinMainWindow::rememberClosedTab(int index)
1907 {
1908 KMenu* tabsMenu = m_recentTabsMenu->menu();
1909
1910 const QString primaryPath = m_viewTab[index].primaryView->url().path();
1911 const QString iconName = KMimeType::iconNameForUrl(primaryPath);
1912
1913 QAction* action = new QAction(squeezedText(primaryPath), tabsMenu);
1914
1915 ClosedTab closedTab;
1916 closedTab.primaryUrl = m_viewTab[index].primaryView->url();
1917
1918 if (m_viewTab[index].secondaryView) {
1919 closedTab.secondaryUrl = m_viewTab[index].secondaryView->url();
1920 closedTab.isSplit = true;
1921 } else {
1922 closedTab.isSplit = false;
1923 }
1924
1925 action->setData(QVariant::fromValue(closedTab));
1926 action->setIcon(KIcon(iconName));
1927
1928 // add the closed tab menu entry after the separator and
1929 // "Empty Recently Closed Tabs" entry
1930 if (tabsMenu->actions().size() == 2) {
1931 tabsMenu->addAction(action);
1932 } else {
1933 tabsMenu->insertAction(tabsMenu->actions().at(2), action);
1934 }
1935
1936 // assure that only up to 8 closed tabs are shown in the menu
1937 if (tabsMenu->actions().size() > 8) {
1938 tabsMenu->removeAction(tabsMenu->actions().last());
1939 }
1940 actionCollection()->action("closed_tabs")->setEnabled(true);
1941 KAcceleratorManager::manage(tabsMenu);
1942 }
1943
1944 void DolphinMainWindow::refreshViews()
1945 {
1946 Q_ASSERT(m_viewTab[m_tabIndex].primaryView);
1947
1948 // remember the current active view, as because of
1949 // the refreshing the active view might change to
1950 // the secondary view
1951 DolphinViewContainer* activeViewContainer = m_activeViewContainer;
1952
1953 const int tabCount = m_viewTab.count();
1954 for (int i = 0; i < tabCount; ++i) {
1955 m_viewTab[i].primaryView->readSettings();
1956 if (m_viewTab[i].secondaryView) {
1957 m_viewTab[i].secondaryView->readSettings();
1958 }
1959 }
1960
1961 setActiveViewContainer(activeViewContainer);
1962
1963 if (GeneralSettings::modifiedStartupSettings()) {
1964 // The startup settings have been changed by the user (see bug #254947).
1965 // Synchronize the split-view setting with the active view:
1966 const bool splitView = GeneralSettings::splitView();
1967 const ViewTab& activeTab = m_viewTab[m_tabIndex];
1968 const bool toggle = ( splitView && !activeTab.secondaryView)
1969 || (!splitView && activeTab.secondaryView);
1970 if (toggle) {
1971 toggleSplitView();
1972 }
1973 }
1974
1975 emit settingsChanged();
1976 }
1977
1978 void DolphinMainWindow::clearStatusBar()
1979 {
1980 m_activeViewContainer->statusBar()->resetToDefaultText();
1981 }
1982
1983 void DolphinMainWindow::connectViewSignals(DolphinViewContainer* container)
1984 {
1985 connect(container, SIGNAL(showFilterBarChanged(bool)),
1986 this, SLOT(updateFilterBarAction(bool)));
1987 connect(container, SIGNAL(writeStateChanged(bool)),
1988 this, SLOT(slotWriteStateChanged(bool)));
1989
1990 DolphinView* view = container->view();
1991 connect(view, SIGNAL(selectionChanged(KFileItemList)),
1992 this, SLOT(slotSelectionChanged(KFileItemList)));
1993 connect(view, SIGNAL(requestItemInfo(KFileItem)),
1994 this, SLOT(slotRequestItemInfo(KFileItem)));
1995 connect(view, SIGNAL(activated()),
1996 this, SLOT(toggleActiveView()));
1997 connect(view, SIGNAL(tabRequested(KUrl)),
1998 this, SLOT(openNewTab(KUrl)));
1999 connect(view, SIGNAL(requestContextMenu(QPoint,KFileItem,KUrl,QList<QAction*>)),
2000 this, SLOT(openContextMenu(QPoint,KFileItem,KUrl,QList<QAction*>)));
2001 connect(view, SIGNAL(directoryLoadingStarted()),
2002 this, SLOT(enableStopAction()));
2003 connect(view, SIGNAL(directoryLoadingCompleted()),
2004 this, SLOT(disableStopAction()));
2005 connect(view, SIGNAL(goBackRequested()),
2006 this, SLOT(goBack()));
2007 connect(view, SIGNAL(goForwardRequested()),
2008 this, SLOT(goForward()));
2009
2010 const KUrlNavigator* navigator = container->urlNavigator();
2011 connect(navigator, SIGNAL(urlChanged(KUrl)),
2012 this, SLOT(changeUrl(KUrl)));
2013 connect(navigator, SIGNAL(historyChanged()),
2014 this, SLOT(updateHistory()));
2015 connect(navigator, SIGNAL(editableStateChanged(bool)),
2016 this, SLOT(slotEditableStateChanged(bool)));
2017 connect(navigator, SIGNAL(tabRequested(KUrl)),
2018 this, SLOT(openNewTab(KUrl)));
2019 }
2020
2021 void DolphinMainWindow::updateSplitAction()
2022 {
2023 QAction* splitAction = actionCollection()->action("split_view");
2024 if (m_viewTab[m_tabIndex].secondaryView) {
2025 if (m_activeViewContainer == m_viewTab[m_tabIndex].secondaryView) {
2026 splitAction->setText(i18nc("@action:intoolbar Close right view", "Close"));
2027 splitAction->setToolTip(i18nc("@info", "Close right view"));
2028 splitAction->setIcon(KIcon("view-right-close"));
2029 } else {
2030 splitAction->setText(i18nc("@action:intoolbar Close left view", "Close"));
2031 splitAction->setToolTip(i18nc("@info", "Close left view"));
2032 splitAction->setIcon(KIcon("view-left-close"));
2033 }
2034 } else {
2035 splitAction->setText(i18nc("@action:intoolbar Split view", "Split"));
2036 splitAction->setToolTip(i18nc("@info", "Split view"));
2037 splitAction->setIcon(KIcon("view-right-new"));
2038 }
2039 }
2040
2041 QString DolphinMainWindow::tabName(const KUrl& url) const
2042 {
2043 QString name;
2044 if (url.equals(KUrl("file:///"))) {
2045 name = '/';
2046 } else {
2047 name = url.fileName();
2048 if (name.isEmpty()) {
2049 name = url.protocol();
2050 } else {
2051 // Make sure that a '&' inside the directory name is displayed correctly
2052 // and not misinterpreted as a keyboard shortcut in QTabBar::setTabText()
2053 name.replace('&', "&&");
2054 }
2055 }
2056 return name;
2057 }
2058
2059 bool DolphinMainWindow::isKompareInstalled() const
2060 {
2061 static bool initialized = false;
2062 static bool installed = false;
2063 if (!initialized) {
2064 // TODO: maybe replace this approach later by using a menu
2065 // plugin like kdiff3plugin.cpp
2066 installed = !KGlobal::dirs()->findExe("kompare").isEmpty();
2067 initialized = true;
2068 }
2069 return installed;
2070 }
2071
2072 void DolphinMainWindow::createSecondaryView(int tabIndex)
2073 {
2074 ViewTab& viewTab = m_viewTab[tabIndex];
2075
2076 QSplitter* splitter = viewTab.splitter;
2077 const int newWidth = (viewTab.primaryView->width() - splitter->handleWidth()) / 2;
2078
2079 const DolphinView* view = viewTab.primaryView->view();
2080 // The final parent of the new view container will be set by adding it
2081 // to the splitter. However, we must make sure that the DolphinMainWindow
2082 // is a parent of the view container already when it is constructed
2083 // because this enables the container's KFileItemModel to assign its
2084 // dir lister to the right main window. The dir lister can then cache
2085 // authentication data.
2086 viewTab.secondaryView = createViewContainer(view->url(), this);
2087 splitter->addWidget(viewTab.secondaryView);
2088 splitter->setSizes(QList<int>() << newWidth << newWidth);
2089
2090 connectViewSignals(viewTab.secondaryView);
2091 viewTab.secondaryView->setActive(false);
2092 viewTab.secondaryView->resize(newWidth, viewTab.primaryView->height());
2093 viewTab.secondaryView->show();
2094 }
2095
2096 QString DolphinMainWindow::tabProperty(const QString& property, int tabIndex) const
2097 {
2098 return "Tab " + QString::number(tabIndex) + ' ' + property;
2099 }
2100
2101 void DolphinMainWindow::setUrlAsCaption(const KUrl& url)
2102 {
2103 QString caption;
2104 if (!url.isLocalFile()) {
2105 caption.append(url.protocol() + " - ");
2106 if (url.hasHost()) {
2107 caption.append(url.host() + " - ");
2108 }
2109 }
2110
2111 const QString fileName = url.fileName().isEmpty() ? "/" : url.fileName();
2112 caption.append(fileName);
2113
2114 setCaption(caption);
2115 }
2116
2117 QString DolphinMainWindow::squeezedText(const QString& text) const
2118 {
2119 const QFontMetrics fm = fontMetrics();
2120 return fm.elidedText(text, Qt::ElideMiddle, fm.maxWidth() * 10);
2121 }
2122
2123 void DolphinMainWindow::createPanelAction(const KIcon& icon,
2124 const QKeySequence& shortcut,
2125 QAction* dockAction,
2126 const QString& actionName)
2127 {
2128 KAction* panelAction = actionCollection()->addAction(actionName);
2129 panelAction->setCheckable(true);
2130 panelAction->setChecked(dockAction->isChecked());
2131 panelAction->setText(dockAction->text());
2132 panelAction->setIcon(icon);
2133 panelAction->setShortcut(shortcut);
2134
2135 connect(panelAction, SIGNAL(triggered()), dockAction, SLOT(trigger()));
2136 connect(dockAction, SIGNAL(toggled(bool)), panelAction, SLOT(setChecked(bool)));
2137 }
2138
2139 DolphinMainWindow::UndoUiInterface::UndoUiInterface() :
2140 KIO::FileUndoManager::UiInterface()
2141 {
2142 }
2143
2144 DolphinMainWindow::UndoUiInterface::~UndoUiInterface()
2145 {
2146 }
2147
2148 void DolphinMainWindow::UndoUiInterface::jobError(KIO::Job* job)
2149 {
2150 DolphinMainWindow* mainWin= qobject_cast<DolphinMainWindow *>(parentWidget());
2151 if (mainWin) {
2152 DolphinViewContainer* container = mainWin->activeViewContainer();
2153 container->showMessage(job->errorString(), DolphinViewContainer::Error);
2154 } else {
2155 KIO::FileUndoManager::UiInterface::jobError(job);
2156 }
2157 }
2158
2159 #include "dolphinmainwindow.moc"