From: Christian Schwarz Date: Mon, 3 Feb 2025 18:23:16 +0000 (+0000) Subject: implement shortcut action for file creation X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/commitdiff_plain/f81d73a7f3a851dc755ef0cd1fd2a2507587a900 implement shortcut action for file creation BUG: 462899 --- diff --git a/src/dolphincontextmenu.cpp b/src/dolphincontextmenu.cpp index e2f7e326b..354111a01 100644 --- a/src/dolphincontextmenu.cpp +++ b/src/dolphincontextmenu.cpp @@ -32,6 +32,7 @@ #include #include #include +#include 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()); diff --git a/src/dolphinmainwindow.cpp b/src/dolphinmainwindow.cpp index d9438ea09..a5dd3f9d3 100644 --- a/src/dolphinmainwindow.cpp +++ b/src/dolphinmainwindow.cpp @@ -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")); diff --git a/src/dolphinmainwindow.h b/src/dolphinmainwindow.h index ceda3cb73..ecc84b971 100644 --- a/src/dolphinmainwindow.h +++ b/src/dolphinmainwindow.h @@ -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); diff --git a/src/dolphinnewfilemenu.cpp b/src/dolphinnewfilemenu.cpp index e3aff9762..2ccd7dd69 100644 --- a/src/dolphinnewfilemenu.cpp +++ b/src/dolphinnewfilemenu.cpp @@ -12,10 +12,11 @@ #include -DolphinNewFileMenu::DolphinNewFileMenu(QAction *createDirAction, QObject *parent) +DolphinNewFileMenu::DolphinNewFileMenu(QAction *createDirAction, QAction *createFileAction, QObject *parent) : KNewFileMenu(parent) { setNewFolderShortcutAction(createDirAction); + setNewFileShortcutAction(createFileAction); DolphinNewFileMenuObserver::instance().attach(this); } diff --git a/src/dolphinnewfilemenu.h b/src/dolphinnewfilemenu.h index 5538c9265..d5235db9b 100644 --- a/src/dolphinnewfilemenu.h +++ b/src/dolphinnewfilemenu.h @@ -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: diff --git a/src/dolphinpart.cpp b/src/dolphinpart.cpp index a79b650c5..a7f0d85cd 100644 --- a/src/dolphinpart.cpp +++ b/src/dolphinpart.cpp @@ -45,6 +45,7 @@ #include #include +#include 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); diff --git a/src/settings/viewmodes/generalviewsettingspage.cpp b/src/settings/viewmodes/generalviewsettingspage.cpp index 00bbae296..7caffe0f9 100644 --- a/src/settings/viewmodes/generalviewsettingspage.cpp +++ b/src/settings/viewmodes/generalviewsettingspage.cpp @@ -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", diff --git a/src/tests/dolphinmainwindowtest.cpp b/src/tests/dolphinmainwindowtest.cpp index b22afa142..d7cb763fd 100644 --- a/src/tests/dolphinmainwindowtest.cpp +++ b/src/tests/dolphinmainwindowtest.cpp @@ -30,6 +30,10 @@ #include #include +#include +#include +#include +#include #include #include @@ -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{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{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("activeViewUrl"); diff --git a/src/views/dolphinviewactionhandler.cpp b/src/views/dolphinviewactionhandler.cpp index 88d5c015b..4d5dba070 100644 --- a/src/views/dolphinviewactionhandler.cpp +++ b/src/views/dolphinviewactionhandler.cpp @@ -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() diff --git a/src/views/dolphinviewactionhandler.h b/src/views/dolphinviewactionhandler.h index f36b3d1d0..20d62cf64 100644 --- a/src/views/dolphinviewactionhandler.h +++ b/src/views/dolphinviewactionhandler.h @@ -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);