diff --git a/src/NotepadNext/ScintillaNext.cpp b/src/NotepadNext/ScintillaNext.cpp index 48058d35a..d905d4375 100644 --- a/src/NotepadNext/ScintillaNext.cpp +++ b/src/NotepadNext/ScintillaNext.cpp @@ -23,6 +23,7 @@ #include "uchardet.h" #include +#include #include #include #include @@ -92,6 +93,7 @@ ScintillaNext *ScintillaNext::fromFile(const QString &filePath, bool tryToCreate return Q_NULLPTR; } + qInfo() << Q_FUNC_INFO << filePath; editor->setFileInfo(filePath); return editor; @@ -231,18 +233,14 @@ QFileInfo ScintillaNext::getFileInfo() const return fileInfo; } -QString ScintillaNext::getPath() const -{ - Q_ASSERT(isFile()); - - return QDir::toNativeSeparators(fileInfo.canonicalPath()); -} - QString ScintillaNext::getFilePath() const { Q_ASSERT(isFile()); - return QDir::toNativeSeparators(fileInfo.canonicalFilePath()); + // All files should be absolute for now + // There is an unexpected behaviour because QFileInfo is refresh info from a disk sometimes + // So canonicalFilePath would return empty string if there is no actual file anymore + return fileInfo.filePath(); } void ScintillaNext::setFoldMarkers(const QString &type) @@ -369,15 +367,7 @@ bool ScintillaNext::rename(const QString &newFilePath) QFile::remove(oldPath); // Everything worked fine, so update the buffer's info - setFileInfo(newFilePath); - setSavePoint(); - - // If this was a temporary file, make sure it is not any more - setTemporary(false); - - emit saved(); - - emit renamed(); + renameEditorPath(newFilePath); return true; } @@ -385,6 +375,19 @@ bool ScintillaNext::rename(const QString &newFilePath) return false; } +void ScintillaNext::renameEditorPath(const QString &newFilePath) +{ + setFileInfo(newFilePath); + setSavePoint(); + + // If this was a temporary file, make sure it is not any more + setTemporary(false); + + emit saved(); + + emit renamed(); +} + ScintillaNext::FileStateChange ScintillaNext::checkFileForStateChange() { if (bufferType == BufferType::New) { @@ -611,6 +614,7 @@ void ScintillaNext::setFileInfo(const QString &filePath) bufferType = ScintillaNext::File; updateTimestamp(); + emit renamed(); } void ScintillaNext::detachFileInfo(const QString &newName) diff --git a/src/NotepadNext/ScintillaNext.h b/src/NotepadNext/ScintillaNext.h index 0eab58078..35f9b0399 100644 --- a/src/NotepadNext/ScintillaNext.h +++ b/src/NotepadNext/ScintillaNext.h @@ -84,7 +84,6 @@ class ScintillaNext : public ScintillaEdit QString getName() const { return name; } void setName(const QString &name); - QString getPath() const; QString getFilePath() const; // NOTE: this is dangerous and should only be used in very rare situations @@ -122,7 +121,8 @@ public slots: void reload(); QFileDevice::FileError saveAs(const QString &newFilePath); QFileDevice::FileError saveCopyAs(const QString &filePath); - bool rename(const QString &newFilePath); + bool rename(const QString &newFilePath); // update FS then update representation + void renameEditorPath(const QString &newFilePath); // update representation only ScintillaNext::FileStateChange checkFileForStateChange(); bool moveToTrash(); diff --git a/src/NotepadNext/SessionManager.cpp b/src/NotepadNext/SessionManager.cpp index 9249fcb27..b561cd879 100644 --- a/src/NotepadNext/SessionManager.cpp +++ b/src/NotepadNext/SessionManager.cpp @@ -245,7 +245,7 @@ bool SessionManager::willFileGetStoredInSession(ScintillaNext *editor) const void SessionManager::storeFileDetails(ScintillaNext *editor, QSettings &settings) { settings.setValue("Type", "File"); - settings.setValue("FilePath", editor->getFilePath()); + settings.setValue("FilePath", QDir::toNativeSeparators(editor->getFilePath())); storeEditorViewDetails(editor, settings); } @@ -254,7 +254,7 @@ ScintillaNext* SessionManager::loadFileDetails(QSettings &settings) { qInfo(Q_FUNC_INFO); - const QString filePath = settings.value("FilePath").toString(); + const QString filePath = QDir::fromNativeSeparators(settings.value("FilePath").toString()); qDebug("Session file: \"%s\"", qUtf8Printable(filePath)); @@ -284,7 +284,7 @@ void SessionManager::storeUnsavedFileDetails(ScintillaNext *editor, QSettings &s const QString sessionFileName = RandomSessionFileName(); settings.setValue("Type", "UnsavedFile"); - settings.setValue("FilePath", editor->getFilePath()); + settings.setValue("FilePath", QDir::toNativeSeparators(editor->getFilePath())); settings.setValue("SessionFileName", sessionFileName); storeEditorViewDetails(editor, settings); diff --git a/src/NotepadNext/dialogs/MainWindow.cpp b/src/NotepadNext/dialogs/MainWindow.cpp index 8ce36adfd..1a1895594 100644 --- a/src/NotepadNext/dialogs/MainWindow.cpp +++ b/src/NotepadNext/dialogs/MainWindow.cpp @@ -276,7 +276,7 @@ MainWindow::MainWindow(NotepadNextApplication *app) : connect(ui->actionCopyFullPath, &QAction::triggered, this, [=]() { auto editor = currentEditor(); if (editor->isFile()) { - QApplication::clipboard()->setText(editor->getFilePath()); + QApplication::clipboard()->setText(QDir::toNativeSeparators(editor->getFilePath())); } }); connect(ui->actionCopyFileName, &QAction::triggered, this, [=]() { @@ -285,7 +285,8 @@ MainWindow::MainWindow(NotepadNextApplication *app) : connect(ui->actionCopyFileDirectory, &QAction::triggered, this, [=]() { auto editor = currentEditor(); if (editor->isFile()) { - QApplication::clipboard()->setText(editor->getPath()); + QFileInfo currentFile(editor->getFilePath()); + QApplication::clipboard()->setText(QDir::toNativeSeparators(currentFile.dir().absolutePath())); } }); @@ -930,7 +931,7 @@ void MainWindow::openFileList(const QStringList &fileNames) const ScintillaNext *mostRecentEditor = Q_NULLPTR; for (const QString &filePath : fileNames) { - qInfo("%s", qUtf8Printable(filePath)); + qInfo() << Q_FUNC_INFO << filePath; // Search currently open editors to see if it is already open ScintillaNext *editor = app->getEditorManager()->getEditorByFilePath(filePath); @@ -1011,7 +1012,8 @@ void MainWindow::openFileDialog() // Use the path if possible if (editor->isFile()) { - dialogDir = editor->getPath(); + QFileInfo currentFile(editor->getFilePath()); + dialogDir = currentFile.dir().absolutePath(); } QStringList fileNames = FileDialogHelpers::getOpenFileNames(this, QString(), dialogDir, filter); @@ -1031,7 +1033,8 @@ void MainWindow::openFolderAsWorkspaceDialog() // Use the path if possible if (editor->isFile()) { - dialogDir = editor->getPath(); + QFileInfo currentFile(editor->getFilePath()); + dialogDir = currentFile.dir().absolutePath(); } QString dir = QFileDialog::getExistingDirectory(this, tr("Open Folder as Workspace"), dialogDir, QFileDialog::ShowDirsOnly); @@ -1357,14 +1360,48 @@ void MainWindow::moveCurrentFileToTrash() moveFileToTrash(editor); } +bool MainWindow::askMoveToTrash(const QString &path) +{ + auto reply = QMessageBox::question(this, tr("Delete File"), tr("Are you sure you want to move %1 to the trash?").arg(path)); + + return reply == QMessageBox::Yes; +} + +void MainWindow::closeByPath(const QString &path, bool isDirectory) +{ + qInfo(Q_FUNC_INFO); + + if (isDirectory) { + for (ScintillaNext *editor: editors()) { + if (editor->isFile() && editor->getFilePath().startsWith(path)) { + qInfo() << "->" << editor->getFilePath(); + + app->getRecentFilesListManager()->removeFile(path); + if (editor->isSavedToDisk()) { + editor->close(); + } + else { + editor->detachFileInfo(editor->getName()); + } + } + } + } + else { + forEachEditorByPath(path, [=](ScintillaNext* editor) { + closeFile(editor); + }); + // Since the file no longer exists, specifically remove it from the recent files list + app->getRecentFilesListManager()->removeFile(path); + } +} + void MainWindow::moveFileToTrash(ScintillaNext *editor) { Q_ASSERT(editor->isFile()); const QString filePath = editor->getFilePath(); - auto reply = QMessageBox::question(this, tr("Delete File"), tr("Are you sure you want to move %1 to the trash?").arg(filePath)); - if (reply == QMessageBox::Yes) { + if (askMoveToTrash(filePath)) { if (editor->moveToTrash()) { closeCurrentFile(); @@ -2025,6 +2062,34 @@ void MainWindow::dropEvent(QDropEvent *event) } } +void MainWindow::onNodeRenamed(const QString &parentPath, const QString &oldName, const QString &newName) +{ + qInfo(Q_FUNC_INFO); + + QDir parentDir(parentPath); + QString oldPath = parentDir.absoluteFilePath(oldName); + QString newPath = parentDir.absoluteFilePath(newName); + + QFileInfo fileInfo(newPath); + if (fileInfo.isDir()) { + for(auto &&editor : editors()) { + qInfo() << "" << editor->getFilePath(); + if (editor->isFile() && editor->getFilePath().startsWith(oldPath)) + { + QString newFilePath = newPath + editor->getFilePath().mid(oldPath.length()); + qInfo() << "->" << newFilePath; + editor->setFileInfo(newFilePath); + } + } + } + else { + forEachEditorByPath(oldPath, [=](ScintillaNext* editor) { + qInfo() << Q_FUNC_INFO << editor->getFilePath(); + editor->setFileInfo(newPath); + }); + } +} + void MainWindow::tabBarRightClicked(ScintillaNext *editor) { qInfo(Q_FUNC_INFO); diff --git a/src/NotepadNext/dialogs/MainWindow.h b/src/NotepadNext/dialogs/MainWindow.h index 3470b4e26..8cba9e726 100644 --- a/src/NotepadNext/dialogs/MainWindow.h +++ b/src/NotepadNext/dialogs/MainWindow.h @@ -58,6 +58,13 @@ class MainWindow : public QMainWindow QVector editors() const; DockedEditor *getDockedEditor() const { return dockedEditor; } + template + void forEachEditorByPath(const QString &path, Func callback); + + bool askMoveToTrash(const QString &path); + + void closeByPath(const QString &path, bool isDirectory); + public slots: void newFile(); @@ -125,6 +132,8 @@ public slots: void switchToEditor(const ScintillaNext *editor); + void onNodeRenamed(const QString &parentPath, const QString &oldName, const QString &newName); + signals: void editorActivated(ScintillaNext *editor); void aboutToClose(); @@ -173,4 +182,18 @@ private slots: int contextMenuPos = 0; }; +template +void MainWindow::forEachEditorByPath(const QString &path, Func callback) +{ + qDebug() << Q_FUNC_INFO << path; + for(auto &&editor : editors()) + { + qDebug() << editor->getFilePath(); + if (editor->getFilePath() == path) + { + callback(editor); + } + } +} + #endif // MAINWINDOW_H diff --git a/src/NotepadNext/docks/FolderAsWorkspaceDock.cpp b/src/NotepadNext/docks/FolderAsWorkspaceDock.cpp index f1dd3ed5d..ae3c68c85 100644 --- a/src/NotepadNext/docks/FolderAsWorkspaceDock.cpp +++ b/src/NotepadNext/docks/FolderAsWorkspaceDock.cpp @@ -18,20 +18,26 @@ #include "FolderAsWorkspaceDock.h" + +#include +#include + #include "ApplicationSettings.h" +#include "MainWindow.h" #include "ui_FolderAsWorkspaceDock.h" -#include ApplicationSetting rootPathSetting{"FolderAsWorkspace/RootPath"}; -FolderAsWorkspaceDock::FolderAsWorkspaceDock(QWidget *parent) : +FolderAsWorkspaceDock::FolderAsWorkspaceDock(MainWindow *parent) : QDockWidget(parent), ui(new Ui::FolderAsWorkspaceDock), + window(parent), model(new QFileSystemModel(this)) { ui->setupUi(this); + model->setReadOnly(false); ui->treeView->setModel(model); ui->treeView->header()->hideSection(1); ui->treeView->header()->hideSection(2); @@ -43,6 +49,9 @@ FolderAsWorkspaceDock::FolderAsWorkspaceDock(QWidget *parent) : } }); + connect(ui->treeView, &QTreeView::customContextMenuRequested, this, &FolderAsWorkspaceDock::onCustomContextMenu); + connect(model, &QFileSystemModel::fileRenamed, window, &MainWindow::onNodeRenamed); + ApplicationSettings settings; setRootPath(settings.get(rootPathSetting)); } @@ -65,3 +74,87 @@ QString FolderAsWorkspaceDock::rootPath() const { return model->rootPath(); } + +void FolderAsWorkspaceDock::onCustomContextMenu(const QPoint &point) +{ + QModelIndex index = ui->treeView->indexAt(point); + + // Nothing was selected + if (!index.isValid()) { + lastSelectedItem = model->index(rootPath()); + ui->menuEmpty->exec(ui->treeView->viewport()->mapToGlobal(point)); + return; + } + + lastSelectedItem = index; + + // Decide which menu to show if it is a directory or not + 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 editor = window->currentEditor(); + QString destinationName(parentDir.absoluteFilePath(editor->getName())); + + if (window->saveFileAs(editor, destinationName)) { + auto newItem = model->index(destinationName); + ui->treeView->setCurrentIndex(newItem); + ui->treeView->edit(newItem); + } + else { + qWarning("Unable to save %s", qUtf8Printable(destinationName)); + } +} + +void FolderAsWorkspaceDock::on_actionNewFolder_triggered() +{ + QDir parentDirectory(model->filePath(lastSelectedItem)); + QString destinationName; + int i = 1; + + // Find the next valid folder name + do { + destinationName = tr("New Folder %1").arg(i); + ++i; + } while (parentDirectory.exists(destinationName)); + + // Try to create it + auto newItem = model->mkdir(lastSelectedItem, destinationName); + + if (newItem.isValid()) { + ui->treeView->setCurrentIndex(newItem); + ui->treeView->edit(newItem); + } + else { + qWarning("Unable to create %s", qUtf8Printable(destinationName)); + } +} + +void FolderAsWorkspaceDock::on_actionRename_triggered() +{ + ui->treeView->setCurrentIndex(lastSelectedItem); + ui->treeView->edit(lastSelectedItem); +} + +void FolderAsWorkspaceDock::on_actionMoveToTrash_triggered() +{ + QString path(model->filePath(lastSelectedItem)); + + if (window->askMoveToTrash(path)) { + QFileInfo fileInfo(path); + bool isDirectory = fileInfo.isDir(); + if (QFile::moveToTrash(path)) { + window->closeByPath(path, isDirectory); + } + else { + qWarning("Unable to remove %s", path.toUtf8().constData()); + } + } +} diff --git a/src/NotepadNext/docks/FolderAsWorkspaceDock.h b/src/NotepadNext/docks/FolderAsWorkspaceDock.h index 085254905..a65792ccd 100644 --- a/src/NotepadNext/docks/FolderAsWorkspaceDock.h +++ b/src/NotepadNext/docks/FolderAsWorkspaceDock.h @@ -21,19 +21,21 @@ #define FOLDERASWORKSPACEDOCK_H #include +#include 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); @@ -42,10 +44,21 @@ 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_actionMoveToTrash_triggered(); + + void onCustomContextMenu(const QPoint &point); + private: Ui::FolderAsWorkspaceDock *ui; + MainWindow *window; QFileSystemModel *model; + + QModelIndex lastSelectedItem; }; #endif // FOLDERASWORKSPACEDOCK_H diff --git a/src/NotepadNext/docks/FolderAsWorkspaceDock.ui b/src/NotepadNext/docks/FolderAsWorkspaceDock.ui index 3515d5620..d74c6de06 100644 --- a/src/NotepadNext/docks/FolderAsWorkspaceDock.ui +++ b/src/NotepadNext/docks/FolderAsWorkspaceDock.ui @@ -27,11 +27,47 @@ 0 + + + + File Menu + + + + + + + + + Folder Menu + + + + + + + + + + + + Space Menu + + + + + + + Qt::CustomContextMenu + QFrame::NoFrame + + QAbstractItemView::EditKeyPressed + false @@ -39,7 +75,33 @@ + + + &Save Current File Here + + + + + New &Folder + + + + + &Rename + + + + + + :/icons/bin_closed.png:/icons/bin_closed.png + + + Move to &Trash + + - + + +