#include "dolphinmainwindow.h"
+#include "admin/workerintegration.h"
#include "dolphin_generalsettings.h"
#include "dolphinbookmarkhandler.h"
#include "dolphincontextmenu.h"
#include "panels/folders/folderspanel.h"
#include "panels/places/placespanel.h"
#include "panels/terminal/terminalpanel.h"
+#include "search/dolphinquery.h"
#include "selectionmode/actiontexthelper.h"
#include "settings/dolphinsettingsdialog.h"
+#include "statusbar/diskspaceusagemenu.h"
#include "statusbar/dolphinstatusbar.h"
#include "views/dolphinnewfilemenuobserver.h"
#include "views/dolphinremoteencoding.h"
#include <KActionCollection>
#include <KActionMenu>
#include <KAuthorized>
+#include <KColorSchemeManager>
#include <KConfig>
#include <KConfigGui>
+#include <KDesktopFile>
+#include <KDialogJobUiDelegate>
#include <KDualAction>
#include <KFileItemListProperties>
#include <KIO/CommandLauncherJob>
#include <KJobWidgets>
#include <KLocalizedString>
#include <KMessageBox>
-#include <KMoreToolsMenuFactory>
#include <KProtocolInfo>
#include <KProtocolManager>
+#include <KRecentFilesAction>
+#include <KRuntimePlatform>
#include <KShell>
#include <KShortcutsDialog>
#include <KStandardAction>
#include <QStandardPaths>
#include <QTimer>
#include <QToolButton>
+#include <QtConcurrentRun>
+#include <dolphindebug.h>
#include <algorithm>
const int CurrentDolphinVersion = 202;
// The maximum number of entries in the back/forward popup menu
const int MaxNumberOfNavigationentries = 12;
-// The maximum number of "Activate Tab" shortcuts
+// The maximum number of "Go to Tab" shortcuts
const int MaxActivateTabShortcuts = 9;
}
, m_remoteEncoding(nullptr)
, m_settingsDialog()
, m_bookmarkHandler(nullptr)
+ , m_disabledActionNotifier(nullptr)
, m_lastHandleUrlOpenJob(nullptr)
, m_terminalPanel(nullptr)
, m_placesPanel(nullptr)
, m_tearDownFromPlacesRequested(false)
, m_backAction(nullptr)
, m_forwardAction(nullptr)
+ , m_splitViewAction(nullptr)
+ , m_splitViewMenuAction(nullptr)
+ , m_diskSpaceUsageMenu(nullptr)
+ , m_sessionSaveTimer(nullptr)
+ , m_sessionSaveWatcher(nullptr)
+ , m_sessionSaveScheduled(false)
{
Q_INIT_RESOURCE(dolphin);
setStateConfigGroup("State");
+#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
+ new KColorSchemeManager(this); // Sets a sensible color scheme which fixes unreadable icons and text on Windows.
+#endif
+
connect(&DolphinNewFileMenuObserver::instance(), &DolphinNewFileMenuObserver::errorMessage, this, &DolphinMainWindow::showErrorMessage);
KIO::FileUndoManager *undoManager = KIO::FileUndoManager::self();
m_actionHandler = new DolphinViewActionHandler(actionCollection(), m_actionTextHelper, this);
connect(m_actionHandler, &DolphinViewActionHandler::actionBeingHandled, this, &DolphinMainWindow::clearStatusBar);
connect(m_actionHandler, &DolphinViewActionHandler::createDirectoryTriggered, this, &DolphinMainWindow::createDirectory);
+ connect(m_actionHandler, &DolphinViewActionHandler::createFileTriggered, this, &DolphinMainWindow::createFile);
connect(m_actionHandler, &DolphinViewActionHandler::selectionModeChangeTriggered, this, &DolphinMainWindow::slotSetSelectionMode);
- Q_CHECK_PTR(actionCollection()->action(QStringLiteral("create_dir")));
- m_newFileMenu->setNewFolderShortcutAction(actionCollection()->action(QStringLiteral("create_dir")));
+ QAction *newDirAction = actionCollection()->action(QStringLiteral("create_dir"));
+ Q_CHECK_PTR(newDirAction);
+ m_newFileMenu->setNewFolderShortcutAction(newDirAction);
+
+ QAction *newFileAction = actionCollection()->action(QStringLiteral("create_file"));
+ Q_CHECK_PTR(newFileAction);
+ m_newFileMenu->setNewFileShortcutAction(newFileAction);
m_remoteEncoding = new DolphinRemoteEncoding(this, m_actionHandler);
connect(this, &DolphinMainWindow::urlChanged, m_remoteEncoding, &DolphinRemoteEncoding::slotAboutToOpenUrl);
+ m_disabledActionNotifier = new DisabledActionNotifier(this);
+ connect(m_disabledActionNotifier, &DisabledActionNotifier::disabledActionTriggered, this, [this](const QAction *, QString reason) {
+ m_activeViewContainer->showMessage(reason, KMessageWidget::Warning);
+ });
+
setupDockWidgets();
- setupGUI(Save | Create | ToolBar);
+ const bool usePhoneUi{KRuntimePlatform::runtimePlatform().contains(QLatin1String("phone"))};
+ setupGUI(Save | Create | ToolBar, usePhoneUi ? QStringLiteral("dolphinuiforphones.rc") : QString() /* load the default dolphinui.rc file */);
stateChanged(QStringLiteral("new_file"));
QClipboard *clipboard = QApplication::clipboard();
if (firstRun) {
menuBar()->setVisible(false);
+
+ if (usePhoneUi) {
+ Q_ASSERT(qobject_cast<QDockWidget *>(m_placesPanel->parent()));
+ m_placesPanel->parentWidget()->hide();
+ auto settings = GeneralSettings::self();
+ settings->setShowZoomSlider(false); // Zooming can be done with pinch gestures instead and we are short on horizontal space.
+ settings->setRenameInline(false); // This works around inline renaming currently not working well with virtual keyboards.
+ settings->save(); // Otherwise the RenameInline setting is not picked up for the first time Dolphin is used.
+ }
}
const bool showMenu = !menuBar()->isHidden();
}
updateAllowedToolbarAreas();
+ updateNavigatorsBackground();
// enable middle-click on back/forward/up to open in a new tab
auto *middleClickEventFilter = new MiddleClickActionEventFilter(this);
{
// This fixes a crash on Wayland when closing the mainwindow while another dialog is open.
disconnect(QGuiApplication::clipboard(), &QClipboard::dataChanged, this, &DolphinMainWindow::updatePasteAction);
+
+ // This fixes a crash in dolphinmainwindowtest where the connection below fires even though the KMainWindow destructor of this object is already running.
+ Q_CHECK_PTR(qobject_cast<DolphinDockWidget *>(m_placesPanel->parent()));
+ disconnect(static_cast<DolphinDockWidget *>(m_placesPanel->parent()),
+ &DolphinDockWidget::visibilityChanged,
+ this,
+ &DolphinMainWindow::slotPlacesPanelVisibilityChanged);
}
QVector<DolphinViewContainer *> DolphinMainWindow::viewContainers() const
updatePasteAction();
updateViewActions();
updateGoActions();
+ m_diskSpaceUsageMenu->setUrl(url);
+
+ // will signal used urls to activities manager, too
+ m_recentFiles->addUrl(url, QString(), "inode/directory");
Q_EMIT urlChanged(url);
}
m_tearDownFromPlacesRequested = false;
}
- m_activeViewContainer->setAutoGrabFocus(false);
+ m_activeViewContainer->setGrabFocusOnUrlChange(false);
changeUrl(url);
- m_activeViewContainer->setAutoGrabFocus(true);
+ m_activeViewContainer->setGrabFocusOnUrlChange(true);
}
void DolphinMainWindow::slotEditableStateChanged(bool editable)
}
if (url.isValid()) {
QString icon;
- if (m_activeViewContainer->isSearchModeEnabled()) {
+ if (isSearchUrl(url)) {
icon = QStringLiteral("folder-saved-search-symbolic");
} else {
icon = KIO::iconNameForUrl(url);
}
}
-void DolphinMainWindow::openNewTab(const QUrl &url)
+DolphinTabPage *DolphinMainWindow::openNewTab(const QUrl &url)
{
- m_tabWidget->openNewTab(url, QUrl());
+ return m_tabWidget->openNewTab(url, QUrl());
}
void DolphinMainWindow::openNewTabAndActivate(const QUrl &url)
void DolphinMainWindow::slotSplitViewChanged()
{
m_tabWidget->currentTabPage()->setSplitViewEnabled(GeneralSettings::splitView(), WithAnimation);
- updateSplitAction();
+ updateSplitActions();
}
void DolphinMainWindow::openInNewTab()
KIO::StatJob *statJob = static_cast<KIO::StatJob *>(job);
if (statJob->error()) {
- m_activeViewContainer->showMessage(job->errorString(), DolphinViewContainer::Error);
+ m_activeViewContainer->showMessage(job->errorString(), KMessageWidget::Error);
} else {
KIO::highlightInFileManager({destinationUrl});
}
{
KXmlGuiWindow::showEvent(event);
- if (!event->spontaneous()) {
+ if (!event->spontaneous() && m_activeViewContainer) {
m_activeViewContainer->view()->setFocus();
}
}
}
}
- if (GeneralSettings::rememberOpenedTabs()) {
+ if (m_sessionSaveTimer && (m_sessionSaveTimer->isActive() || m_sessionSaveWatcher->isRunning())) {
+ const bool sessionSaveTimerActive = m_sessionSaveTimer->isActive();
+
+ m_sessionSaveTimer->stop();
+ m_sessionSaveWatcher->disconnect();
+ m_sessionSaveWatcher->waitForFinished();
+
+ if (sessionSaveTimerActive || m_sessionSaveScheduled) {
+ slotSaveSession();
+ }
+ }
+
+ GeneralSettings::setVersion(CurrentDolphinVersion);
+ GeneralSettings::self()->save();
+
+ KXmlGuiWindow::closeEvent(event);
+}
+
+void DolphinMainWindow::slotSaveSession()
+{
+ m_sessionSaveScheduled = false;
+
+ if (m_sessionSaveWatcher->isRunning()) {
+ // The previous session is still being saved - schedule another save.
+ m_sessionSaveWatcher->disconnect();
+ connect(m_sessionSaveWatcher, &QFutureWatcher<void>::finished, this, &DolphinMainWindow::slotSaveSession, Qt::SingleShotConnection);
+ m_sessionSaveScheduled = true;
+ } else if (!m_sessionSaveTimer->isActive()) {
+ // No point in saving the session if the timer is running (since it will save the session again when it times out).
KConfigGui::setSessionConfig(QStringLiteral("dolphin"), QStringLiteral("dolphin"));
KConfig *config = KConfigGui::sessionConfig();
saveGlobalProperties(config);
savePropertiesInternal(config, 1);
- config->sync();
+ KConfigGroup group = config->group(QStringLiteral("Number"));
+ group.writeEntry("NumberOfWindows", 1); // Makes session restore aware that there is a window to restore.
+
+ auto future = QtConcurrent::run([config]() {
+ config->sync();
+ });
+ m_sessionSaveWatcher->setFuture(future);
}
+}
- GeneralSettings::setVersion(CurrentDolphinVersion);
- GeneralSettings::self()->save();
+void DolphinMainWindow::setSessionAutoSaveEnabled(bool enable)
+{
+ if (enable) {
+ if (!m_sessionSaveTimer) {
+ m_sessionSaveTimer = new QTimer(this);
+ m_sessionSaveWatcher = new QFutureWatcher<void>(this);
+ m_sessionSaveTimer->setSingleShot(true);
+ m_sessionSaveTimer->setInterval(22000);
- KXmlGuiWindow::closeEvent(event);
+ connect(m_sessionSaveTimer, &QTimer::timeout, this, &DolphinMainWindow::slotSaveSession);
+ }
+
+ connect(m_tabWidget, &DolphinTabWidget::urlChanged, m_sessionSaveTimer, qOverload<>(&QTimer::start), Qt::UniqueConnection);
+ connect(m_tabWidget, &DolphinTabWidget::tabCountChanged, m_sessionSaveTimer, qOverload<>(&QTimer::start), Qt::UniqueConnection);
+ connect(m_tabWidget, &DolphinTabWidget::activeViewChanged, m_sessionSaveTimer, qOverload<>(&QTimer::start), Qt::UniqueConnection);
+ } else if (m_sessionSaveTimer) {
+ m_sessionSaveTimer->stop();
+ m_sessionSaveWatcher->disconnect();
+ m_sessionSaveScheduled = false;
+
+ m_sessionSaveWatcher->waitForFinished();
+
+ m_sessionSaveTimer->deleteLater();
+ m_sessionSaveWatcher->deleteLater();
+ m_sessionSaveTimer = nullptr;
+ m_sessionSaveWatcher = nullptr;
+ }
}
void DolphinMainWindow::saveProperties(KConfigGroup &group)
void DolphinMainWindow::createDirectory()
{
- m_newFileMenu->setWorkingDirectory(activeViewContainer()->url());
- m_newFileMenu->createDirectory();
+ // When creating directory, namejob is being run. In network folders,
+ // this job can take long time, so instead of starting multiple namejobs,
+ // just check if we are already running one. This prevents opening multiple
+ // dialogs. BUG:481401
+ if (!m_newFileMenu->isCreateDirectoryRunning()) {
+ m_newFileMenu->setWorkingDirectory(activeViewContainer()->url());
+ m_newFileMenu->createDirectory();
+ }
+}
+
+void DolphinMainWindow::createFile()
+{
+ // Use the same logic as in createDirectory()
+ if (!m_newFileMenu->isCreateFileRunning()) {
+ m_newFileMenu->setWorkingDirectory(activeViewContainer()->url());
+ m_newFileMenu->createFile();
+ }
}
void DolphinMainWindow::quit()
void DolphinMainWindow::showErrorMessage(const QString &message)
{
- m_activeViewContainer->showMessage(message, DolphinViewContainer::Error);
+ m_activeViewContainer->showMessage(message, KMessageWidget::Error);
}
void DolphinMainWindow::slotUndoAvailable(bool available)
void DolphinMainWindow::find()
{
- m_activeViewContainer->setSearchModeEnabled(true);
+ m_activeViewContainer->setSearchBarVisible(true);
+ m_activeViewContainer->setFocusToSearchBar();
}
void DolphinMainWindow::updateSearchAction()
{
QAction *toggleSearchAction = actionCollection()->action(QStringLiteral("toggle_search"));
- toggleSearchAction->setChecked(m_activeViewContainer->isSearchModeEnabled());
+ toggleSearchAction->setChecked(m_activeViewContainer->isSearchBarVisible());
}
void DolphinMainWindow::updatePasteAction()
QAction *pasteAction = actionCollection()->action(KStandardAction::name(KStandardAction::Paste));
QPair<bool, QString> pasteInfo = m_activeViewContainer->view()->pasteInfo();
pasteAction->setEnabled(pasteInfo.first);
+ m_disabledActionNotifier->setDisabledReason(pasteAction,
+ m_activeViewContainer->rootItem().isWritable()
+ ? i18nc("@info", "Cannot paste: The clipboard is empty.")
+ : i18nc("@info", "Cannot paste: You do not have permission to write into this folder."));
pasteAction->setText(pasteInfo.second);
}
{
const QUrl url = urlNavigator->locationUrl(historyIndex);
- QString text = url.toDisplayString(QUrl::PreferLocalFile);
+ QString text;
- if (!urlNavigator->showFullPath()) {
+ if (isSearchUrl(url)) {
+ text = Search::DolphinQuery(url, QUrl{}).title();
+ } else if (urlNavigator->showFullPath()) {
+ text = url.toDisplayString(QUrl::PreferLocalFile);
+ } else {
const KFilePlacesModel *placesModel = DolphinPlacesModelSingleton::instance().placesModel();
const QModelIndex closestIdx = placesModel->closestItem(url);
const KUrlNavigator *urlNavigator = m_activeViewContainer->urlNavigatorInternalWithHistory();
int entries = 0;
QMenu *menu = m_forwardAction->popupMenu();
+ menu->clear();
for (int i = urlNavigator->historyIndex() - 1; i >= 0 && entries < MaxNumberOfNavigationentries; --i, ++entries) {
QAction *action = urlNavigatorHistoryAction(urlNavigator, i, menu);
menu->addAction(action);
void DolphinMainWindow::toggleSplitView()
{
+ QUrl newSplitViewUrl;
+ const KFileItemList list = m_activeViewContainer->view()->selectedItems();
+ if (list.count() == 1) {
+ const KFileItem &item = list.first();
+ newSplitViewUrl = DolphinView::openItemAsFolderUrl(item);
+ }
+
DolphinTabPage *tabPage = m_tabWidget->currentTabPage();
- tabPage->setSplitViewEnabled(!tabPage->splitViewEnabled(), WithAnimation);
+ tabPage->setSplitViewEnabled(!tabPage->splitViewEnabled(), WithAnimation, newSplitViewUrl);
+ m_tabWidget->updateTabName(m_tabWidget->indexOf(tabPage));
updateViewActions();
}
+void DolphinMainWindow::popoutSplitView()
+{
+ DolphinTabPage *tabPage = m_tabWidget->currentTabPage();
+ if (!tabPage->splitViewEnabled())
+ return;
+ openNewWindow((GeneralSettings::closeActiveSplitView() ? tabPage->activeViewContainer() : tabPage->inactiveViewContainer())->url());
+ tabPage->setSplitViewEnabled(false, WithAnimation);
+ updateSplitActions();
+}
+
void DolphinMainWindow::toggleSplitStash()
{
DolphinTabPage *tabPage = m_tabWidget->currentTabPage();
// If the text field currently has focus and everything is selected,
// pressing the keyboard shortcut returns the whole thing to breadcrumb mode
+ // and goes back to the view, just like how it was before this action was triggered the first time.
if (navigator->isUrlEditable() && lineEdit->hasFocus() && lineEdit->selectedText() == lineEdit->text()) {
navigator->setUrlEditable(false);
+ m_activeViewContainer->view()->setFocus();
} else {
navigator->setUrlEditable(true);
navigator->setFocus();
GeneralSettings::setLockPanels(newLockState);
}
-void DolphinMainWindow::slotTerminalPanelVisibilityChanged()
+void DolphinMainWindow::slotTerminalPanelVisibilityChanged(bool visible)
{
- if (m_terminalPanel->isHiddenInVisibleWindow() && m_activeViewContainer) {
+ if (!visible && m_activeViewContainer) {
m_activeViewContainer->view()->setFocus();
}
+ // Putting focus to the Terminal is not handled here but in TerminalPanel::showEvent().
+}
+
+void DolphinMainWindow::slotPlacesPanelVisibilityChanged(bool visible)
+{
+ if (!visible && m_activeViewContainer) {
+ m_activeViewContainer->view()->setFocus();
+ return;
+ }
+ m_placesPanel->setFocus();
}
void DolphinMainWindow::goBack()
QPointer<QAction> DolphinMainWindow::preferredSearchTool()
{
m_searchTools.clear();
- KMoreToolsMenuFactory("dolphin/search-tools").fillMenuFromGroupingNames(&m_searchTools, {"files-find"}, m_activeViewContainer->url());
- QList<QAction *> actions = m_searchTools.actions();
- if (actions.isEmpty()) {
- return nullptr;
- }
- QAction *action = actions.first();
- if (action->isSeparator()) {
+
+ KService::Ptr kfind = KService::serviceByDesktopName(QStringLiteral("org.kde.kfind"));
+
+ if (!kfind) {
return nullptr;
}
+
+ auto *action = new QAction(QIcon::fromTheme(kfind->icon()), kfind->name(), this);
+
+ connect(action, &QAction::triggered, this, [this, kfind] {
+ auto *job = new KIO::ApplicationLauncherJob(kfind);
+ job->setUrls({m_activeViewContainer->url()});
+ job->start();
+ });
+
return action;
}
// trash:/ is writable but we don't want to create new items in it.
// TODO: remove the trash check once https://phabricator.kde.org/T8234 is implemented
newFileMenu()->setEnabled(isFolderWritable && m_activeViewContainer->url().scheme() != QLatin1String("trash"));
+ // When the menu is disabled, actions in it are disabled later in the event loop, and we need to set the disabled reason after that.
+ QTimer::singleShot(0, this, [this]() {
+ m_disabledActionNotifier->setDisabledReason(actionCollection()->action(QStringLiteral("create_file")),
+ i18nc("@info", "Cannot create new file: You do not have permission to create items in this folder."));
+ m_disabledActionNotifier->setDisabledReason(actionCollection()->action(QStringLiteral("create_dir")),
+ i18nc("@info", "Cannot create new folder: You do not have permission to create items in this folder."));
+ });
}
void DolphinMainWindow::openContextMenu(const QPoint &pos, const KFileItem &item, const KFileItemList &selectedItems, const QUrl &url)
{
QPointer<DolphinContextMenu> contextMenu = new DolphinContextMenu(this, item, selectedItems, url, &m_fileItemActions);
- contextMenu.data()->exec(pos);
+ contextMenu->exec(pos);
// Delete the menu, unless it has been deleted in its own nested event loop already.
if (contextMenu) {
menu = new QMenu(this);
hamburgerMenu->setMenu(menu);
hamburgerMenu->hideActionsOf(ac->action(QStringLiteral("basic_actions"))->menu());
- hamburgerMenu->hideActionsOf(ac->action(QStringLiteral("zoom"))->menu());
+ hamburgerMenu->hideActionsOf(qobject_cast<KToolBarPopupAction *>(ac->action(QStringLiteral("zoom")))->popupMenu());
} else {
menu->clear();
}
// The third group contains actions to change what one sees in the view
// and to change the more general UI.
if (!toolBar()->isVisible()
- || (!toolbarActions.contains(ac->action(QStringLiteral("icons"))) && !toolbarActions.contains(ac->action(QStringLiteral("compact")))
- && !toolbarActions.contains(ac->action(QStringLiteral("details"))) && !toolbarActions.contains(ac->action(QStringLiteral("view_mode"))))) {
+ || ((!toolbarActions.contains(ac->action(QStringLiteral("icons"))) && !toolbarActions.contains(ac->action(QStringLiteral("compact")))
+ && !toolbarActions.contains(ac->action(QStringLiteral("details"))) && !toolbarActions.contains(ac->action(QStringLiteral("view_mode"))))
+ && !toolbarActions.contains(ac->action(QStringLiteral("view_settings"))))) {
menu->addAction(ac->action(QStringLiteral("view_mode")));
}
- menu->addAction(ac->action(QStringLiteral("show_hidden_files")));
- menu->addAction(ac->action(QStringLiteral("sort")));
- menu->addAction(ac->action(QStringLiteral("additional_info")));
- if (!GeneralSettings::showStatusBar() || !GeneralSettings::showZoomSlider()) {
- menu->addAction(ac->action(QStringLiteral("zoom")));
+ if (!toolBar()->isVisible() || !toolbarActions.contains(ac->action(QStringLiteral("view_settings")))) {
+ menu->addAction(ac->action(QStringLiteral("show_hidden_files")));
+ menu->addAction(ac->action(QStringLiteral("sort")));
+ menu->addAction(ac->action(QStringLiteral("additional_info")));
+ if (!GeneralSettings::showStatusBar() || !GeneralSettings::showZoomSlider()) {
+ menu->addAction(ac->action(QStringLiteral("zoom")));
+ }
}
menu->addAction(ac->action(QStringLiteral("panels")));
// We can end up here if the user clicked a device in the Places Panel
// which had been unmounted earlier, see https://bugs.kde.org/show_bug.cgi?id=161385.
reloadView();
+
+ m_activeViewContainer->view()->setFocus(); // We always want the focus on the view after activating a place.
} else {
view->disableUrlNavigatorSelectionRequests();
changeUrl(url);
m_activeViewContainer = viewContainer;
if (oldViewContainer) {
- const QAction *toggleSearchAction = actionCollection()->action(QStringLiteral("toggle_search"));
- toggleSearchAction->disconnect(oldViewContainer);
-
// Disconnect all signals between the old view container (container,
// view and url navigator) and main window.
oldViewContainer->disconnect(this);
if (auto secondaryUrlNavigator = navigators->secondaryUrlNavigator()) {
secondaryUrlNavigator->disconnect(this);
}
+ oldViewContainer->disconnect(m_diskSpaceUsageMenu);
// except the requestItemInfo so that on hover the information panel can still be updated
connect(oldViewContainer->view(), &DolphinView::requestItemInfo, this, &DolphinMainWindow::requestItemInfo);
updateViewActions();
updateGoActions();
updateSearchAction();
+ connect(m_diskSpaceUsageMenu,
+ &DiskSpaceUsageMenu::showMessage,
+ viewContainer,
+ [viewContainer](const QString &message, KMessageWidget::MessageType messageType) {
+ viewContainer->showMessage(message, messageType);
+ });
+ connect(m_diskSpaceUsageMenu, &DiskSpaceUsageMenu::showInstallationProgress, viewContainer, &DolphinViewContainer::showProgress);
const QUrl url = viewContainer->url();
Q_EMIT urlChanged(url);
+ Q_EMIT selectionChanged(m_activeViewContainer->view()->selectedItems());
}
void DolphinMainWindow::tabCountChanged(int count)
{
const QVector<DolphinViewContainer *> theViewContainers = viewContainers();
for (DolphinViewContainer *viewContainer : theViewContainers) {
- if (viewContainer && viewContainer->url().toLocalFile().startsWith(mountPath)) {
+ if (!viewContainer) {
+ continue;
+ }
+ const auto viewPath = viewContainer->url().toLocalFile();
+ if (viewPath.startsWith(mountPath + QLatin1String("/")) || viewPath == mountPath) {
viewContainer->setUrl(QUrl::fromLocalFile(QDir::homePath()));
}
}
auto hamburgerMenuAction = KStandardAction::hamburgerMenu(nullptr, nullptr, actionCollection());
// setup 'File' menu
- m_newFileMenu = new DolphinNewFileMenu(nullptr, this);
+ m_newFileMenu = new DolphinNewFileMenu(nullptr, nullptr, this);
actionCollection()->addAction(QStringLiteral("new_menu"), m_newFileMenu);
QMenu *menu = m_newFileMenu->menu();
menu->setTitle(i18nc("@title:menu Create new folder, file, link, etc.", "Create New"));
newWindow->setToolTip(i18nc("@info", "Open a new Dolphin window"));
newWindow->setWhatsThis(xi18nc("@info:whatsthis",
"This opens a new "
- "window just like this one with the current location and view."
+ "window just like this one with the current location."
"<nl/>You can drag and drop items between windows."));
newWindow->setIcon(QIcon::fromTheme(QStringLiteral("window-new")));
newTab->setText(i18nc("@action:inmenu File", "New Tab"));
newTab->setWhatsThis(xi18nc("@info:whatsthis",
"This opens a new "
- "<emphasis>Tab</emphasis> with the current location and view.<nl/>"
- "A tab is an additional view within this window. "
+ "<emphasis>Tab</emphasis> with the current location."
+ "<nl/>Tabs allow you to quickly switch between multiple locations and views within this window. "
"You can drag and drop items between tabs."));
- actionCollection()->setDefaultShortcuts(newTab, {Qt::CTRL | Qt::Key_T, Qt::CTRL | Qt::SHIFT | Qt::Key_N});
+ actionCollection()->setDefaultShortcut(newTab, Qt::CTRL | Qt::Key_T);
connect(newTab, &QAction::triggered, this, &DolphinMainWindow::openNewActivatedTab);
QAction *addToPlaces = actionCollection()->addAction(QStringLiteral("add_to_places"));
QAction *closeTab = KStandardAction::close(m_tabWidget, QOverload<>::of(&DolphinTabWidget::closeTab), actionCollection());
closeTab->setText(i18nc("@action:inmenu File", "Close Tab"));
+ closeTab->setToolTip(i18nc("@info", "Close Tab"));
closeTab->setWhatsThis(i18nc("@info:whatsthis",
"This closes the "
- "currently viewed tab. If no more tabs are left this window "
- "will close instead."));
+ "currently viewed tab. If no more tabs are left, this closes "
+ "the whole window instead."));
QAction *quitAction = KStandardAction::quit(this, &DolphinMainWindow::quit, actionCollection());
quitAction->setWhatsThis(i18nc("@info:whatsthis quit", "This closes this window."));
m_actionTextHelper->registerTextWhenNothingIsSelected(copyToOtherViewAction, i18nc("@action:inmenu", "Copy to Other View…"));
copyToOtherViewAction->setWhatsThis(xi18nc("@info:whatsthis Copy",
"This copies the selected items from "
- "the <emphasis>active</emphasis> view to the inactive split view."));
+ "the view in focus to the other view. "
+ "(Only available while in Split View mode.)"));
copyToOtherViewAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
- copyToOtherViewAction->setIconText(i18nc("@action:inmenu Edit", "Copy to Inactive Split View"));
+ copyToOtherViewAction->setIconText(i18nc("@action:inmenu Edit", "Copy to Other View"));
actionCollection()->setDefaultShortcut(copyToOtherViewAction, Qt::SHIFT | Qt::Key_F5);
connect(copyToOtherViewAction, &QAction::triggered, this, &DolphinMainWindow::copyToInactiveSplitView);
m_actionTextHelper->registerTextWhenNothingIsSelected(moveToOtherViewAction, i18nc("@action:inmenu", "Move to Other View…"));
moveToOtherViewAction->setWhatsThis(xi18nc("@info:whatsthis Move",
"This moves the selected items from "
- "the <emphasis>active</emphasis> view to the inactive split view."));
+ "the view in focus to the other view. "
+ "(Only available while in Split View mode.)"));
moveToOtherViewAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-cut")));
- moveToOtherViewAction->setIconText(i18nc("@action:inmenu Edit", "Move to Inactive Split View"));
+ moveToOtherViewAction->setIconText(i18nc("@action:inmenu Edit", "Move to Other View"));
actionCollection()->setDefaultShortcut(moveToOtherViewAction, Qt::SHIFT | Qt::Key_F6);
connect(moveToOtherViewAction, &QAction::triggered, this, &DolphinMainWindow::moveToInactiveSplitView);
showFilterBar->setWhatsThis(xi18nc("@info:whatsthis",
"This opens the "
"<emphasis>Filter Bar</emphasis> at the bottom of the window.<nl/> "
- "There you can enter a text to filter the files and folders currently displayed. "
+ "There you can enter text to filter the files and folders currently displayed. "
"Only those that contain the text in their name will be kept in view."));
showFilterBar->setIcon(QIcon::fromTheme(QStringLiteral("view-filter")));
actionCollection()->setDefaultShortcuts(showFilterBar, {Qt::CTRL | Qt::Key_I, Qt::Key_Slash});
searchAction->setToolTip(i18nc("@info:tooltip", "Search for files and folders"));
searchAction->setWhatsThis(xi18nc("@info:whatsthis find",
"<para>This helps you "
- "find files and folders by opening a <emphasis>find bar</emphasis>. "
+ "find files and folders by opening a <emphasis>search bar</emphasis>. "
"There you can enter search terms and specify settings to find the "
- "objects you are looking for.</para><para>Use this help again on "
- "the find bar so we can have a look at it while the settings are "
- "explained.</para>"));
+ "items you are looking for.</para>"));
// toggle_search acts as a copy of the main searchAction to be used mainly
// in the toolbar, with no default shortcut attached, to avoid messing with
toggleSearchAction->setToolTip(searchAction->toolTip());
toggleSearchAction->setWhatsThis(searchAction->whatsThis());
toggleSearchAction->setCheckable(true);
+ connect(toggleSearchAction, &QAction::triggered, this, [this](bool checked) {
+ if (checked) {
+ find();
+ } else {
+ m_activeViewContainer->setSearchBarVisible(false);
+ }
+ });
QAction *toggleSelectionModeAction = actionCollection()->addAction(QStringLiteral("toggle_selection_mode"));
// i18n: This action toggles a selection mode.
"</para>"));
toggleSelectionModeAction->setIcon(QIcon::fromTheme(QStringLiteral("quickwizard")));
toggleSelectionModeAction->setCheckable(true);
- actionCollection()->setDefaultShortcut(toggleSelectionModeAction, Qt::Key_Space );
+ actionCollection()->setDefaultShortcut(toggleSelectionModeAction, Qt::Key_Space);
connect(toggleSelectionModeAction, &QAction::triggered, this, &DolphinMainWindow::toggleSelectionMode);
// A special version of the toggleSelectionModeAction for the toolbar that also contains a menu
invertSelection->setText(i18nc("@action:inmenu Edit", "Invert Selection"));
invertSelection->setWhatsThis(xi18nc("@info:whatsthis invert",
"This selects all "
- "objects that you have currently <emphasis>not</emphasis> selected instead."));
+ "items that you have currently <emphasis>not</emphasis> selected instead."));
invertSelection->setIcon(QIcon::fromTheme(QStringLiteral("edit-select-invert")));
actionCollection()->setDefaultShortcut(invertSelection, Qt::CTRL | Qt::SHIFT | Qt::Key_A);
connect(invertSelection, &QAction::triggered, this, &DolphinMainWindow::invertSelection);
// setup 'View' menu
// (note that most of it is set up in DolphinViewActionHandler)
- QAction *split = actionCollection()->addAction(QStringLiteral("split_view"));
- split->setWhatsThis(xi18nc("@info:whatsthis find",
- "<para>This splits "
- "the folder view below into two autonomous views.</para><para>This "
- "way you can see two locations at once and move items between them "
- "quickly.</para>Click this again afterwards to recombine the views."));
- actionCollection()->setDefaultShortcut(split, Qt::Key_F3);
- connect(split, &QAction::triggered, this, &DolphinMainWindow::toggleSplitView);
+ Admin::WorkerIntegration::createActAsAdminAction(actionCollection(), this);
+
+ m_splitViewAction = actionCollection()->add<KActionMenu>(QStringLiteral("split_view"));
+ m_splitViewMenuAction = actionCollection()->addAction(QStringLiteral("split_view_menu"));
+
+ m_splitViewAction->setWhatsThis(xi18nc("@info:whatsthis split",
+ "<para>This presents "
+ "a second view side-by-side with the current view, so you can see "
+ "the contents of two folders at once and easily move items between "
+ "them.</para><para>The view that is not \"in focus\" will be dimmed. "
+ "</para>Click this button again to close one of the views."));
+ m_splitViewMenuAction->setWhatsThis(m_splitViewAction->whatsThis());
+
+ // only set it for the menu version
+ actionCollection()->setDefaultShortcut(m_splitViewMenuAction, Qt::Key_F3);
+
+ connect(m_splitViewAction, &QAction::triggered, this, &DolphinMainWindow::toggleSplitView);
+ connect(m_splitViewMenuAction, &QAction::triggered, this, &DolphinMainWindow::toggleSplitView);
+
+ QAction *popoutSplit = actionCollection()->addAction(QStringLiteral("popout_split_view"));
+ popoutSplit->setWhatsThis(xi18nc("@info:whatsthis",
+ "If the view has been split, this will pop the view in focus "
+ "out into a new window."));
+ popoutSplit->setIcon(QIcon::fromTheme(QStringLiteral("window-new")));
+ actionCollection()->setDefaultShortcut(popoutSplit, Qt::SHIFT | Qt::Key_F3);
+ connect(popoutSplit, &QAction::triggered, this, &DolphinMainWindow::popoutSplitView);
QAction *stashSplit = actionCollection()->addAction(QStringLiteral("split_stash"));
actionCollection()->setDefaultShortcut(stashSplit, Qt::CTRL | Qt::Key_S);
undoAction->setWhatsThis(xi18nc("@info:whatsthis",
"This undoes "
"the last change you made to files or folders.<nl/>"
- "Such changes include <interface>creating, renaming</interface> "
+ "Such changes include <interface>creating</interface>, <interface>renaming</interface> "
"and <interface>moving</interface> them to a different location "
- "or to the <filename>Trash</filename>. <nl/>Changes that can't "
- "be undone will ask for your confirmation."));
+ "or to the <filename>Trash</filename>. <nl/>Any changes that cannot be undone "
+ "will ask for your confirmation beforehand."));
undoAction->setEnabled(false); // undo should be disabled by default
{
homeAction->setWhatsThis(xi18nc("@info:whatsthis",
"Go to your "
"<filename>Home</filename> folder.<nl/>Every user account "
- "has their own <filename>Home</filename> that contains their data "
- "including folders that contain personal application data."));
+ "has their own <filename>Home</filename> that contains their personal files, "
+ "as well as hidden folders for their applications' data and configuration files."));
// setup 'Tools' menu
QAction *compareFiles = actionCollection()->addAction(QStringLiteral("compare_files"));
compareFiles->setEnabled(false);
connect(compareFiles, &QAction::triggered, this, &DolphinMainWindow::compareFiles);
+ QAction *manageDiskSpaceUsage = actionCollection()->addAction(QStringLiteral("manage_disk_space"));
+ manageDiskSpaceUsage->setText(i18nc("@action:inmenu Tools", "Manage Disk Space Usage"));
+ manageDiskSpaceUsage->setIcon(QIcon::fromTheme(QStringLiteral("filelight")));
+ m_diskSpaceUsageMenu = new DiskSpaceUsageMenu{this};
+ manageDiskSpaceUsage->setMenu(m_diskSpaceUsageMenu);
+
QAction *openPreferredSearchTool = actionCollection()->addAction(QStringLiteral("open_preferred_search_tool"));
openPreferredSearchTool->setText(i18nc("@action:inmenu Tools", "Open Preferred Search Tool"));
openPreferredSearchTool->setWhatsThis(xi18nc("@info:whatsthis",
connect(openPreferredSearchTool, &QAction::triggered, this, &DolphinMainWindow::openPreferredSearchTool);
if (KAuthorized::authorize(QStringLiteral("shell_access"))) {
+ // Get icon of user default terminal emulator application
+ const KConfigGroup group(KSharedConfig::openConfig(QStringLiteral("kdeglobals"), KConfig::SimpleConfig), QStringLiteral("General"));
+ const QString terminalDesktopFilename = group.readEntry("TerminalService");
+ // Use utilities-terminal icon from theme if readEntry() has failed
+ const QString terminalIcon = terminalDesktopFilename.isEmpty() ? "utilities-terminal" : KDesktopFile(terminalDesktopFilename).readIcon();
+
QAction *openTerminal = actionCollection()->addAction(QStringLiteral("open_terminal"));
openTerminal->setText(i18nc("@action:inmenu Tools", "Open Terminal"));
openTerminal->setWhatsThis(xi18nc("@info:whatsthis",
"<para>This opens a <emphasis>terminal</emphasis> application for the viewed location.</para>"
- "<para>To learn more about terminals use the help in the terminal application.</para>"));
- openTerminal->setIcon(QIcon::fromTheme(QStringLiteral("utilities-terminal")));
+ "<para>To learn more about terminals use the help features in the terminal application.</para>"));
+ openTerminal->setIcon(QIcon::fromTheme(terminalIcon));
actionCollection()->setDefaultShortcut(openTerminal, Qt::SHIFT | Qt::Key_F4);
connect(openTerminal, &QAction::triggered, this, &DolphinMainWindow::openTerminal);
openTerminalHere->setText(i18nc("@action:inmenu Tools", "Open Terminal Here"));
openTerminalHere->setWhatsThis(xi18nc("@info:whatsthis",
"<para>This opens <emphasis>terminal</emphasis> applications for the selected items' locations.</para>"
- "<para>To learn more about terminals use the help in the terminal application.</para>"));
- openTerminalHere->setIcon(QIcon::fromTheme(QStringLiteral("utilities-terminal")));
+ "<para>To learn more about terminals use the help features in the terminal application.</para>"));
+ openTerminalHere->setIcon(QIcon::fromTheme(terminalIcon));
actionCollection()->setDefaultShortcut(openTerminalHere, Qt::SHIFT | Qt::ALT | Qt::Key_F4);
connect(openTerminalHere, &QAction::triggered, this, &DolphinMainWindow::openTerminalHere);
-
-#if HAVE_TERMINAL
- QAction *focusTerminalPanel = actionCollection()->addAction(QStringLiteral("focus_terminal_panel"));
- focusTerminalPanel->setText(i18nc("@action:inmenu Tools", "Focus Terminal Panel"));
- focusTerminalPanel->setIcon(QIcon::fromTheme(QStringLiteral("swap-panels")));
- actionCollection()->setDefaultShortcut(focusTerminalPanel, Qt::CTRL | Qt::SHIFT | Qt::Key_F4);
- connect(focusTerminalPanel, &QAction::triggered, this, &DolphinMainWindow::focusTerminalPanel);
-#endif
}
// setup 'Bookmarks' menu
KToggleAction *showMenuBar = KStandardAction::showMenubar(nullptr, nullptr, actionCollection());
showMenuBar->setWhatsThis(xi18nc("@info:whatsthis",
"<para>This switches between having a <emphasis>Menubar</emphasis> "
- "and having a <interface>%1</interface> button. Both "
+ "and having an <interface>%1</interface> button. Both "
"contain mostly the same actions and configuration options.</para>"
- "<para>The Menubar takes up more space but allows for fast and organised access to all "
- "actions an application has to offer.</para><para>The <interface>%1</interface> button "
+ "<para>The Menubar takes up more space but allows for fast and organized access to all "
+ "actions an application has to offer.</para><para>The %1 button "
"is simpler and small which makes triggering advanced actions more time consuming.</para>",
hamburgerMenuAction->text().replace('&', "")));
connect(showMenuBar,
&DolphinMainWindow::toggleShowMenuBar,
Qt::QueuedConnection);
- KToggleAction *showStatusBar = KStandardAction::showStatusbar(nullptr, nullptr, actionCollection());
- showStatusBar->setChecked(GeneralSettings::showStatusBar());
- connect(GeneralSettings::self(), &GeneralSettings::showStatusBarChanged, showStatusBar, &KToggleAction::setChecked);
- connect(showStatusBar, &KToggleAction::triggered, this, [this](bool checked) {
- GeneralSettings::setShowStatusBar(checked);
- refreshViews();
- });
-
KStandardAction::keyBindings(this, &DolphinMainWindow::slotKeyBindings, actionCollection());
KStandardAction::preferences(this, &DolphinMainWindow::editSettings, actionCollection());
for (int i = 0; i < MaxActivateTabShortcuts; ++i) {
QAction *activateTab = actionCollection()->addAction(QStringLiteral("activate_tab_%1").arg(i));
- activateTab->setText(i18nc("@action:inmenu", "Activate Tab %1", i + 1));
+ activateTab->setText(i18nc("@action:inmenu", "Go to Tab %1", i + 1));
activateTab->setEnabled(false);
connect(activateTab, &QAction::triggered, this, [this, i]() {
m_tabWidget->activateTab(i);
}
QAction *activateLastTab = actionCollection()->addAction(QStringLiteral("activate_last_tab"));
- activateLastTab->setText(i18nc("@action:inmenu", "Activate Last Tab"));
+ activateLastTab->setIconText(i18nc("@action:inmenu", "Last Tab"));
+ activateLastTab->setText(i18nc("@action:inmenu", "Go to Last Tab"));
activateLastTab->setEnabled(false);
connect(activateLastTab, &QAction::triggered, m_tabWidget, &DolphinTabWidget::activateLastTab);
actionCollection()->setDefaultShortcut(activateLastTab, Qt::ALT | Qt::Key_0);
QAction *activateNextTab = actionCollection()->addAction(QStringLiteral("activate_next_tab"));
activateNextTab->setIconText(i18nc("@action:inmenu", "Next Tab"));
- activateNextTab->setText(i18nc("@action:inmenu", "Activate Next Tab"));
+ activateNextTab->setText(i18nc("@action:inmenu", "Go to Next Tab"));
activateNextTab->setEnabled(false);
connect(activateNextTab, &QAction::triggered, m_tabWidget, &DolphinTabWidget::activateNextTab);
actionCollection()->setDefaultShortcuts(activateNextTab, nextTabKeys);
QAction *activatePrevTab = actionCollection()->addAction(QStringLiteral("activate_prev_tab"));
activatePrevTab->setIconText(i18nc("@action:inmenu", "Previous Tab"));
- activatePrevTab->setText(i18nc("@action:inmenu", "Activate Previous Tab"));
+ activatePrevTab->setText(i18nc("@action:inmenu", "Go to Previous Tab"));
activatePrevTab->setEnabled(false);
connect(activatePrevTab, &QAction::triggered, m_tabWidget, &DolphinTabWidget::activatePrevTab);
actionCollection()->setDefaultShortcuts(activatePrevTab, prevTabKeys);
QAction *openInSplitViewAction = actionCollection()->addAction(QStringLiteral("open_in_split_view"));
openInSplitViewAction->setText(i18nc("@action:inmenu", "Open in Split View"));
- openInSplitViewAction->setIcon(QIcon::fromTheme(QStringLiteral("view-right-new")));
+ openInSplitViewAction->setIcon(QIcon::fromTheme(QStringLiteral("view-split-left-right")));
connect(openInSplitViewAction, &QAction::triggered, this, [this]() {
openInSplitView(QUrl());
});
+
+ m_recentFiles = new KRecentFilesAction(this);
}
void DolphinMainWindow::setupDockWidgets()
connect(infoPanel, &InformationPanel::urlActivated, this, &DolphinMainWindow::handleUrl);
infoDock->setWidget(infoPanel);
- QAction *infoAction = infoDock->toggleViewAction();
- createPanelAction(QIcon::fromTheme(QStringLiteral("dialog-information")), Qt::Key_F11, infoAction, QStringLiteral("show_information_panel"));
+ createPanelAction(QIcon::fromTheme(QStringLiteral("documentinfo")), Qt::Key_F11, infoDock, QStringLiteral("show_information_panel"));
addDockWidget(Qt::RightDockWidgetArea, infoDock);
connect(this, &DolphinMainWindow::urlChanged, infoPanel, &InformationPanel::setUrl);
connect(this, &DolphinMainWindow::selectionChanged, infoPanel, &InformationPanel::setSelection);
connect(this, &DolphinMainWindow::requestItemInfo, infoPanel, &InformationPanel::requestDelayedItemInfo);
connect(this, &DolphinMainWindow::fileItemsChanged, infoPanel, &InformationPanel::slotFilesItemChanged);
+ connect(this, &DolphinMainWindow::settingsChanged, infoPanel, &InformationPanel::readSettings);
#endif
// i18n: This is the last paragraph for the "What's This"-texts of all four panels.
foldersPanel->setCustomContextMenuActions({lockLayoutAction});
foldersDock->setWidget(foldersPanel);
- QAction *foldersAction = foldersDock->toggleViewAction();
- createPanelAction(QIcon::fromTheme(QStringLiteral("folder")), Qt::Key_F7, foldersAction, QStringLiteral("show_folders_panel"));
+ createPanelAction(QIcon::fromTheme(QStringLiteral("folder")), Qt::Key_F7, foldersDock, QStringLiteral("show_folders_panel"));
addDockWidget(Qt::LeftDockWidgetArea, foldersDock);
connect(this, &DolphinMainWindow::urlChanged, foldersPanel, &FoldersPanel::setUrl);
DolphinDockWidget *terminalDock = new DolphinDockWidget(i18nc("@title:window Shell terminal", "Terminal"));
terminalDock->setLocked(lock);
terminalDock->setObjectName(QStringLiteral("terminalDock"));
+ terminalDock->setContentsMargins(0, 0, 0, 0);
m_terminalPanel = new TerminalPanel(terminalDock);
m_terminalPanel->setCustomContextMenuActions({lockLayoutAction});
terminalDock->setWidget(m_terminalPanel);
connect(terminalDock, &DolphinDockWidget::visibilityChanged, m_terminalPanel, &TerminalPanel::dockVisibilityChanged);
connect(terminalDock, &DolphinDockWidget::visibilityChanged, this, &DolphinMainWindow::slotTerminalPanelVisibilityChanged);
- QAction *terminalAction = terminalDock->toggleViewAction();
- createPanelAction(QIcon::fromTheme(QStringLiteral("dialog-scripts")), Qt::Key_F4, terminalAction, QStringLiteral("show_terminal_panel"));
+ createPanelAction(QIcon::fromTheme(QStringLiteral("dialog-scripts")), Qt::Key_F4, terminalDock, QStringLiteral("show_terminal_panel"));
addDockWidget(Qt::BottomDockWidgetArea, terminalDock);
connect(this, &DolphinMainWindow::urlChanged, m_terminalPanel, &TerminalPanel::setUrl);
"<nl/>The location in the terminal will always match the folder "
"view so you can navigate using either.</para><para>The terminal "
"panel is not needed for basic computer usage but can be useful "
- "for advanced tasks. To learn more about terminals use the help "
+ "for advanced tasks. To learn more about terminals use the help features "
"in a standalone terminal application like Konsole.</para>"));
terminalDock->setWhatsThis(xi18nc("@info:whatsthis",
"<para>This is "
"normal terminal but will match the location of the folder view "
"so you can navigate using either.</para><para>The terminal panel "
"is not needed for basic computer usage but can be useful for "
- "advanced tasks. To learn more about terminals use the help in a "
+ "advanced tasks. To learn more about terminals use the help features in a "
"standalone terminal application like Konsole.</para>")
+ panelWhatsThis);
- }
-#endif
+
+ QAction *focusTerminalPanel = actionCollection()->addAction(QStringLiteral("focus_terminal_panel"));
+ focusTerminalPanel->setText(i18nc("@action:inmenu Tools", "Focus Terminal Panel"));
+ focusTerminalPanel->setToolTip(i18nc("@info:tooltip", "Move keyboard focus to and from the Terminal panel."));
+ focusTerminalPanel->setIcon(QIcon::fromTheme(QStringLiteral("swap-panels")));
+ actionCollection()->setDefaultShortcut(focusTerminalPanel, Qt::CTRL | Qt::SHIFT | Qt::Key_F4);
+ connect(focusTerminalPanel, &QAction::triggered, this, &DolphinMainWindow::toggleTerminalPanelFocus);
+ } // endif "shell_access" allowed
+#endif // HAVE_TERMINAL
if (GeneralSettings::version() < 200) {
infoDock->hide();
m_placesPanel->setCustomContextMenuActions({lockLayoutAction});
placesDock->setWidget(m_placesPanel);
- QAction *placesAction = placesDock->toggleViewAction();
- createPanelAction(QIcon::fromTheme(QStringLiteral("compass")), Qt::Key_F9, placesAction, QStringLiteral("show_places_panel"));
+ createPanelAction(QIcon::fromTheme(QStringLiteral("compass")), Qt::Key_F9, placesDock, QStringLiteral("show_places_panel"));
addDockWidget(Qt::LeftDockWidgetArea, placesDock);
connect(m_placesPanel, &PlacesPanel::placeActivated, this, &DolphinMainWindow::slotPlaceActivated);
connect(m_placesPanel, &PlacesPanel::errorMessage, this, &DolphinMainWindow::showErrorMessage);
connect(this, &DolphinMainWindow::urlChanged, m_placesPanel, &PlacesPanel::setUrl);
connect(placesDock, &DolphinDockWidget::visibilityChanged, &DolphinUrlNavigatorsController::slotPlacesPanelVisibilityChanged);
+ connect(placesDock, &DolphinDockWidget::visibilityChanged, this, &DolphinMainWindow::slotPlacesPanelVisibilityChanged);
connect(this, &DolphinMainWindow::settingsChanged, m_placesPanel, &PlacesPanel::readSettings);
connect(m_placesPanel, &PlacesPanel::storageTearDownRequested, this, &DolphinMainWindow::slotStorageTearDownFromPlacesRequested);
connect(m_placesPanel, &PlacesPanel::storageTearDownExternallyRequested, this, &DolphinMainWindow::slotStorageTearDownExternallyRequested);
actionShowAllPlaces->setWhatsThis(i18nc("@info:whatsthis",
"This displays "
"all places in the places panel that have been hidden. They will "
- "appear semi-transparent unless you uncheck their hide property."));
+ "appear semi-transparent and allow you to uncheck their \"Hide\" property."));
- connect(actionShowAllPlaces, &QAction::triggered, this, [actionShowAllPlaces, this](bool checked) {
+ connect(actionShowAllPlaces, &QAction::triggered, this, [this](bool checked) {
m_placesPanel->setShowAll(checked);
});
connect(m_placesPanel, &PlacesPanel::allPlacesShownChanged, actionShowAllPlaces, &QAction::setChecked);
"</interface> to display it again.</para>")
+ panelWhatsThis);
+ QAction *focusPlacesPanel = actionCollection()->addAction(QStringLiteral("focus_places_panel"));
+ focusPlacesPanel->setText(i18nc("@action:inmenu View", "Focus Places Panel"));
+ focusPlacesPanel->setToolTip(i18nc("@info:tooltip", "Move keyboard focus to and from the Places panel."));
+ focusPlacesPanel->setIcon(QIcon::fromTheme(QStringLiteral("swap-panels")));
+ actionCollection()->setDefaultShortcut(focusPlacesPanel, Qt::CTRL | Qt::Key_P);
+ connect(focusPlacesPanel, &QAction::triggered, this, &DolphinMainWindow::togglePlacesPanelFocus);
+
// Add actions into the "Panels" menu
KActionMenu *panelsMenu = new KActionMenu(i18nc("@action:inmenu View", "Show Panels"), this);
actionCollection()->addAction(QStringLiteral("panels"), panelsMenu);
panelsMenu->addAction(ac->action(QStringLiteral("show_folders_panel")));
panelsMenu->addAction(ac->action(QStringLiteral("show_terminal_panel")));
panelsMenu->addSeparator();
- panelsMenu->addAction(actionShowAllPlaces);
panelsMenu->addAction(lockLayoutAction);
+ panelsMenu->addSeparator();
+ panelsMenu->addAction(actionShowAllPlaces);
+ panelsMenu->addAction(focusPlacesPanel);
+ panelsMenu->addAction(ac->action(QStringLiteral("focus_terminal_panel")));
- connect(panelsMenu->menu(), &QMenu::aboutToShow, this, [actionShowAllPlaces, this] {
+ connect(panelsMenu->menu(), &QMenu::aboutToShow, this, [actionShowAllPlaces] {
actionShowAllPlaces->setEnabled(DolphinPlacesModelSingleton::instance().placesModel()->hiddenCount());
});
}
QAction *addToPlacesAction = col->action(QStringLiteral("add_to_places"));
QAction *copyToOtherViewAction = col->action(QStringLiteral("copy_to_inactive_split_view"));
QAction *moveToOtherViewAction = col->action(QStringLiteral("move_to_inactive_split_view"));
- QAction *copyLocation = col->action(QString("copy_location"));
+ QAction *copyLocation = col->action(QStringLiteral("copy_location"));
if (list.isEmpty()) {
stateChanged(QStringLiteral("has_no_selection"));
const bool enableMoveToTrash = capabilitiesSource.isLocal() && capabilitiesSource.supportsMoving();
renameAction->setEnabled(capabilitiesSource.supportsMoving());
- moveToTrashAction->setEnabled(enableMoveToTrash);
+ m_disabledActionNotifier->setDisabledReason(renameAction, i18nc("@info", "Cannot rename: You do not have permission to rename items in this folder."));
deleteAction->setEnabled(capabilitiesSource.supportsDeleting());
- deleteWithTrashShortcut->setEnabled(capabilitiesSource.supportsDeleting() && !enableMoveToTrash);
+ m_disabledActionNotifier->setDisabledReason(deleteAction,
+ i18nc("@info", "Cannot delete: You do not have permission to remove items from this folder."));
cutAction->setEnabled(capabilitiesSource.supportsMoving());
+ m_disabledActionNotifier->setDisabledReason(cutAction, i18nc("@info", "Cannot cut: You do not have permission to move items from this folder."));
copyLocation->setEnabled(list.length() == 1);
showTarget->setEnabled(list.length() == 1 && list.at(0).isLink());
duplicateAction->setEnabled(capabilitiesSource.supportsWriting());
+ m_disabledActionNotifier->setDisabledReason(duplicateAction,
+ i18nc("@info", "Cannot duplicate here: You do not have permission to create items in this folder."));
+
+ if (enableMoveToTrash) {
+ moveToTrashAction->setEnabled(true);
+ deleteWithTrashShortcut->setEnabled(false);
+ m_disabledActionNotifier->clearDisabledReason(deleteWithTrashShortcut);
+ } else {
+ moveToTrashAction->setEnabled(false);
+ deleteWithTrashShortcut->setEnabled(capabilitiesSource.supportsDeleting());
+ m_disabledActionNotifier->setDisabledReason(deleteWithTrashShortcut,
+ i18nc("@info", "Cannot delete: You do not have permission to remove items from this folder."));
+ }
}
- if (m_tabWidget->currentTabPage()->splitViewEnabled() && !list.isEmpty()) {
+ if (!m_tabWidget->currentTabPage()->splitViewEnabled()) {
+ // No need to set the disabled reason here, as it's obvious to the user that the reason is the split view being disabled.
+ copyToOtherViewAction->setEnabled(false);
+ m_disabledActionNotifier->clearDisabledReason(copyToOtherViewAction);
+ moveToOtherViewAction->setEnabled(false);
+ m_disabledActionNotifier->clearDisabledReason(moveToOtherViewAction);
+ } else if (list.isEmpty()) {
+ copyToOtherViewAction->setEnabled(false);
+ m_disabledActionNotifier->setDisabledReason(copyToOtherViewAction, i18nc("@info", "Cannot copy to other view: No files selected."));
+ moveToOtherViewAction->setEnabled(false);
+ m_disabledActionNotifier->setDisabledReason(moveToOtherViewAction, i18nc("@info", "Cannot move to other view: No files selected."));
+ } else {
DolphinTabPage *tabPage = m_tabWidget->currentTabPage();
KFileItem capabilitiesDestination;
return item.url().adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash) != destUrl;
});
- copyToOtherViewAction->setEnabled(capabilitiesDestination.isWritable() && allNotTargetOrigin);
- moveToOtherViewAction->setEnabled((list.isEmpty() || capabilitiesSource.supportsMoving()) && capabilitiesDestination.isWritable()
- && allNotTargetOrigin);
- } else {
- copyToOtherViewAction->setEnabled(false);
- moveToOtherViewAction->setEnabled(false);
+ if (!allNotTargetOrigin) {
+ copyToOtherViewAction->setEnabled(false);
+ m_disabledActionNotifier->setDisabledReason(copyToOtherViewAction,
+ i18nc("@info", "Cannot copy to other view: The other view already contains these items."));
+ moveToOtherViewAction->setEnabled(false);
+ m_disabledActionNotifier->setDisabledReason(moveToOtherViewAction,
+ i18nc("@info", "Cannot move to other view: The other view already contains these items."));
+ } else if (!capabilitiesDestination.isWritable()) {
+ copyToOtherViewAction->setEnabled(false);
+ m_disabledActionNotifier->setDisabledReason(
+ copyToOtherViewAction,
+ i18nc("@info", "Cannot copy to other view: You do not have permission to write into the destination folder."));
+ moveToOtherViewAction->setEnabled(false);
+ m_disabledActionNotifier->setDisabledReason(
+ moveToOtherViewAction,
+ i18nc("@info", "Cannot move to other view: You do not have permission to write into the destination folder."));
+ } else {
+ copyToOtherViewAction->setEnabled(true);
+ moveToOtherViewAction->setEnabled(capabilitiesSource.supportsMoving());
+ m_disabledActionNotifier->setDisabledReason(
+ moveToOtherViewAction,
+ i18nc("@info", "Cannot move to other view: You do not have permission to move items from this folder."));
+ }
}
}
QAction *toggleFilterBarAction = actionCollection()->action(QStringLiteral("toggle_filter"));
toggleFilterBarAction->setChecked(m_activeViewContainer->isFilterBarVisible());
- updateSplitAction();
+ updateSplitActions();
}
void DolphinMainWindow::updateGoActions()
updateWindowTitle();
}
- updateSplitAction();
+ updateSplitActions();
Q_EMIT settingsChanged();
}
{
connect(container, &DolphinViewContainer::showFilterBarChanged, this, &DolphinMainWindow::updateFilterBarAction);
connect(container, &DolphinViewContainer::writeStateChanged, this, &DolphinMainWindow::slotWriteStateChanged);
- connect(container, &DolphinViewContainer::searchModeEnabledChanged, this, &DolphinMainWindow::updateSearchAction);
+ slotWriteStateChanged(container->view()->isFolderWritable());
+ connect(container, &DolphinViewContainer::searchBarVisibilityChanged, this, &DolphinMainWindow::updateSearchAction);
connect(container, &DolphinViewContainer::captionChanged, this, &DolphinMainWindow::updateWindowTitle);
connect(container, &DolphinViewContainer::tabRequested, this, &DolphinMainWindow::openNewTab);
connect(container, &DolphinViewContainer::activeTabRequested, this, &DolphinMainWindow::openNewTabAndActivate);
- const QAction *toggleSearchAction = actionCollection()->action(QStringLiteral("toggle_search"));
- connect(toggleSearchAction, &QAction::triggered, container, &DolphinViewContainer::setSearchModeEnabled);
-
// Make the toggled state of the selection mode actions visually follow the selection mode state of the view.
auto toggleSelectionModeAction = actionCollection()->action(QStringLiteral("toggle_selection_mode"));
toggleSelectionModeAction->setChecked(m_activeViewContainer->isSelectionModeEnabled());
connect(view, &DolphinView::goForwardRequested, this, &DolphinMainWindow::goForward);
connect(view, &DolphinView::urlActivated, this, &DolphinMainWindow::handleUrl);
connect(view, &DolphinView::goUpRequested, this, &DolphinMainWindow::goUp);
+ connect(view, &DolphinView::doubleClickViewBackground, this, &DolphinMainWindow::slotDoubleClickViewBackground);
connect(container->urlNavigatorInternalWithHistory(), &KUrlNavigator::urlChanged, this, &DolphinMainWindow::changeUrl);
connect(container->urlNavigatorInternalWithHistory(), &KUrlNavigator::historyChanged, this, &DolphinMainWindow::updateHistory);
connect(navigator, &KUrlNavigator::newWindowRequested, this, &DolphinMainWindow::openNewWindow);
}
-void DolphinMainWindow::updateSplitAction()
+void DolphinMainWindow::updateSplitActions()
{
- QAction *splitAction = actionCollection()->action(QStringLiteral("split_view"));
+ QAction *popoutSplitAction = actionCollection()->action(QStringLiteral("popout_split_view"));
+
+ auto setActionPopupMode = [this](KActionMenu *action, QToolButton::ToolButtonPopupMode popupMode) {
+ action->setPopupMode(popupMode);
+ if (auto *buttonForAction = qobject_cast<QToolButton *>(toolBar()->widgetForAction(action))) {
+ buttonForAction->setPopupMode(popupMode);
+ }
+ };
+
const DolphinTabPage *tabPage = m_tabWidget->currentTabPage();
if (tabPage->splitViewEnabled()) {
if (GeneralSettings::closeActiveSplitView() ? tabPage->primaryViewActive() : !tabPage->primaryViewActive()) {
- splitAction->setText(i18nc("@action:intoolbar Close left view", "Close"));
- splitAction->setToolTip(i18nc("@info", "Close left view"));
- splitAction->setIcon(QIcon::fromTheme(QStringLiteral("view-left-close")));
+ m_splitViewAction->setText(i18nc("@action:intoolbar Close left view", "Close"));
+ m_splitViewAction->setToolTip(i18nc("@info", "Close left view"));
+ m_splitViewAction->setIcon(QIcon::fromTheme(QStringLiteral("view-left-close")));
+ m_splitViewMenuAction->setText(i18nc("@action:inmenu Close left view", "Close Left View"));
+
+ popoutSplitAction->setText(i18nc("@action:intoolbar Move left view to a new window", "Pop out Left View"));
+ popoutSplitAction->setToolTip(i18nc("@info", "Move left view to a new window"));
} else {
- splitAction->setText(i18nc("@action:intoolbar Close right view", "Close"));
- splitAction->setToolTip(i18nc("@info", "Close right view"));
- splitAction->setIcon(QIcon::fromTheme(QStringLiteral("view-right-close")));
+ m_splitViewAction->setText(i18nc("@action:intoolbar Close right view", "Close"));
+ m_splitViewAction->setToolTip(i18nc("@info", "Close right view"));
+ m_splitViewAction->setIcon(QIcon::fromTheme(QStringLiteral("view-right-close")));
+ m_splitViewMenuAction->setText(i18nc("@action:inmenu Close left view", "Close Right View"));
+
+ popoutSplitAction->setText(i18nc("@action:intoolbar Move right view to a new window", "Pop out Right View"));
+ popoutSplitAction->setToolTip(i18nc("@info", "Move right view to a new window"));
+ }
+ popoutSplitAction->setEnabled(true);
+ if (!m_splitViewAction->menu()) {
+ setActionPopupMode(m_splitViewAction, QToolButton::MenuButtonPopup);
+ m_splitViewAction->setMenu(new QMenu);
+ m_splitViewAction->addAction(popoutSplitAction);
}
} else {
- splitAction->setText(i18nc("@action:intoolbar Split view", "Split"));
- splitAction->setToolTip(i18nc("@info", "Split view"));
- splitAction->setIcon(QIcon::fromTheme(QStringLiteral("view-right-new")));
+ m_splitViewAction->setText(i18nc("@action:intoolbar Split view", "Split"));
+ m_splitViewMenuAction->setText(m_splitViewAction->text());
+ m_splitViewAction->setToolTip(i18nc("@info", "Split view"));
+ m_splitViewAction->setIcon(QIcon::fromTheme(QStringLiteral("view-split-left-right")));
+ popoutSplitAction->setText(i18nc("@action:intoolbar Move view in focus to a new window", "Pop out"));
+ popoutSplitAction->setEnabled(false);
+ if (m_splitViewAction->menu()) {
+ m_splitViewAction->removeAction(popoutSplitAction);
+ m_splitViewAction->menu()->deleteLater();
+ m_splitViewAction->setMenu(nullptr);
+ setActionPopupMode(m_splitViewAction, QToolButton::DelayedPopup);
+ }
}
+
+ // Update state from toolbar action
+ m_splitViewMenuAction->setToolTip(m_splitViewAction->toolTip());
+ m_splitViewMenuAction->setIcon(m_splitViewAction->icon());
}
void DolphinMainWindow::updateAllowedToolbarAreas()
}
}
+void DolphinMainWindow::updateNavigatorsBackground()
+{
+ auto navigators = static_cast<DolphinNavigatorsWidgetAction *>(actionCollection()->action(QStringLiteral("url_navigators")));
+ navigators->setBackgroundEnabled(navigators->isInToolbar());
+}
+
bool DolphinMainWindow::isKompareInstalled() const
{
static bool initialized = false;
return installed;
}
-void DolphinMainWindow::createPanelAction(const QIcon &icon, const QKeySequence &shortcut, QAction *dockAction, const QString &actionName)
+void DolphinMainWindow::createPanelAction(const QIcon &icon, const QKeySequence &shortcut, QDockWidget *dockWidget, const QString &actionName)
{
- QAction *panelAction = actionCollection()->addAction(actionName);
- panelAction->setCheckable(true);
- panelAction->setChecked(dockAction->isChecked());
- panelAction->setText(dockAction->text());
- panelAction->setIcon(icon);
+ auto dockAction = dockWidget->toggleViewAction();
dockAction->setIcon(icon);
dockAction->setEnabled(true);
- actionCollection()->setDefaultShortcut(panelAction, shortcut);
- connect(panelAction, &QAction::triggered, dockAction, &QAction::trigger);
- connect(dockAction, &QAction::toggled, panelAction, &QAction::setChecked);
+ QAction *panelAction = actionCollection()->addAction(actionName, dockAction);
+ actionCollection()->setDefaultShortcut(panelAction, shortcut);
}
// clang-format off
void DolphinMainWindow::setupWhatsThis()
m_tabWidget->currentTabPage()->insertNavigatorsWidget(navigators);
}
updateAllowedToolbarAreas();
+ updateNavigatorsBackground();
(static_cast<KHamburgerMenu *>(actionCollection()->action(KStandardAction::name(KStandardAction::HamburgerMenu))))->hideActionsOf(toolBar());
}
-void DolphinMainWindow::focusTerminalPanel()
+void DolphinMainWindow::toggleTerminalPanelFocus()
{
- if (m_terminalPanel->isVisible()) {
- if (m_terminalPanel->terminalHasFocus()) {
- m_activeViewContainer->view()->setFocus(Qt::FocusReason::ShortcutFocusReason);
- actionCollection()->action(QStringLiteral("focus_terminal_panel"))->setText(i18nc("@action:inmenu Tools", "Focus Terminal Panel"));
- } else {
- m_terminalPanel->setFocus(Qt::FocusReason::ShortcutFocusReason);
- actionCollection()->action(QStringLiteral("focus_terminal_panel"))->setText(i18nc("@action:inmenu Tools", "Defocus Terminal Panel"));
- }
- } else {
- actionCollection()->action(QStringLiteral("show_terminal_panel"))->trigger();
+ if (!m_terminalPanel->isVisible()) {
+ actionCollection()->action(QStringLiteral("show_terminal_panel"))->trigger(); // Also moves focus to the panel.
actionCollection()->action(QStringLiteral("focus_terminal_panel"))->setText(i18nc("@action:inmenu Tools", "Defocus Terminal Panel"));
+ return;
}
+
+ if (m_terminalPanel->terminalHasFocus()) {
+ m_activeViewContainer->view()->setFocus(Qt::FocusReason::ShortcutFocusReason);
+ actionCollection()->action(QStringLiteral("focus_terminal_panel"))->setText(i18nc("@action:inmenu Tools", "Focus Terminal Panel"));
+ return;
+ }
+
+ m_terminalPanel->setFocus(Qt::FocusReason::ShortcutFocusReason);
+ actionCollection()->action(QStringLiteral("focus_terminal_panel"))->setText(i18nc("@action:inmenu Tools", "Defocus Terminal Panel"));
+}
+
+void DolphinMainWindow::togglePlacesPanelFocus()
+{
+ if (!m_placesPanel->isVisible()) {
+ actionCollection()->action(QStringLiteral("show_places_panel"))->trigger(); // Also moves focus to the panel.
+ actionCollection()->action(QStringLiteral("focus_places_panel"))->setText(i18nc("@action:inmenu View", "Defocus Terminal Panel"));
+ return;
+ }
+
+ if (m_placesPanel->hasFocus()) {
+ m_activeViewContainer->view()->setFocus(Qt::FocusReason::ShortcutFocusReason);
+ actionCollection()->action(QStringLiteral("focus_places_panel"))->setText(i18nc("@action:inmenu View", "Focus Places Panel"));
+ return;
+ }
+
+ m_placesPanel->setFocus(Qt::FocusReason::ShortcutFocusReason);
+ actionCollection()->action(QStringLiteral("focus_places_panel"))->setText(i18nc("@action:inmenu View", "Defocus Places Panel"));
}
DolphinMainWindow::UndoUiInterface::UndoUiInterface()
DolphinMainWindow *mainWin = qobject_cast<DolphinMainWindow *>(parentWidget());
if (mainWin) {
DolphinViewContainer *container = mainWin->activeViewContainer();
- container->showMessage(job->errorString(), DolphinViewContainer::Error);
+ container->showMessage(job->errorString(), KMessageWidget::Error);
} else {
KIO::FileUndoManager::UiInterface::jobError(job);
}
return m_tabWidget->isItemVisibleInAnyView(QUrl::fromUserInput(urlOfItem));
}
+void DolphinMainWindow::slotDoubleClickViewBackground(Qt::MouseButton button)
+{
+ if (button != Qt::MouseButton::LeftButton) {
+ // only handle left mouse button for now
+ return;
+ }
+
+ GeneralSettings *settings = GeneralSettings::self();
+ QString clickAction = settings->doubleClickViewAction();
+
+ DolphinView *view = activeViewContainer()->view();
+ if (view == nullptr || clickAction == "none") {
+ return;
+ }
+
+ if (clickAction == customCommand) {
+ // run custom command set by the user
+ QString path = view->url().toLocalFile();
+ QString clickCustomAction = settings->doubleClickViewCustomAction();
+ clickCustomAction.replace("{path}", path.prepend('"').append('"'));
+
+ m_job = new KIO::CommandLauncherJob(clickCustomAction);
+ m_job->setUiDelegate(new KDialogJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this));
+ m_job->start();
+
+ } else {
+ // get the action set by the user and trigger it
+ const KActionCollection *actions = actionCollection();
+ QAction *action = actions->action(clickAction);
+ if (action == nullptr) {
+ qCWarning(DolphinDebug) << QStringLiteral("Double-click view: action `%1` was not found").arg(clickAction);
+ return;
+ }
+ action->trigger();
+ }
+}
+
#include "moc_dolphinmainwindow.cpp"