Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Context Menu operations on Folder as Workspace tree #556

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
109 changes: 105 additions & 4 deletions src/NotepadNext/docks/FolderAsWorkspaceDock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,27 @@


#include "FolderAsWorkspaceDock.h"

#include <QFileSystemModel>
#include <QMessageBox>

#include "ApplicationSettings.h"
#include "MainWindow.h"
#include "ui_FolderAsWorkspaceDock.h"

#include <QFileSystemModel>
QString NEW_DIR_TEMPLATE("dir_%1");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd have to look deeper how this is used but might make sense to keep it inside the class so this can be translated.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


ApplicationSetting<QString> rootPathSetting{"FolderAsWorkspace/RootPath"};

FolderAsWorkspaceDock::FolderAsWorkspaceDock(QWidget *parent) :
FolderAsWorkspaceDock::FolderAsWorkspaceDock(MainWindow *parent) :
QDockWidget(parent),
ui(new Ui::FolderAsWorkspaceDock),
model(new QFileSystemModel(this))
window(parent)
{
ui = new Ui::FolderAsWorkspaceDock;
model = new QFileSystemModel(this);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any particular reason these couldn't be kept in the initializer list?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was warning about -Wreorder I don't get how to remove

ui->setupUi(this);

model->setReadOnly(false);
ui->treeView->setModel(model);
ui->treeView->header()->hideSection(1);
ui->treeView->header()->hideSection(2);
Expand All @@ -42,6 +49,8 @@ FolderAsWorkspaceDock::FolderAsWorkspaceDock(QWidget *parent) :
emit fileDoubleClicked(model->filePath(index));
}
});
connect(ui->treeView, &QTreeView::customContextMenuRequested, this, &FolderAsWorkspaceDock::onCustomContextMenu);
connect(model, &QFileSystemModel::fileRenamed, this, &FolderAsWorkspaceDock::onFileRenamed);

ApplicationSettings settings;
setRootPath(settings.get(rootPathSetting));
Expand All @@ -65,3 +74,95 @@ QString FolderAsWorkspaceDock::rootPath() const
{
return model->rootPath();
}

void FolderAsWorkspaceDock::onCustomContextMenu(const QPoint &point)
{
QModelIndex index = ui->treeView->indexAt(point);
if (!index.isValid()) {
lastSelectedItem = model->index(rootPath());
ui->menuEmpty->exec(ui->treeView->viewport()->mapToGlobal(point));
return;
}
lastSelectedItem = index;
if (model->isDir(index)) {
ui->menuDirectory->exec(ui->treeView->viewport()->mapToGlobal(point));
}
else {
ui->menuFile->exec(ui->treeView->viewport()->mapToGlobal(point));
}
}

void FolderAsWorkspaceDock::on_actionSaveHere_triggered()
{
QDir parentDir(model->filePath(lastSelectedItem));
auto doc = window->currentEditor();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To keep consistent with the rest of the code base, rename doc to editor.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

QString dstName(parentDir.absoluteFilePath(doc->getName()));

if (doc->saveAs(dstName) != QFileDevice::NoError)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In most cases I prefer the "happy path" to be the first part (e.g. switching this to ==). Just helps with readability since you have to skip over the failure case and look at the else clause to see the natural flow.

Also, this is a bit of a code 'smell' that the FolderAsWorkspace is messing with the lower editor API. Might be possible to use something like window->saveFileAs(...) so that incase there are errors it will be handled the same way as a normal file would if it failed.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I separate UI questions inside MainWindow and use them here instead.

{
qWarning("Unable to save %s", dstName.toUtf8().constData());
}
else
{
auto newItem = model->index(dstName);
if (!doc->isFile()) {
doc->setFileInfo(dstName);
}
ui->treeView->setCurrentIndex(newItem);
ui->treeView->edit(newItem);
}
}

void FolderAsWorkspaceDock::on_actionNewFolder_triggered()
{
QDir parentDir(model->filePath(lastSelectedItem));
int i = 1;

for (;parentDir.exists(NEW_DIR_TEMPLATE.arg(i)); i++) {
// Intentional
}
QString dstName = NEW_DIR_TEMPLATE.arg(i);

auto newItem = model->mkdir(lastSelectedItem, dstName);
if (!newItem.isValid()) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switch this condition as well.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

qWarning("Unable to create %s", dstName.toUtf8().constData());
}
else {
ui->treeView->setCurrentIndex(newItem);
ui->treeView->edit(newItem);
}
}

void FolderAsWorkspaceDock::on_actionRename_triggered()
{
ui->treeView->setCurrentIndex(lastSelectedItem);
ui->treeView->edit(lastSelectedItem);
}

void FolderAsWorkspaceDock::onFileRenamed(const QString &path, const QString &oldName, const QString &newName)
{
QDir dir(path);
QString fileName = dir.absoluteFilePath(oldName);
for(auto &&editor : window->editors())
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might also leverage something in window...might need something new like renameFile to take an editor.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is some mess between MainWindow <-> EditorManager and also ScintillaNext as a view vs some nonexistent Content which could be file or i.e. temporary buffer

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved parts into MainWindow

{
if (editor->isFile() && (editor->getFilePath() == fileName))
{
editor->setName(newName);
}
}
}

void FolderAsWorkspaceDock::on_actionDelete_triggered()
{
QString path(model->filePath(lastSelectedItem));
QMessageBox::StandardButton reply = QMessageBox::question(this, tr("Delete Item"),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably use window->moveFileToTrash() here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented both options but I have no windows box nearby to test on windows.

tr("Are you sure you want to delete <b>%1</b>?").arg(path));

if (reply == QMessageBox::Yes)
{
if (!model->remove(lastSelectedItem))
{
qWarning("Unable to delete %s", path.toUtf8().constData());
}
}
}
16 changes: 15 additions & 1 deletion src/NotepadNext/docks/FolderAsWorkspaceDock.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,21 @@
#define FOLDERASWORKSPACEDOCK_H

#include <QDockWidget>
#include <QModelIndex>

namespace Ui {
class FolderAsWorkspaceDock;
}

class QFileSystemModel;
class MainWindow;

class FolderAsWorkspaceDock : public QDockWidget
{
Q_OBJECT

public:
explicit FolderAsWorkspaceDock(QWidget *parent = nullptr);
explicit FolderAsWorkspaceDock(MainWindow *parent = nullptr);
~FolderAsWorkspaceDock();

void setRootPath(const QString dir);
Expand All @@ -42,10 +44,22 @@ class FolderAsWorkspaceDock : public QDockWidget
signals:
void fileDoubleClicked(const QString &filePath);

private slots:
void on_actionSaveHere_triggered();
void on_actionNewFolder_triggered();
void on_actionRename_triggered();
void on_actionDelete_triggered();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Qt doesn't recommend the auto connect slots and I haven't used them elsewhere throughout the codebase, but for now it is fine. It can be cleaned up later.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

M.. why/where? connectSlotsByName has no such notices at any version.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Author

@SimLV SimLV May 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is good scripting usage to automate creation of all these boilerplate as a lua script inside NotepadNext.
For some future version obviously

local luaxml = require 'LuaXML'

local file_name = nn.activeEditor.path:gsub('.ui$')
local cpp_editor = nn.findEditorByPath(file_name .. '.cpp')
local h_editor = nn.findEditorByPath(file_name .. '.h')

assert(cpp_editor and h_editor, 'Please open corresponding .cpp and .h')
local cpp_bookmark = cpp_editor:findNamedBookmark('ctor signals')
local h_bookmark = cpp_editor:findNamedBookmark('slots')
assert(cpp_bookmark and h_bookmark, 'Please set "ctor signals" and "slots" bookmarks')

local items = {}
local tree = xml.load(file_name .. '.ui')
tree:iterate(function(el)
  table.insert(items, el.name)
end, 'action', nil, nil, true)

local class_name = find_class_name(cpp_bookmark) -- TODO

for i,name in ipairs(items) do
  local method_name = 'on'.. name:gsub('^[a-z]', function(s) return s:upper() end) .. 'Triggered'
  local full_method = 'void '..method_name..'()'
  if not h_editor:findText(full_method) then
   h_bookmark:appendText(full_method .. ';\n');
   cpp_bookmark:append('connect(ui->'..name..', &QAction::triggered, this, &'.. class_name.. '::'..method_name..');')
   cpp_editor:append(full_method..'\n{\n}\n')
  end
end

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting idea, though I don't see Lua as a long-term solution. It was originally just added for debugging/testing purposes

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see lua as very good "Glue language" to customize and script stuff. I am going to use NotepadNext as very customizable light editor (instead of NotepadQQ and ZeroBraneStudio) on projects where I don't need big IDE like PyCharm


void onCustomContextMenu(const QPoint &point);
void onFileRenamed(const QString &path, const QString &oldName, const QString &newName);

private:
Ui::FolderAsWorkspaceDock *ui;

MainWindow *window;
QFileSystemModel *model;

QModelIndex lastSelectedItem;
};

#endif // FOLDERASWORKSPACEDOCK_H
60 changes: 59 additions & 1 deletion src/NotepadNext/docks/FolderAsWorkspaceDock.ui
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,77 @@
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QMenu" name="menuFile">
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can QtCreator create a QMenu in the ui file? or some other method (e.g. manually)? I'm just interested since I've never seen this done before :)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can't. I googled for it but at least Qt 5 can't. For me it looks like minor inconvenience everyone accustomed to.

<property name="title">
<string>File Menu</string>
</property>
<addaction name="actionRename"/>
<addaction name="actionDelete"/>
</widget>
</item>
<item>
<widget class="QMenu" name="menuDirectory">
<property name="title">
<string>Folder Menu</string>
</property>
<addaction name="actionRename"/>
<addaction name="actionDelete"/>
<addaction name="separator"/>
<addaction name="actionNewFolder"/>
<addaction name="actionSaveHere"/>
</widget>
</item>
<item>
<widget class="QMenu" name="menuEmpty">
<property name="title">
<string>Space Menu</string>
</property>
<addaction name="actionNewFolder"/>
<addaction name="actionSaveHere"/>
</widget>
</item>
<item>
<widget class="QTreeView" name="treeView">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="editTriggers">
<set>QAbstractItemView::EditKeyPressed</set>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
</layout>
</widget>
<action name="actionSaveHere">
<property name="text">
<string>&amp;Save Here</string>
</property>
</action>
<action name="actionNewFolder">
<property name="text">
<string>New &amp;Folder</string>
</property>
</action>
<action name="actionRename">
<property name="text">
<string>&amp;Rename</string>
</property>
</action>
<action name="actionDelete">
<property name="text">
<string>&amp;Delete</string>
</property>
</action>
</widget>
<resources/>
<resources>
<include location="../resources.qrc"/>
</resources>
<connections/>
</ui>