]> cloud.milkyroute.net Git - dolphin.git/commitdiff
implement shortcut action for file creation
authorChristian Schwarz <cmrs@cmrs.io>
Mon, 3 Feb 2025 18:23:16 +0000 (18:23 +0000)
committerMéven Car <meven@kde.org>
Mon, 3 Feb 2025 18:23:16 +0000 (18:23 +0000)
BUG: 462899

src/dolphincontextmenu.cpp
src/dolphinmainwindow.cpp
src/dolphinmainwindow.h
src/dolphinnewfilemenu.cpp
src/dolphinnewfilemenu.h
src/dolphinpart.cpp
src/settings/viewmodes/generalviewsettingspage.cpp
src/tests/dolphinmainwindowtest.cpp
src/views/dolphinviewactionhandler.cpp
src/views/dolphinviewactionhandler.h

index e2f7e326bf89eaec7c47d3528f66872b785ad1db..354111a01d9b523a1698eeb28f56b4715fe2ac63 100644 (file)
@@ -32,6 +32,7 @@
 #include <QApplication>
 #include <QClipboard>
 #include <QKeyEvent>
+#include <QAction>
 
 DolphinContextMenu::DolphinContextMenu(DolphinMainWindow *parent,
                                        const KFileItem &fileInfo,
@@ -198,7 +199,9 @@ void DolphinContextMenu::addDirectoryItemContextMenu()
     addOpenWithActions();
 
     // set up 'Create New' menu
-    DolphinNewFileMenu *newFileMenu = new DolphinNewFileMenu(m_mainWindow->actionCollection()->action(QStringLiteral("create_dir")), m_mainWindow);
+    QAction *newDirAction = m_mainWindow->actionCollection()->action(QStringLiteral("create_dir"));
+    QAction *newFileAction = m_mainWindow->actionCollection()->action(QStringLiteral("create_file"));
+    DolphinNewFileMenu *newFileMenu = new DolphinNewFileMenu(newDirAction, newFileAction, m_mainWindow);
     newFileMenu->checkUpToDate();
     newFileMenu->setWorkingDirectory(m_fileInfo.url());
     newFileMenu->setEnabled(selectedItemsProps.supportsWriting());
index d9438ea091b5313be7eddf368c616afce2012e3e..a5dd3f9d345e2fb32698956fcc4b46707875cebc 100644 (file)
@@ -176,10 +176,16 @@ DolphinMainWindow::DolphinMainWindow()
     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);
@@ -816,6 +822,15 @@ void DolphinMainWindow::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()
 {
     close();
@@ -1446,6 +1461,8 @@ void DolphinMainWindow::slotWriteStateChanged(bool isFolderWritable)
     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."));
     });
@@ -1700,7 +1717,7 @@ void DolphinMainWindow::setupActions()
     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"));
index ceda3cb73f9fb8ed417f935f33536dbe665aca7d..ecc84b97123d0a0b4c0eb220f6e726134466649d 100644 (file)
@@ -296,6 +296,7 @@ private Q_SLOTS:
     void updateNewMenu();
 
     void createDirectory();
+    void createFile();
 
     /** Shows the error message in the status bar of the active view. */
     void showErrorMessage(const QString &message);
index e3aff97622851c47579a26c6ad321201da06a4ae..2ccd7dd694927ee339b7323e0a7073b2ca85ad46 100644 (file)
 
 #include <QAction>
 
-DolphinNewFileMenu::DolphinNewFileMenu(QAction *createDirAction, QObject *parent)
+DolphinNewFileMenu::DolphinNewFileMenu(QAction *createDirAction, QAction *createFileAction, QObject *parent)
     : KNewFileMenu(parent)
 {
     setNewFolderShortcutAction(createDirAction);
+    setNewFileShortcutAction(createFileAction);
     DolphinNewFileMenuObserver::instance().attach(this);
 }
 
index 5538c92659ee8c9ff187e4ba3f4817818fd744cc..d5235db9b5a23647e1bacaaabe80c2ed300042a8 100644 (file)
@@ -25,7 +25,7 @@ class DOLPHIN_EXPORT DolphinNewFileMenu : public KNewFileMenu
     Q_OBJECT
 
 public:
-    DolphinNewFileMenu(QAction *createDirAction, QObject *parent);
+    DolphinNewFileMenu(QAction *createDirAction, QAction *createFileAction, QObject *parent);
     ~DolphinNewFileMenu() override;
 
 Q_SIGNALS:
index a79b650c5b2a1afcfe7b883003d16a2b104cfdc3..a7f0d85cd2e939c7d266699370b1ee7cc0514d17 100644 (file)
@@ -45,6 +45,7 @@
 #include <QTextDocument>
 
 #include <KPluginFactory>
+#include <QAction>
 
 K_PLUGIN_CLASS_WITH_JSON(DolphinPart, "dolphinpart.json")
 
@@ -139,8 +140,9 @@ DolphinPart::~DolphinPart()
 void DolphinPart::createActions()
 {
     // Edit menu
-
-    m_newFileMenu = new DolphinNewFileMenu(actionCollection()->action(QStringLiteral("create_dir")), this);
+    QAction *newDirAction = actionCollection()->action(QStringLiteral("create_dir"));
+    QAction *newFileAction = actionCollection()->action(QStringLiteral("create_file"));
+    m_newFileMenu = new DolphinNewFileMenu(newDirAction, newFileAction, this);
     m_newFileMenu->setParentWidget(widget());
     connect(m_newFileMenu->menu(), &QMenu::aboutToShow, this, &DolphinPart::updateNewMenu);
 
index 00bbae29648017d64effda1cea3ad5d78407ff4a..7caffe0f9cd5f99b9af7bcdd455f5872c8e3bf36 100644 (file)
@@ -112,6 +112,7 @@ GeneralViewSettingsPage::GeneralViewSettingsPage(const QUrl &url, QWidget *paren
                                "edit_select_all",
                                "toggle_selection_mode",
                                "create_dir",
+                               "create_file",
                                "show_preview",
                                "show_hidden_files",
                                "show_in_groups",
index b22afa142f6022f692852cf305850624d19c6781..d7cb763fd8f6658f79bc9e8efe42b666df6c08a2 100644 (file)
 #include <QStandardPaths>
 #include <QTest>
 
+#include <kfileitem.h>
+#include <qapplication.h>
+#include <qkeysequence.h>
+#include <qnamespace.h>
 #include <set>
 #include <unordered_set>
 
@@ -49,6 +53,8 @@ private Q_SLOTS:
     void testOpenInNewTabTitle();
     void testNewFileMenuEnabled_data();
     void testNewFileMenuEnabled();
+    void testCreateFileAction();
+    void testCreateFileActionRequiresWritePermission();
     void testWindowTitle_data();
     void testWindowTitle();
     void testFocusLocationBar();
@@ -372,6 +378,74 @@ void DolphinMainWindowTest::testNewFileMenuEnabled()
     QTRY_COMPARE(newFileMenu->isEnabled(), expectedEnabled);
 }
 
+void DolphinMainWindowTest::testCreateFileAction()
+{
+    QScopedPointer<TestDir> testDir{new TestDir()};
+    QString testDirUrl(QDir::cleanPath(testDir->url().toString()));
+    m_mainWindow->openDirectories({testDirUrl}, false);
+    m_mainWindow->show();
+    QVERIFY(QTest::qWaitForWindowExposed(m_mainWindow.data()));
+    QVERIFY(m_mainWindow->isVisible());
+
+    QCOMPARE(m_mainWindow->m_activeViewContainer->view()->items().count(), 0);
+
+    auto createFileAction = m_mainWindow->actionCollection()->action(QStringLiteral("create_file"));
+    QTRY_COMPARE(createFileAction->isEnabled(), true);
+
+    createFileAction->setShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_N));
+
+    QSignalSpy createFileActionSpy(createFileAction, &QAction::triggered);
+
+    QTest::keyClick(QApplication::activeWindow(), Qt::Key_N, Qt::ControlModifier | Qt::AltModifier);
+
+    QTRY_COMPARE(createFileActionSpy.count(), 1);
+
+    QTRY_VERIFY(QApplication::activeModalWidget() != nullptr);
+
+    auto newFileDialog = QApplication::activeModalWidget()->focusWidget();
+    QTest::keyClick(newFileDialog, Qt::Key_X);
+    QTest::keyClick(newFileDialog, Qt::Key_Y);
+    QTest::keyClick(newFileDialog, Qt::Key_Z);
+    QTest::keyClick(newFileDialog, Qt::Key_Enter);
+
+    QTRY_COMPARE(m_mainWindow->m_activeViewContainer->view()->items().count(), 1);
+
+    QFile file(testDir->url().toLocalFile() + "/xyz.txt");
+    QVERIFY(file.exists());
+    QCOMPARE(file.size(), 0);
+}
+
+ void DolphinMainWindowTest::testCreateFileActionRequiresWritePermission()
+{
+    QScopedPointer<TestDir> testDir{new TestDir()};
+    QString testDirUrl(QDir::cleanPath(testDir->url().toString()));
+    auto testDirAsFile = QFile(testDir->url().toLocalFile());
+
+    // make test dir read only
+    QVERIFY(testDirAsFile.setPermissions(QFileDevice::ReadOwner));
+
+    m_mainWindow->openDirectories({testDirUrl}, false);
+    m_mainWindow->show();
+    QVERIFY(QTest::qWaitForWindowExposed(m_mainWindow.data()));
+    QVERIFY(m_mainWindow->isVisible());
+
+    QCOMPARE(m_mainWindow->m_activeViewContainer->view()->items().count(), 0);
+
+    auto createFileAction = m_mainWindow->actionCollection()->action(QStringLiteral("create_file"));
+    QTRY_COMPARE(createFileAction->isEnabled(), false);
+
+    createFileAction->setShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_N));
+    QTest::keyClick(QApplication::activeWindow(), Qt::Key_N, Qt::ControlModifier | Qt::AltModifier);
+
+    QTRY_COMPARE(QApplication::activeModalWidget(), nullptr);
+
+    QTRY_COMPARE(m_mainWindow->m_activeViewContainer->view()->items().count(), 0);
+
+    QTRY_COMPARE(createFileAction->isEnabled(), false);
+
+    QVERIFY(m_mainWindow->isVisible());
+}
+
 void DolphinMainWindowTest::testWindowTitle_data()
 {
     QTest::addColumn<QUrl>("activeViewUrl");
index 88d5c015b589d09c269a709143bec3049fddfde4..4d5dba070a2246cdb1cb9a74a6708853fee3b682 100644 (file)
@@ -84,6 +84,12 @@ void DolphinViewActionHandler::createActions(SelectionMode::ActionTextHelper *ac
     newDirAction->setEnabled(false); // Will be enabled in slotWriteStateChanged(bool) if the current URL is writable
     connect(newDirAction, &QAction::triggered, this, &DolphinViewActionHandler::createDirectoryTriggered);
 
+    QAction *newFileAction = m_actionCollection->addAction(QStringLiteral("create_file"));
+    newFileAction->setText(i18nc("@action", "Create File…"));
+    newFileAction->setIcon(QIcon::fromTheme(QStringLiteral("document-new")));
+    newFileAction->setEnabled(false); // Will be enabled in slotWriteStateChanged(bool) if the current URL is writable
+    connect(newFileAction, &QAction::triggered, this, &DolphinViewActionHandler::createFileTriggered);
+
     // File menu
 
     auto renameAction = KStandardAction::renameFile(this, &DolphinViewActionHandler::slotRename, m_actionCollection);
@@ -639,7 +645,9 @@ void DolphinViewActionHandler::slotHiddenFilesShownChanged(bool shown)
 
 void DolphinViewActionHandler::slotWriteStateChanged(bool isFolderWritable)
 {
-    m_actionCollection->action(QStringLiteral("create_dir"))->setEnabled(isFolderWritable && KProtocolManager::supportsMakeDir(currentView()->url()));
+    const bool supportsMakeDir = KProtocolManager::supportsMakeDir(currentView()->url());
+    m_actionCollection->action(QStringLiteral("create_dir"))->setEnabled(isFolderWritable && supportsMakeDir);
+    m_actionCollection->action(QStringLiteral("create_file"))->setEnabled(isFolderWritable);
 }
 
 KToggleAction *DolphinViewActionHandler::iconsModeAction()
index f36b3d1d04277a05463b8e559e373d32fd40662f..20d62cf64a0e61601495734f354315d9b222e10a 100644 (file)
@@ -87,6 +87,13 @@ Q_SIGNALS:
      */
     void createDirectoryTriggered();
 
+    /**
+     * Emitted if the user requested creating a new file.
+     * The receiver of the signal (DolphinMainWindow or DolphinPart) invokes
+     * the method createFile of their KNewFileMenu instance.
+     */
+    void createFileTriggered();
+
     /** Used to request either entering or leaving of selection mode */
     void selectionModeChangeTriggered(bool enabled, SelectionMode::BottomBar::Contents bottomBarContents = SelectionMode::BottomBar::Contents::GeneralContents);