From c73d9a0678ed78fec55da29a60d6dc74896f04e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Sat, 21 Oct 2017 21:20:10 +0200 Subject: [PATCH] Projects Management (#66) * Add TabWidget to NewFileDialog * Project loading from NewFileDialog * Projects dir in NewFileDialog * Add SaveProjectDialog * Make SaveProjectDialog work, refactor project saving and Notepad * Add shortcut for Save * Fix notes loading * Sort projects in NewFileDialog * Implement selecting projects dir in SaveProjectDialog * Fix QAbstractButton include --- src/MainWindow.cpp | 89 +++--- src/MainWindow.h | 24 +- src/MainWindow.ui | 19 +- src/cutter.cpp | 29 +- src/cutter.h | 14 +- src/cutter.pro | 9 +- src/dialogs/NewFileDialog.cpp | 276 +++++++++++------- src/dialogs/NewFileDialog.h | 30 +- src/dialogs/NewfileDialog.ui | 445 +++++++++++++++++++++--------- src/dialogs/SaveProjectDialog.cpp | 104 +++++++ src/dialogs/SaveProjectDialog.h | 35 +++ src/dialogs/SaveProjectDialog.ui | 140 ++++++++++ src/main.cpp | 2 +- src/utils/Helpers.cpp | 6 - src/utils/Helpers.h | 2 - src/widgets/Notepad.cpp | 47 ++-- src/widgets/Notepad.h | 27 +- 17 files changed, 947 insertions(+), 351 deletions(-) create mode 100644 src/dialogs/SaveProjectDialog.cpp create mode 100644 src/dialogs/SaveProjectDialog.h create mode 100644 src/dialogs/SaveProjectDialog.ui diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 947d3e55..ff01913b 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -61,6 +61,7 @@ #include "dialogs/OptionsDialog.h" #include "widgets/EntrypointWidget.h" #include "widgets/DisassemblerGraphView.h" +#include "dialogs/SaveProjectDialog.h" // graphics #include @@ -298,16 +299,8 @@ void MainWindow::initUI() QShortcut *refresh_shortcut = new QShortcut(QKeySequence(QKeySequence::Refresh), this); connect(refresh_shortcut, SIGNAL(activated()), this, SLOT(refreshVisibleDockWidgets())); -} -void MainWindow::openFile(const QString &fn, int anal_level, QList advanced) -{ - QString project_name = qhelpers::uniqueProjectName(fn); - - if (core->getProjectNames().contains(project_name)) - openProject(project_name); - else - openNewFile(fn, anal_level, advanced); + connect(core, SIGNAL(projectSaved(const QString &)), this, SLOT(projectSaved(const QString &))); } void MainWindow::openNewFile(const QString &fn, int anal_level, QList advanced) @@ -327,7 +320,7 @@ void MainWindow::openProject(const QString &project_name) QString filename = core->cmd("Pi " + project_name); setFilename(filename.trimmed()); - core->cmd("Po " + project_name); + core->openProject(project_name); initUI(); finalizeOpen(); @@ -347,20 +340,9 @@ void MainWindow::finalizeOpen() core->cmd("fs sections"); updateFrames(); - // Restore project notes - QString notes = this->core->cmd("Pnj"); - //qDebug() << "Notes:" << notes; - if (notes != "") + if(core->getNotes().isEmpty()) { - QByteArray ba; - ba.append(notes); - notepadDock->setText(QByteArray::fromBase64(ba)); - } - else - { - addOutput(tr(" > Adding binary information to notepad")); - - notepadDock->setText(tr("# Binary information\n\n") + core->cmd("i") + + core->setNotes(tr("# Binary information\n\n") + core->cmd("i") + "\n" + core->cmd("ie") + "\n" + core->cmd("iM") + "\n"); } @@ -377,14 +359,27 @@ void MainWindow::finalizeOpen() notepadDock->highlightPreview(); } -void MainWindow::saveProject() +bool MainWindow::saveProject(bool quit) { - QString project_name = qhelpers::uniqueProjectName(filename); - core->cmd("Ps " + project_name); - QString notes = this->notepadDock->textToBase64(); - //this->add_debug_output(notes); - this->core->cmd("Pnj " + notes); - this->addOutput(tr("Project saved: ") + project_name); + QString projectName = core->getConfig("prj.name"); + if (projectName.isEmpty()) + { + return saveProjectAs(quit); + } + else + { + core->saveProject(projectName); + return true; + } +} + +bool MainWindow::saveProjectAs(bool quit) +{ + SaveProjectDialog dialog(quit, this); + int result = dialog.exec(); + + return !quit || result != SaveProjectDialog::Rejected; + } void MainWindow::toggleSideBarTheme() @@ -412,13 +407,19 @@ void MainWindow::closeEvent(QCloseEvent *event) //qDebug() << ret; if (ret == QMessageBox::Save) { - QSettings settings; - settings.setValue("geometry", saveGeometry()); - settings.setValue("size", size()); - settings.setValue("pos", pos()); - settings.setValue("state", saveState()); - saveProject(); - QMainWindow::closeEvent(event); + if(saveProject(true)) + { + QSettings settings; + settings.setValue("geometry", saveGeometry()); + settings.setValue("size", size()); + settings.setValue("pos", pos()); + settings.setValue("state", saveState()); + QMainWindow::closeEvent(event); + } + else + { + event->ignore(); + } } else if (ret == QMessageBox::Discard) { @@ -670,7 +671,7 @@ void MainWindow::on_actionRefresh_Panels_triggered() this->updateFrames(); } -void MainWindow::toggleDockWidget(DockWidget *dock_widget) +void MainWindow::toggleDockWidget(QDockWidget *dock_widget) { if (dock_widget->isVisible()) { @@ -808,7 +809,7 @@ void MainWindow::on_actionhide_bottomPannel_triggered() void MainWindow::sendToNotepad(const QString &txt) { - this->notepadDock->appendPlainText("```\n" + txt + "\n```"); + core->setNotes(core->getNotes() + "```\n" + txt + "\n```"); } void MainWindow::on_actionFunctionsRename_triggered() @@ -841,6 +842,11 @@ void MainWindow::on_actionSave_triggered() saveProject(); } +void MainWindow::on_actionSaveAs_triggered() +{ + saveProjectAs(); +} + void MainWindow::on_actionRun_Script_triggered() { QFileDialog dialog(this); @@ -970,3 +976,8 @@ void MainWindow::on_actionAsmOptions_triggered() auto dialog = new AsmOptionsDialog(this); dialog->show(); } + +void MainWindow::projectSaved(const QString &name) +{ + this->addOutput(tr("Project saved: ") + name); +} diff --git a/src/MainWindow.h b/src/MainWindow.h index 87166ceb..c479c5fc 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -53,11 +53,23 @@ public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); - void openFile(const QString &fn, int anal_level = -1, QList advanced = QList()); + void openNewFile(const QString &fn, int anal_level = -1, QList advanced = QList()); + void openProject(const QString &project_name); + void initUI(); void finalizeOpen(); - void saveProject(); + /*! + * @param quit whether to show destructive button in dialog + * @return if quit is true, false if the application should not close + */ + bool saveProject(bool quit = false); + + /*! + * @param quit whether to show destructive button in dialog + * @return if quit is true, false if the application should not close + */ + bool saveProjectAs(bool quit = false); void start_web_server(); void closeEvent(QCloseEvent *event) override; @@ -144,6 +156,7 @@ private slots: void on_actionNew_triggered(); void on_actionSave_triggered(); + void on_actionSaveAs_triggered(); void on_actionWhite_Theme_triggered(); @@ -165,6 +178,8 @@ private slots: void on_actionAsmOptions_triggered(); + void projectSaved(const QString &name); + private: CutterCore *core; DisassemblyWidget *disassemblyDock; @@ -206,10 +221,7 @@ private: SectionsDock *sectionsDock; ConsoleWidget *consoleWidget; - void openProject(const QString &project_name); - void openNewFile(const QString &fn, int anal_level, QList advanced); - - void toggleDockWidget(DockWidget *dock_widget); + void toggleDockWidget(QDockWidget *dock_widget); public: RVA getCursorAddress() const { return cursorAddress; } diff --git a/src/MainWindow.ui b/src/MainWindow.ui index b4f70a2f..c8927e80 100644 --- a/src/MainWindow.ui +++ b/src/MainWindow.ui @@ -174,7 +174,7 @@ border-top: 0px; 0 0 1013 - 20 + 22 @@ -186,10 +186,10 @@ border-top: 0px; - 273 - 136 - 148 - 167 + 419 + 265 + 173 + 206 @@ -199,6 +199,7 @@ border-top: 0px; + @@ -419,6 +420,9 @@ QToolButton .svg-icon path { Save + + Ctrl+S + @@ -979,6 +983,11 @@ QToolButton .svg-icon path { Disassembly Options + + + Save As... + + diff --git a/src/cutter.cpp b/src/cutter.cpp index 1f945c4d..f5078fce 100644 --- a/src/cutter.cpp +++ b/src/cutter.cpp @@ -46,7 +46,6 @@ CutterCore::CutterCore(QObject *parent) : QObject(parent) { r_cons_new(); // initialize console - this->projectPath = ""; this->core_ = r_core_new(); r_core_loadlibs(this->core_, R_CORE_LOADLIBS_ALL, NULL); // IMPLICIT r_bin_iobind (core_->bin, core_->io); @@ -1219,3 +1218,31 @@ void CutterCore::loadPDB(const QString &file) { cmd("idp " + sanitizeStringForCommand(file)); } + +void CutterCore::openProject(const QString &name) +{ + cmd("Po " + name); + + QString notes = QString::fromUtf8(QByteArray::fromBase64(cmd("Pnj").toUtf8())); + setNotes(notes); +} + +void CutterCore::saveProject(const QString &name) +{ + cmd("Ps " + name); + cmd("Pnj " + notes.toUtf8().toBase64()); + emit projectSaved(name); +} + +bool CutterCore::isProjectNameValid(const QString &name) +{ + // see is_valid_project_name() in libr/core/project.c + static const QRegExp regexp(R"(^[a-zA-Z0-9\\\._:-]{1,}$)"); + return regexp.exactMatch(name) && !name.endsWith(".zip") ; +} + +void CutterCore::setNotes(const QString ¬es) +{ + this->notes = notes; + emit notesChanged(this->notes); +} diff --git a/src/cutter.h b/src/cutter.h index deffc6d5..da94ac8a 100644 --- a/src/cutter.h +++ b/src/cutter.h @@ -184,8 +184,6 @@ class CutterCore: public QObject friend class ccClass; public: - QString projectPath; - explicit CutterCore(QObject *parent = 0); ~CutterCore(); static CutterCore* getInstance(); @@ -267,6 +265,13 @@ public: QStringList getAnalPluginNames(); QStringList getProjectNames(); + void openProject(const QString &name); + void saveProject(const QString &name); + + static bool isProjectNameValid(const QString &name); + + const QString &getNotes() const { return notes; } + void setNotes(const QString ¬es); QList getRBinPluginDescriptions(const QString &type = nullptr); @@ -303,6 +308,9 @@ signals: void flagsChanged(); void commentsChanged(); + void notesChanged(const QString ¬es); + void projectSaved(const QString &name); + /*! * emitted when config regarding disassembly display changes */ @@ -321,6 +329,8 @@ private: QString default_cpu; int default_bits; + QString notes; + RCore *core_; }; diff --git a/src/cutter.pro b/src/cutter.pro index b1c70947..37382962 100644 --- a/src/cutter.pro +++ b/src/cutter.pro @@ -77,7 +77,8 @@ SOURCES += \ widgets/SidebarWidget.cpp \ widgets/HexdumpWidget.cpp \ utils/Configuration.cpp \ - utils/Colors.cpp + utils/Colors.cpp \ + dialogs/SaveProjectDialog.cpp HEADERS += \ cutter.h \ @@ -126,7 +127,8 @@ HEADERS += \ widgets/SidebarWidget.h \ widgets/HexdumpWidget.h \ utils/Configuration.h \ - utils/Colors.h + utils/Colors.h \ + dialogs/SaveProjectDialog.h FORMS += \ widgets/PreviewWidget.ui \ @@ -156,7 +158,8 @@ FORMS += \ widgets/StringsWidget.ui \ widgets/SymbolsWidget.ui \ widgets/SidebarWidget.ui \ - widgets/HexdumpWidget.ui + widgets/HexdumpWidget.ui \ + dialogs/SaveProjectDialog.ui RESOURCES += \ resources.qrc diff --git a/src/dialogs/NewFileDialog.cpp b/src/dialogs/NewFileDialog.cpp index 2c8f3029..34bafbdf 100644 --- a/src/dialogs/NewFileDialog.cpp +++ b/src/dialogs/NewFileDialog.cpp @@ -64,10 +64,152 @@ NewFileDialog::NewFileDialog(QWidget *parent) : { ui->setupUi(this); setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint)); - ui->recentsList->addAction(ui->actionRemove_item); - ui->recentsList->addAction(ui->actionClear_all); - ui->recentsList->setIconSize(QSize(48, 48)); + ui->recentsListWidget->addAction(ui->actionRemove_item); + ui->recentsListWidget->addAction(ui->actionClear_all); + fillRecentFilesList(); + bool projectsExist = fillProjectsList(); + + if(projectsExist) + { + ui->tabWidget->setCurrentWidget(ui->projectsTab); + } + else + { + ui->tabWidget->setCurrentWidget(ui->filesTab); + } + + // Hide "create" button until the dialog works + ui->createButton->hide(); + + ui->loadProjectButton->setEnabled(ui->projectsListWidget->currentItem() != nullptr); +} + +NewFileDialog::~NewFileDialog() {} + +void NewFileDialog::on_loadFileButton_clicked() +{ + loadFile(ui->newFileEdit->text()); +} + +void NewFileDialog::on_selectFileButton_clicked() +{ + QString fileName = QFileDialog::getOpenFileName(this, tr("Select file"), QDir::homePath()); + + if (!fileName.isEmpty()) + { + ui->newFileEdit->setText(fileName); + ui->loadFileButton->setFocus(); + } +} + +void NewFileDialog::on_selectProjectsDirButton_clicked() +{ + QFileDialog dialog(this); + dialog.setFileMode(QFileDialog::DirectoryOnly); + + QString currentDir = CutterCore::getInstance()->getConfig("dir.projects"); + if(currentDir.startsWith("~")) + { + currentDir = QDir::homePath() + currentDir.mid(1); + } + dialog.setDirectory(currentDir); + + dialog.setWindowTitle(tr("Select project path (dir.projects)")); + + if(!dialog.exec()) + { + return; + } + + QString dir = dialog.selectedFiles().first(); + if (!dir.isEmpty()) + { + CutterCore::getInstance()->setConfig("dir.projects", dir); + fillProjectsList(); + } +} + +void NewFileDialog::on_loadProjectButton_clicked() +{ + QListWidgetItem *item = ui->projectsListWidget->currentItem(); + + if (item == nullptr) + { + return; + } + + loadProject(item->data(Qt::UserRole).toString()); +} + +void NewFileDialog::on_recentsListWidget_itemClicked(QListWidgetItem *item) +{ + QVariant data = item->data(Qt::UserRole); + QString sitem = data.toString(); + ui->newFileEdit->setText(sitem); +} + +void NewFileDialog::on_recentsListWidget_itemDoubleClicked(QListWidgetItem *item) +{ + loadFile(item->data(Qt::UserRole).toString()); +} + +void NewFileDialog::on_projectsListWidget_itemSelectionChanged() +{ + ui->loadProjectButton->setEnabled(ui->projectsListWidget->currentItem() != nullptr); +} + +void NewFileDialog::on_projectsListWidget_itemDoubleClicked(QListWidgetItem *item) +{ + loadProject(item->data(Qt::UserRole).toString()); +} + +void NewFileDialog::on_cancelButton_clicked() +{ + close(); +} + +void NewFileDialog::on_actionRemove_item_triggered() +{ + // Remove selected item from recents list + QListWidgetItem *item = ui->recentsListWidget->currentItem(); + + QVariant data = item->data(Qt::UserRole); + QString sitem = data.toString(); + + QSettings settings; + QStringList files = settings.value("recentFileList").toStringList(); + files.removeAll(sitem); + settings.setValue("recentFileList", files); + + ui->recentsListWidget->takeItem(ui->recentsListWidget->currentRow()); + + ui->newFileEdit->clear(); +} + +void NewFileDialog::on_createButton_clicked() +{ + // Close dialog and open create new file dialog + close(); + CreateNewDialog *n = new CreateNewDialog(nullptr); + n->exec(); +} + +void NewFileDialog::on_actionClear_all_triggered() +{ + // Clear recent file list + QSettings settings; + QStringList files = settings.value("recentFileList").toStringList(); + files.clear(); + + ui->recentsListWidget->clear(); + // TODO: if called from main window its ok, otherwise its not + settings.setValue("recentFileList", files); + ui->newFileEdit->clear(); +} + +bool NewFileDialog::fillRecentFilesList() +{ // Fill list with recent opened files QSettings settings; @@ -94,30 +236,50 @@ NewFileDialog::NewFileDialog(QWidget *parent) : else { QListWidgetItem *item = new QListWidgetItem( - getIconFor(name, i++), - file + "\nCreated: " + info.created().toString() + "\nSize: " + formatBytecount(info.size()) + getIconFor(name, i++), + file + "\nCreated: " + info.created().toString() + "\nSize: " + formatBytecount(info.size()) ); //":/img/icons/target.svg"), name ); item->setData(Qt::UserRole, file); - ui->recentsList->addItem(item); + ui->recentsListWidget->addItem(item); } } - ui->recentsList->setSortingEnabled(true); - // Hide "create" button until the dialog works - ui->createButton->hide(); - - // Removes files were deleted from the stringlist. Save it again. + // Removed files were deleted from the stringlist. Save it again. settings.setValue("recentFileList", files); + + return !files.isEmpty(); } -NewFileDialog::~NewFileDialog() {} +bool NewFileDialog::fillProjectsList() +{ + CutterCore *core = CutterCore::getInstance(); -void NewFileDialog::on_loadFileButton_clicked() + ui->projectsDirEdit->setText(core->getConfig("dir.projects")); + + QStringList projects = core->getProjectNames(); + projects.sort(Qt::CaseInsensitive); + + ui->projectsListWidget->clear(); + + int i=0; + for(const QString &project : projects) + { + QString info = core->cmd("Pi " + project); + + QListWidgetItem *item = new QListWidgetItem(getIconFor(project, i++), project + "\n" + info); + + item->setData(Qt::UserRole, project); + ui->projectsListWidget->addItem(item); + } + + return !projects.isEmpty(); +} + +void NewFileDialog::loadFile(const QString &filename) { // Check that there is a file selected - QString fname = ui->newFileEdit->text(); - QFileInfo checkfile(fname); + QFileInfo checkfile(filename); if (!checkfile.exists() || !checkfile.isFile()) { QMessageBox msgBox(this); @@ -129,8 +291,8 @@ void NewFileDialog::on_loadFileButton_clicked() // Add file to recent file list QSettings settings; QStringList files = settings.value("recentFileList").toStringList(); - files.removeAll(fname); - files.prepend(fname); + files.removeAll(filename); + files.prepend(filename); while (files.size() > MaxRecentFiles) files.removeLast(); @@ -140,90 +302,16 @@ void NewFileDialog::on_loadFileButton_clicked() // Close dialog and open MainWindow/OptionsDialog MainWindow *main = new MainWindow(); - main->openFile(fname); + main->openNewFile(filename); //OptionsDialog *o = new OptionsDialog(fname); //o->exec(); } } -void NewFileDialog::on_newFileButton_clicked() +void NewFileDialog::loadProject(const QString &project) { - QFileDialog dialog(this); - dialog.setFileMode(QFileDialog::ExistingFile); - dialog.setViewMode(QFileDialog::Detail); - dialog.setDirectory(QDir::home()); - - QString fileName; - fileName = dialog.getOpenFileName(this, tr("Select file")); - - if (!fileName.isEmpty()) - { - ui->newFileEdit->setText(fileName); - ui->loadFileButton->setFocus(); - } -} - -void NewFileDialog::on_recentsList_itemClicked(QListWidgetItem *item) -{ - QVariant data = item->data(Qt::UserRole); - QString sitem = data.toString(); - ui->newFileEdit->setText(sitem); -} - -void NewFileDialog::on_recentsList_itemDoubleClicked(QListWidgetItem *item) -{ - // Get selected item to send to options dialog - QVariant data = item->data(Qt::UserRole); - QString sitem = data.toString(); - // Close dialog and open OptionsDialog close(); MainWindow *main = new MainWindow(); - main->openFile(sitem); - //OptionsDialog *o = new OptionsDialog(sitem); - //o->exec(); -} - -void NewFileDialog::on_cancelButton_clicked() -{ - close(); -} - -void NewFileDialog::on_actionRemove_item_triggered() -{ - // Remove selected item from recents list - QListWidgetItem *item = ui->recentsList->currentItem(); - - QVariant data = item->data(Qt::UserRole); - QString sitem = data.toString(); - - QSettings settings; - QStringList files = settings.value("recentFileList").toStringList(); - files.removeAll(sitem); - settings.setValue("recentFileList", files); - - ui->recentsList->takeItem(ui->recentsList->currentRow()); - - ui->newFileEdit->clear(); -} - -void NewFileDialog::on_createButton_clicked() -{ - // Close dialog and open create new file dialog - close(); - CreateNewDialog *n = new CreateNewDialog(nullptr); - n->exec(); -} - -void NewFileDialog::on_actionClear_all_triggered() -{ - // Clear recent file list - QSettings settings; - QStringList files = settings.value("recentFileList").toStringList(); - files.clear(); - - ui->recentsList->clear(); - // TODO: if called from main window its ok, otherwise its not - settings.setValue("recentFileList", files); - ui->newFileEdit->clear(); + main->openProject(project); } diff --git a/src/dialogs/NewFileDialog.h b/src/dialogs/NewFileDialog.h index fb037d24..654297ec 100644 --- a/src/dialogs/NewFileDialog.h +++ b/src/dialogs/NewFileDialog.h @@ -19,26 +19,40 @@ public: ~NewFileDialog(); private slots: - void on_loadFileButton_clicked(); + void on_selectFileButton_clicked(); + void on_createButton_clicked(); - void on_newFileButton_clicked(); + void on_selectProjectsDirButton_clicked(); + void on_loadProjectButton_clicked(); - void on_recentsList_itemClicked(QListWidgetItem *item); + void on_cancelButton_clicked(); - void on_recentsList_itemDoubleClicked(QListWidgetItem *item); + void on_recentsListWidget_itemClicked(QListWidgetItem *item); + void on_recentsListWidget_itemDoubleClicked(QListWidgetItem *item); - void on_cancelButton_clicked(); + void on_projectsListWidget_itemSelectionChanged(); + void on_projectsListWidget_itemDoubleClicked(QListWidgetItem *item); void on_actionRemove_item_triggered(); - - void on_createButton_clicked(); - void on_actionClear_all_triggered(); private: std::unique_ptr ui; + /*! + * @return true if list is not empty + */ + bool fillRecentFilesList(); + + /*! + * @return true if list is not empty + */ + bool fillProjectsList(); + + void loadFile(const QString &filename); + void loadProject(const QString &project); + static const int MaxRecentFiles = 5; }; diff --git a/src/dialogs/NewfileDialog.ui b/src/dialogs/NewfileDialog.ui index 388e4c1d..679df1ce 100644 --- a/src/dialogs/NewfileDialog.ui +++ b/src/dialogs/NewfileDialog.ui @@ -6,15 +6,15 @@ 0 0 - 400 - 450 + 452 + 532 Open File - + :/img/logo-small.png:/img/logo-small.png @@ -42,7 +42,7 @@ - :/img/logo-small.png + :/img/logo-small.png false @@ -97,123 +97,309 @@ 5 - - - QLayout::SetDefaultConstraint + + + 0 - - 5 - - - - - - 1 - 0 - - - - Qt::Horizontal - - - - - - - - 0 - 1 - - - - - 11 - - - - Qt::ActionsContextMenu - - - QFrame::NoFrame - - - QFrame::Plain - - - 0 - - - QAbstractItemView::ScrollPerPixel - - - QListView::Adjust - - - 5 - - - QListView::ListMode - - - false - - - false - - - true - - - - - - - - 0 - 0 - - - - Select - - - - - - - <html><head/><body><p><span style=" font-weight:600;">Previous sessions</span></p></body></html> - - - - - - - - 12 - - - - false - - - true - - - - - - - - 0 - 0 - - - - <html><head/><body><p><span style=" font-weight:600;">Select new file</span></p></body></html> - - - - + + + Open File + + + + + + QLayout::SetDefaultConstraint + + + 5 + + + + + + 0 + 0 + + + + Select + + + + + + + false + + + true + + + + + + + + 0 + 0 + + + + <html><head/><body><p><span style=" font-weight:600;">Select new file</span></p></body></html> + + + + + + + + + + 1 + 0 + + + + Qt::Horizontal + + + + + + + + 0 + 1 + + + + + 11 + + + + Qt::ActionsContextMenu + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + 48 + 48 + + + + QAbstractItemView::ScrollPerPixel + + + QListView::Adjust + + + 5 + + + QListView::ListMode + + + false + + + false + + + true + + + true + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Create + + + + + + + Open + + + + + + + + + + Projects + + + + + + QLayout::SetDefaultConstraint + + + 5 + + + + + + 0 + 0 + + + + Select + + + + + + + false + + + true + + + + + + + + 0 + 0 + + + + <html><head/><body><p><span style=" font-weight:600;">Projects path (dir.projects):</span></p></body></html> + + + + + + + + + + 1 + 0 + + + + Qt::Horizontal + + + + + + + + 0 + 1 + + + + + 11 + + + + Qt::ActionsContextMenu + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + 48 + 48 + + + + QAbstractItemView::ScrollPerPixel + + + QListView::Adjust + + + 5 + + + QListView::ListMode + + + false + + + false + + + true + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Open + + + + + + + + @@ -221,7 +407,7 @@ - + 10 @@ -248,20 +434,6 @@ - - - - Create - - - - - - - Open - - - @@ -277,7 +449,8 @@ - + + diff --git a/src/dialogs/SaveProjectDialog.cpp b/src/dialogs/SaveProjectDialog.cpp new file mode 100644 index 00000000..6aba072d --- /dev/null +++ b/src/dialogs/SaveProjectDialog.cpp @@ -0,0 +1,104 @@ + +#include + +#include +#include "SaveProjectDialog.h" +#include "ui_SaveProjectDialog.h" + +SaveProjectDialog::SaveProjectDialog(bool quit, QWidget *parent) : + QDialog(parent), + ui(new Ui::SaveProjectDialog) +{ + ui->setupUi(this); + + CutterCore *core = CutterCore::getInstance(); + + if (quit) + { + ui->buttonBox->setStandardButtons(QDialogButtonBox::Save + | QDialogButtonBox::Discard + | QDialogButtonBox::Cancel); + } + else + { + ui->buttonBox->setStandardButtons(QDialogButtonBox::Save + | QDialogButtonBox::Cancel); + } + + ui->nameEdit->setText(core->getConfig("prj.name")); + ui->projectsDirEdit->setText(core->getConfig("dir.projects")); + ui->filesCheckBox->setChecked(core->getConfigb("prj.files")); + ui->gitCheckBox->setChecked(core->getConfigb("prj.git")); + ui->zipCheckBox->setChecked(core->getConfigb("prj.zip")); +} + +SaveProjectDialog::~SaveProjectDialog() +{ +} + +void SaveProjectDialog::on_selectProjectsDirButton_clicked() +{ + QFileDialog dialog(this); + dialog.setFileMode(QFileDialog::DirectoryOnly); + + QString currentDir = ui->projectsDirEdit->text(); + if(currentDir.startsWith("~")) + { + currentDir = QDir::homePath() + currentDir.mid(1); + } + dialog.setDirectory(currentDir); + + dialog.setWindowTitle(tr("Select project path (dir.projects)")); + + if(!dialog.exec()) + { + return; + } + + QString dir = dialog.selectedFiles().first(); + if (!dir.isEmpty()) + { + ui->projectsDirEdit->setText(dir); + } +} + +void SaveProjectDialog::on_buttonBox_clicked(QAbstractButton *button) +{ + switch(ui->buttonBox->buttonRole(button)) + { + case QDialogButtonBox::DestructiveRole: + QDialog::done(Destructive); + break; + + case QDialogButtonBox::RejectRole: + QDialog::done(Rejected); + break; + + default: + break; + } +} + +void SaveProjectDialog::accept() +{ + CutterCore *core = CutterCore::getInstance(); + core->setConfig("dir.projects", ui->projectsDirEdit->text().toUtf8().constData()); + core->setConfig("prj.files", ui->filesCheckBox->isChecked()); + core->setConfig("prj.git", ui->gitCheckBox->isChecked()); + core->setConfig("prj.zip", ui->zipCheckBox->isChecked()); + + QString projectName = ui->nameEdit->text().trimmed(); + if(!CutterCore::isProjectNameValid(projectName)) + { + QMessageBox::critical(this, tr("Save project"), tr("Invalid project name.")); + return; + } + + core->saveProject(projectName); + + QDialog::done(Saved); +} + +void SaveProjectDialog::reject() +{ +} diff --git a/src/dialogs/SaveProjectDialog.h b/src/dialogs/SaveProjectDialog.h new file mode 100644 index 00000000..f4fed151 --- /dev/null +++ b/src/dialogs/SaveProjectDialog.h @@ -0,0 +1,35 @@ +#ifndef SAVEPROJECTDIALOG_H +#define SAVEPROJECTDIALOG_H + +#include +#include + +#include + +namespace Ui +{ + class SaveProjectDialog; +} + +class SaveProjectDialog : public QDialog +{ + Q_OBJECT + +public: + enum Result { Saved, Rejected, Destructive }; + + explicit SaveProjectDialog(bool quit, QWidget *parent = 0); + ~SaveProjectDialog(); + + virtual void accept() override; + virtual void reject() override; + +private slots: + void on_buttonBox_clicked(QAbstractButton *button); + void on_selectProjectsDirButton_clicked(); + +private: + std::unique_ptr ui; +}; + +#endif // SAVEPROJECTDIALOG_H diff --git a/src/dialogs/SaveProjectDialog.ui b/src/dialogs/SaveProjectDialog.ui new file mode 100644 index 00000000..77b77174 --- /dev/null +++ b/src/dialogs/SaveProjectDialog.ui @@ -0,0 +1,140 @@ + + + SaveProjectDialog + + + + 0 + 0 + 506 + 227 + + + + Save Project + + + + QLayout::SetMinAndMaxSize + + + + + + + Project name (prj.name): + + + + + + + + + + + + Qt::Horizontal + + + + + + + + + Projects path (dir.projects): + + + + + + + false + + + + + + + + 0 + 0 + + + + Select + + + + + + + + + Save the target binary inside the project directory (prj.files) + + + + + + + Project is a git repo and saving is committing (prj.git) + + + + + + + Use ZIP format for project files (prj.zip) + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Save + + + + + + + + + buttonBox + accepted() + SaveProjectDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + SaveProjectDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/main.cpp b/src/main.cpp index 0d0cbf13..d5890aec 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -82,7 +82,7 @@ int main(int argc, char *argv[]) else // filename specified as positional argument { MainWindow *main = new MainWindow(); - main->openFile(args[0], anal_level_specified ? anal_level : -1); + main->openNewFile(args[0], anal_level_specified ? anal_level : -1); } return a.exec(); diff --git a/src/utils/Helpers.cpp b/src/utils/Helpers.cpp index cfd79cf9..080ac672 100644 --- a/src/utils/Helpers.cpp +++ b/src/utils/Helpers.cpp @@ -39,12 +39,6 @@ namespace qhelpers #endif } - QString uniqueProjectName(const QString &filename) - { - const QByteArray fullHash(QCryptographicHash::hash(filename.toUtf8(), QCryptographicHash::Sha1)); - return QFileInfo(filename).fileName() + "_" + fullHash.toHex().left(10); - } - void adjustColumns(QTreeWidget *tw, int columnCount, int padding) { const int count = columnCount == 0 ? tw->columnCount() : columnCount; diff --git a/src/utils/Helpers.h b/src/utils/Helpers.h index 19a16fbd..fba10672 100644 --- a/src/utils/Helpers.h +++ b/src/utils/Helpers.h @@ -16,8 +16,6 @@ namespace qhelpers void normalizeFont(QPlainTextEdit *edit); void normalizeEditFont(QTextEdit *edit); - QString uniqueProjectName(const QString &filename); - void adjustColumns(QTreeWidget *tw, int columnCount = 0, int padding = 0); QTreeWidgetItem *appendRow(QTreeWidget *tw, const QString &str, const QString &str2 = QString(), diff --git a/src/widgets/Notepad.cpp b/src/widgets/Notepad.cpp index 2fb0c44d..a698e417 100644 --- a/src/widgets/Notepad.cpp +++ b/src/widgets/Notepad.cpp @@ -15,7 +15,7 @@ Notepad::Notepad(MainWindow *main, QWidget *parent) : - DockWidget(parent), + QDockWidget(parent), ui(new Ui::Notepad) { ui->setupUi(this); @@ -40,35 +40,15 @@ Notepad::Notepad(MainWindow *main, QWidget *parent) : ui->notepadTextEdit->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui->notepadTextEdit, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(showNotepadContextMenu(const QPoint &))); + connect(ui->notepadTextEdit, SIGNAL(textChanged()), this, SLOT(textChanged())); + + connect(CutterCore::getInstance(), SIGNAL(notesChanged(const QString &)), this, SLOT(updateNotes(const QString &))); + + updateNotes(CutterCore::getInstance()->getNotes()); } Notepad::~Notepad() {} -void Notepad::setup() -{ -} - -void Notepad::refresh() -{ - // TODO: implement - eprintf("%s - not implemented\n", Q_FUNC_INFO); -} - -void Notepad::setText(const QString &str) -{ - ui->notepadTextEdit->setPlainText(str); -} - -QString Notepad::textToBase64() const -{ - return notesTextEdit->toPlainText().toUtf8().toBase64(); -} - -void Notepad::appendPlainText(const QString &text) -{ - notesTextEdit->appendPlainText(text); -} - void Notepad::on_fontButton_clicked() { bool ok = true; @@ -359,3 +339,18 @@ void Notepad::on_actionHexdump_function_triggered() { ui->previewTextEdit->setPlainText(CutterCore::getInstance()->cmd("pxf @ " + this->addr)); } + +void Notepad::updateNotes(const QString ¬es) +{ + disconnect(ui->notepadTextEdit, SIGNAL(textChanged()), this, SLOT(textChanged())); + ui->notepadTextEdit->setPlainText(notes); + connect(ui->notepadTextEdit, SIGNAL(textChanged()), this, SLOT(textChanged())); +} + +void Notepad::textChanged() +{ + CutterCore *core = CutterCore::getInstance(); + disconnect(core, SIGNAL(notesChanged(const QString &)), this, SLOT(updateNotes(const QString &))); + core->setNotes(ui->notepadTextEdit->toPlainText()); + connect(core, SIGNAL(notesChanged(const QString &)), this, SLOT(updateNotes(const QString &))); +} diff --git a/src/widgets/Notepad.h b/src/widgets/Notepad.h index 2dd8e7a0..5a5b1cf8 100644 --- a/src/widgets/Notepad.h +++ b/src/widgets/Notepad.h @@ -1,8 +1,8 @@ #ifndef NOTEPAD_H #define NOTEPAD_H -#include "DockWidget.h" #include +#include class MainWindow; class MdHighlighter; @@ -14,7 +14,7 @@ namespace Ui class Notepad; } -class Notepad : public DockWidget +class Notepad : public QDockWidget { Q_OBJECT @@ -22,15 +22,6 @@ public: explicit Notepad(MainWindow *main, QWidget *parent = 0); ~Notepad(); - void setup() override; - - void refresh() override; - - void setText(const QString &str); - QString textToBase64() const; - - void appendPlainText(const QString &text); - void highlightPreview(); public slots: @@ -38,39 +29,31 @@ public slots: private slots: void on_fontButton_clicked(); - void on_boldButton_clicked(); - void on_italicsButton_clicked(); void on_h1Button_clicked(); - void on_h2Button_clicked(); - void on_h3Button_clicked(); void on_undoButton_clicked(); - void on_redoButton_clicked(); void on_searchEdit_returnPressed(); - void on_searchEdit_textEdited(const QString &arg1); - void on_searchEdit_textChanged(const QString &arg1); void showNotepadContextMenu(const QPoint &pt); void on_actionDisassmble_bytes_triggered(); - void on_actionDisassmble_function_triggered(); - void on_actionHexdump_bytes_triggered(); - void on_actionCompact_Hexdump_triggered(); - void on_actionHexdump_function_triggered(); + void updateNotes(const QString ¬es); + void textChanged(); + private: std::unique_ptr ui; MdHighlighter *highlighter;