diff --git a/include/FileManager.h b/include/FileManager.h index 6a44b39..162d5be 100644 --- a/include/FileManager.h +++ b/include/FileManager.h @@ -52,6 +52,9 @@ class FileManager : public QObject static OperationResult duplicatePath(const QFileInfo &pathInfo); static OperationResult deletePath(const QFileInfo &pathInfo); + int buildUnsavedChangesMessage() const; + bool hasUnsavedChanges(); + public slots: void newFile(); void saveFile(); @@ -59,6 +62,8 @@ public slots: void openFile(); void loadFileInEditor(const QString &filePath); + bool promptUnsavedChanges(); + QString getDirectoryPath() const; private: @@ -69,4 +74,5 @@ public slots: MainWindow *m_mainWindow; QSyntaxHighlighter *m_currentHighlighter = nullptr; QString m_currentFileName; + bool m_isDirty = false; }; \ No newline at end of file diff --git a/src/FileManager.cpp b/src/FileManager.cpp index 5568349..411d5a7 100644 --- a/src/FileManager.cpp +++ b/src/FileManager.cpp @@ -11,6 +11,7 @@ #include #include + FileManager::FileManager(CodeEditor *editor, MainWindow *mainWindow) : m_editor(editor), m_mainWindow(mainWindow) { @@ -23,6 +24,8 @@ void FileManager::initialize(CodeEditor *editor, MainWindow *mainWindow) { m_editor = editor; m_mainWindow = mainWindow; + + connect(m_editor, &QPlainTextEdit::textChanged, this, [this](){m_isDirty = true;}); } QString FileManager::getCurrentFileName() const @@ -35,65 +38,102 @@ void FileManager::setCurrentFileName(const QString fileName) m_currentFileName = fileName; } -void FileManager::newFile() +QString getLastSaved(const QFileInfo& file) { - QString currentFileName = getCurrentFileName(); - bool isFileSaved = !currentFileName.isEmpty(); - bool isTextEditorEmpty = this->m_editor->toPlainText().isEmpty(); - // File has not been saved and the text editor is not empty - if (!isFileSaved && !isTextEditorEmpty) - { - // Create box to prompt user to save changes to file - QMessageBox promptBox; - promptBox.setWindowTitle("Save Current File"); - promptBox.setText("Would you like to save the file?"); - promptBox.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel); - promptBox.setDefaultButton(QMessageBox::Save); - - int option = promptBox.exec(); - // return if the user hit Cancel button - if (option == QMessageBox::Cancel) - { - return; - } + const auto lastSaved = file.lastModified(); + const auto now = QDateTime::currentDateTime(); + const auto seconds = lastSaved.secsTo(now); + + const int days = seconds / (60 * 60 * 24); + if (days == 0) + return "today"; + if (days == 1) + return "yesterday"; + + return QString::number(days) + " days ago"; +} - saveFile(); +bool FileManager::hasUnsavedChanges() +{ + if(!m_isDirty) + { + return false; } - // File has been previously saved - else if (isFileSaved) + + // Additional safeguard if content still matches file on disk + if (!m_currentFileName.isEmpty()) { - // Read from saved file and compare to current file - QFile file(currentFileName); - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) - return; - QTextStream in(&file); - QString savedFileContents = in.readAll(); - file.close(); - if (savedFileContents != this->m_editor->toPlainText().trimmed()) + QFile file(m_currentFileName); + if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { - // Create box to prompt user to save changes to file - QMessageBox promptBox; - promptBox.setWindowTitle("Changes Detected"); - promptBox.setText("Would you like to save the current changes to the file?"); - promptBox.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel); - promptBox.setDefaultButton(QMessageBox::Save); - int option = promptBox.exec(); - // return if the user hit Cancel button - if (option == QMessageBox::Cancel) + QTextStream in(&file); + QString diskContents = in.readAll(); + if (diskContents == m_editor->toPlainText()) { - return; + m_isDirty = false; + return false; } - saveFile(); } } + return true; // nothing changed +} + +int FileManager::buildUnsavedChangesMessage() const +{ + QString infoText = "Your changes will be lost if you don't save."; if (!m_currentFileName.isEmpty()) { - setCurrentFileName(""); - m_editor->clear(); - m_mainWindow->setWindowTitle("Code Astra ~ untitled"); + QFileInfo file(m_currentFileName); + if (file.exists()) + { + QString timeSinceSave = getLastSaved(file); + infoText = "The document has been modified. It was last edited " + timeSinceSave + "."; + } } - + + QMessageBox promptBox; + promptBox.setWindowTitle("Unsaved changes"); + promptBox.setText("Would you like to save your changes?"); + promptBox.setInformativeText(infoText); + promptBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); + promptBox.setDefaultButton(QMessageBox::Save); + + return promptBox.exec(); +} + +bool FileManager::promptUnsavedChanges() +{ + if (!hasUnsavedChanges()) + { + return true; + } + + int option = buildUnsavedChangesMessage(); + if (option == QMessageBox::Save) + { + saveFile(); + } + else if (option == QMessageBox::Cancel) + { + return false; + } + + // if discard selected, continue without saving. + return true; +} + +void FileManager::newFile() +{ + if (!promptUnsavedChanges()) + { + return; + } + + m_currentFileName = ""; + m_editor->clear(); + m_mainWindow->setWindowTitle("Untitle ~ Code Astra"); + m_isDirty = false; } void FileManager::saveFile() @@ -104,7 +144,7 @@ void FileManager::saveFile() return; } - qDebug() << "Saving file:" << m_currentFileName; + // qDebug() << "Saving file:" << m_currentFileName; QFile file(m_currentFileName); if (!file.open(QFile::WriteOnly | QFile::Text)) @@ -125,6 +165,16 @@ void FileManager::saveFile() } file.close(); + if (m_mainWindow) + { + m_mainWindow->setWindowTitle("CodeAstra ~ " + QFileInfo(m_currentFileName).fileName()); + } + else + { + qWarning() << "MainWindow is not initialized in FileManager."; + } + + m_isDirty = false; emit m_editor->statusMessageChanged("File saved successfully."); } @@ -152,7 +202,7 @@ void FileManager::openFile() "All Files (*);;C++ Files (*.cpp *.h);;Text Files (*.txt)"); if (!fileName.isEmpty()) { - qDebug() << "Opening file: " << fileName; + // qDebug() << "Opening file: " << fileName; m_currentFileName = fileName; loadFileInEditor(fileName); } @@ -164,7 +214,8 @@ void FileManager::openFile() void FileManager::loadFileInEditor(const QString &filePath) { - qDebug() << "Loading file:" << filePath; + // qDebug() << "Loading file:" << filePath; + QFile file(filePath); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { @@ -175,7 +226,9 @@ void FileManager::loadFileInEditor(const QString &filePath) QTextStream in(&file); if (m_editor) { + m_editor->blockSignals(true); m_editor->setPlainText(in.readAll()); + m_editor->blockSignals(false); delete m_currentHighlighter; @@ -197,6 +250,8 @@ void FileManager::loadFileInEditor(const QString &filePath) { qWarning() << "MainWindow is not initialized in FileManager."; } + + m_isDirty = false; } QString FileManager::getFileExtension() const @@ -296,6 +351,7 @@ OperationResult FileManager::deletePath(const QFileInfo &pathInfo) { return {false, "ERROR: invalid file path." + pathToDelete.filename().string()}; } + QString qPathToDelete = QString::fromStdString(pathToDelete.string()); if (!QFile::moveToTrash(qPathToDelete)) { @@ -330,7 +386,6 @@ OperationResult FileManager::newFile(const QFileInfo &pathInfo, QString newFileP { file.close(); } - qDebug() << "New file created."; FileManager::getInstance().setCurrentFileName(QString::fromStdString(filePath.string())); return {true, filePath.filename().string() + " created successfully."}; diff --git a/src/Syntax.cpp b/src/Syntax.cpp index e3aab1c..f705bb5 100644 --- a/src/Syntax.cpp +++ b/src/Syntax.cpp @@ -59,8 +59,6 @@ void Syntax::loadSyntaxRules(const YAML::Node &config) continue; } - qDebug() << "regex: " << regex; - QColor color; try { @@ -76,7 +74,7 @@ void Syntax::loadSyntaxRules(const YAML::Node &config) //checks if the color is a valid color if(!color.isValid()) { - qWarning() << "Invalid COlor : Skipping..."; + qWarning() << "Invalid Color : Skipping..."; continue; } diff --git a/src/Tree.cpp b/src/Tree.cpp index 93d170e..182b054 100644 --- a/src/Tree.cpp +++ b/src/Tree.cpp @@ -67,8 +67,17 @@ void Tree::openFile(const QModelIndex &index) return; } - FileManager::getInstance().setCurrentFileName(filePath); - FileManager::getInstance().loadFileInEditor(filePath); + FileManager &fm = FileManager::getInstance(); + if (fm.getCurrentFileName() != filePath) + { + if (!fm.promptUnsavedChanges()) + { + return; // if user has cancelled + } + + fm.setCurrentFileName(filePath); + fm.loadFileInEditor(filePath); + } } QFileSystemModel *Tree::getModel() const