#include "dolphinmainwindow.h"
+#include "admin/workerintegration.h"
#include "dolphin_generalsettings.h"
#include "dolphinbookmarkhandler.h"
#include "dolphincontextmenu.h"
#include <KColorSchemeManager>
#include <KConfig>
#include <KConfigGui>
+#include <KDialogJobUiDelegate>
#include <KDualAction>
#include <KFileItemListProperties>
#include <KIO/CommandLauncherJob>
#include <QTimer>
#include <QToolButton>
#include <QtConcurrentRun>
+#include <dolphindebug.h>
#include <algorithm>
, m_remoteEncoding(nullptr)
, m_settingsDialog()
, m_bookmarkHandler(nullptr)
+ , m_disabledActionNotifier(nullptr)
, m_lastHandleUrlOpenJob(nullptr)
, m_terminalPanel(nullptr)
, m_placesPanel(nullptr)
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, DolphinViewContainer::Warning);
+ });
+
setupDockWidgets();
setupGUI(Save | Create | ToolBar);
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::quit()
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);
}
// 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_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)
// setup 'View' menu
// (note that most of it is set up in DolphinViewActionHandler)
+ Admin::WorkerIntegration::createActAsAdminAction(actionCollection(), this);
+
m_splitViewAction = actionCollection()->add<KActionMenu>(QStringLiteral("split_view"));
m_splitViewMenuAction = actionCollection()->addAction(QStringLiteral("split_view_menu"));
connect(infoPanel, &InformationPanel::urlActivated, this, &DolphinMainWindow::handleUrl);
infoDock->setWidget(infoPanel);
- createPanelAction(QIcon::fromTheme(QStringLiteral("dialog-information")), Qt::Key_F11, infoDock, 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);
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."));
+ }
}
}
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);
return m_tabWidget->isItemVisibleInAnyView(QUrl::fromUserInput(urlOfItem));
}
+void DolphinMainWindow::slotDoubleClickViewBackground(Qt::MouseButton button)
+{
+ Q_UNUSED(button) // might be of use later
+
+ 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"