diff --git a/NEWS.md b/NEWS.md index 09f3a0c5..7ed65eda 100644 --- a/NEWS.md +++ b/NEWS.md @@ -7,6 +7,7 @@ Version 0.7.0 - enhancement: set mouse wheel scroll speed in the editor general option tab ( 3 lines by default) - fix: don't highlight '#' with spaces preceeding it as error - fix: correctly handle integer with 'L' suffix in #if directives ( so can be correctly parsed ) + - enhancement: bookmark view Version 0.6.8 - enhancement: add link to cppreference in the help menu diff --git a/RedPandaIDE/RedPandaIDE_zh_CN.qm b/RedPandaIDE/RedPandaIDE_zh_CN.qm index 6eab60c8..0119b85b 100644 Binary files a/RedPandaIDE/RedPandaIDE_zh_CN.qm and b/RedPandaIDE/RedPandaIDE_zh_CN.qm differ diff --git a/RedPandaIDE/RedPandaIDE_zh_CN.ts b/RedPandaIDE/RedPandaIDE_zh_CN.ts index 342ccfc1..11ab5320 100644 --- a/RedPandaIDE/RedPandaIDE_zh_CN.ts +++ b/RedPandaIDE/RedPandaIDE_zh_CN.ts @@ -95,35 +95,73 @@ BacktraceModel - + Function 函数 - + Filename 文件名 - + Line - BreakpointModel + BookmarkModel - - Filename - 文件名 + + Save file '%1' failed. + 保存文件'%1'失败。 - + + Can't open file '%1' for write. + 无法写入文件'%1'. + + + + Error in json file '%1':%2 : %3 + JSON文件'%1':%2中存在错误:%3 + + + + Can't open file '%1' for read. + 无法读取文件'%1'. + + + + Description + 描述 + + + Line + + + Filename + 文件名 + + + + BreakpointModel + Filename + 文件名 + + + + Line + + + + Condition 条件 @@ -954,10 +992,10 @@ Are you really want to continue? - - - - + + + + Error 错误 @@ -972,55 +1010,55 @@ Are you really want to continue? 另存为 - + The text to be copied exceeds count limit! 要复制的内容超过了行数限制! - + The text to be copied exceeds character limit! 要复制的内容超过了字符数限制! - + The text to be cut exceeds count limit! 要剪切的内容超过了行数限制! - + The text to be cut exceeds character limit! 要剪切的内容超过了字符数限制! - + Print Document 打印文档 - - - + + + Ctrl+click for more info Ctrl+单击以获取更多信息 - - + + Symbol '%1' not found! 未找到符号'%1'! - + Break point condition 断点条件 - + Enter the condition of the breakpoint: 输入当前断点的生效条件: - + Readonly 只读 @@ -2728,50 +2766,50 @@ Are you really want to continue? 小熊猫C++ - - + + Issues 编译器 - + Compile Log 编译日志 - + File 文件 - + Tools 工具 - - + + Run 运行 - + Edit 编辑 - - + + Project 项目 - + Watch 监视 - + Structure 结构 @@ -2784,82 +2822,82 @@ Are you really want to continue? 资源 - - - - + + + + Debug 调试 - + Evaluate: 求值 - + Debug Console 调试主控台 - + Call Stack 调用栈 - + Breakpoints 断点 - + Locals 本地变量 - - + + Search 查找 - + History: 历史: - + Search Again 重新查找 - + Replace with: 替换为: - + Replace 替换 - + Close 关闭 - + Execute 运行 - - + + Code 代码 - + Window 窗口 @@ -2877,660 +2915,681 @@ Are you really want to continue? 新建 - + Ctrl+N Ctrl+N - + Open... 打开... - + Ctrl+O Ctrl+O - + Save 保存 - + Ctrl+S Ctrl+S - + Save As... 另存为... - + Save As 另存为 - + Save All 全部保存 - + Ctrl+Shift+S Ctrl+Shift+S - + Options 选项 - - - - - - - + + + + + + + Compile 编译 - + F9 F9 - + F10 F10 - + Undo 恢复 - + Ctrl+Z Ctrl+Z - + Redo 重做 - + Ctrl+Y Ctrl+Y - + Cut 剪切 - + Ctrl+X Ctrl+X - - - + + + Copy 复制 - + Ctrl+C Ctrl+C - - + + Paste 粘贴 - + Ctrl+V Ctrl+V - - + + Select All 选择全部 - + Ctrl+A Ctrl+A - + Indent 缩进 - + UnIndent 取消缩进 - + Toggle Comment 切换注释 - + Ctrl+/ Ctrl+/ - + Collapse All 全部收起 - + Uncollapse All 全部展开 - + Encode in ANSI 使用ANSI编码 - + Encode in UTF-8 使用UTF-8编码 - + Auto Detect 自动检测 - + Convert to ANSI 转换为ANSI编码 - + Convert to UTF-8 转换为UTF-8编码 - - + + Compile & Run 编译运行 - + F11 F11 - - + + Rebuild All 全部重编译 - + F12 F12 - + Stop Execution 停止执行 - + F6 F6 - + F5 F5 - + Step Over 单步跳过 - + F7 F7 - + Step Into 单步进入 - + Memory 内存 - + Address Expression: Address: 地址表达式: - + Cancel 取消 - + TODO TODO - + + Bookmark + 书签 + + + Help 帮助 - + Refactor 重构 - + Main 主工具栏 - + Compiler Set 编译器配置集 - - + + New Source File 新建源代码文件 - + Tab Tab - + Shift+Tab Shift+Tab - + F8 F8 - + Step Out 单步跳出 - + Ctrl+F8 Ctrl+F8 - + Run To Cursor 执行到光标处 - + Ctrl+F5 Ctrl+F5 - + Continue 继续执行 - + F4 F4 - + Add Watch... 添加监视 - + View CPU Window... 打开CPU信息窗口... - + Exit 退出 - + Find... 查找... - + Ctrl+F Ctrl+F - + Find in Files... 在文件中查找... - + Ctrl+Shift+F Ctrl+Shift+F - + Replace... 替换 - + Ctrl+R Ctrl+R - + Find Next 查找下一个 - + F3 F3 - + Find Previous 查找前一个 - + Shift+F3 Shift+F3 - + Remove Watch 删除监视 - + + Remove All 清除全部监视 - + Modify Watch... 修改监视值 - + Reformat Code 对代码重新排版 - + Ctrl+Shift+A Ctrl+Shift+A - + Go back 前一次编辑位置 - + Ctrl+Alt+Left Ctrl+Alt+Left - + Forward 后一次编辑位置 - + Ctrl+Alt+Right Ctrl+Alt+Right - + Ctrl+W Ctrl+W - + Close All 全部关闭 - + Ctrl+Shift+W Ctrl+Shift+W - + Maximize Editor 最大化编辑器 - + Ctrl+F11 Ctrl+F11 - + Next 下一窗口 - + Ctrl+Tab Ctrl+Tab - + Previous 前一窗口 - + Ctrl+Shift+Tab Ctrl+Shift+Tab - + Toggle breakpoint 切换断点 - + Ctrl+F4 Ctrl+F4 - - + + Clear all breakpoints 删除所有断点 - + Breakpoint property... 设置断点条件... - + Goto Declaration 跳转到声明处 - + Goto Definition 跳转到定义处 - + Find references 查找符号的引用 - + Open containing folder 打开所在的文件夹 - + Ctrl+B Ctrl+B - + Open a terminal here 打开命令行窗口 - + File Properties... 文件属性... - + Close Project 关闭项目 - + Project options 项目属性 - + New Project... 新建项目... - + New File 新建项目文件 - + Add to project... 添加到项目... - + Remove from project 从项目删除 - + View Makefile 查看Makefile - + Clean 清理构建文件 - + Open Folder in Explorer 在浏览器中打开 - + Open In Terminal 在终端中打开 - + About 关于 - - + + Rename Symbol 重命名符号 - + Shift+F6 Shift+F6 - + Print... 打印... - + Ctrl+P Ctrl+P - - + + Export As RTF 导出为RTF - - + + Export As HTML 导出为HTML - + Move To Other View 移动到其他视图 - + Ctrl+M Ctrl+M - + C/C++ Reference C/C++参考 - + EGE Manual EGE图形库手册 - + + Add Bookmark + 添加书签 + + + + Remove Bookmark + 删除书签 + + + + Modify Bookmark Description + 修改书签说明 + + + File Encoding 文件编码 @@ -3540,32 +3599,32 @@ Are you really want to continue? 文件历史 - - - - - - + + + + + + Debugging 正在调试 - - - - - - + + + + + + Running 正在运行 - - - - - - + + + + + + Compiling 正在编译 @@ -3574,168 +3633,168 @@ Are you really want to continue? 行:%1 列:%2 已选择:%3 总行数:%4 总长度:%5 - + Line:%1 Col:%2 Selected:%3 Lines:%4 Length:%5 Line: %1 Col: %2 Selected: %3 Lines: %4 Length: %5 行: %1 列: %2 已选择 :%3 总行数: %4 总长度: %5 - + Read Only 只读 - + Insert 插入 - + Overwrite 覆写 - + Close project 关闭项目 - + Are you sure you want to close %1? 你确定要关闭'%1'吗? - - + + Confirm 确认 - - - + + + Source file is not compiled. 源文件尚未编译。 - - + + Compile now? 现在编译? - - - + + + Source file is more recent than executable. 源文件比可执行程序新。 - + Recompile now? 重新编译? - + No compiler set 无编译器设置 - + No compiler set is configured. 没有配置编译器设置。 - + Can't start debugging. 无法启动调试器 - - + + Enable debugging 启用调试参数 - - + + You have not enabled debugging info (-g3) and/or stripped it from the executable (-s) in Compiler Options.<BR /><BR />Do you want to correct this now? 当前编译设置中未启用调试选项(-g3),或启用了信息剥除选项(-s)<br /><br/>是否纠正这一问题? - + Project not built 项目尚未构建 - + Project hasn't been built. Build it now? 项目尚未构建。是否构建? - + Host applcation missing 宿主程序不存在 - + DLL project needs a host application to run. 动态链接库(DLL)需要一个宿主程序来运行。 - + But it's missing. 但它不存在。 - + Host application not exists 宿主程序不存在 - + Host application file '%1' doesn't exist. 宿主程序'%1'不存在。 - + Recompile? 重新编译? - - + + Save last open info error 保存上次打开信息失败 - + Can't remove old last open information file '%1' 无法删除旧上次打开信息文件'%1' - + Can't save last open info file '%1' 无法保存上次打开信息文件'%1' - + Load last open info error 载入上次打开信息失败 - + Can't load last open info file '%1' 无法载入上次打开信息文件'%1' - + Copy all 全部复制 - - + + Clear 清除 @@ -3750,232 +3809,256 @@ Are you really want to continue? 插入代码段 - + + Remove + 删除 + + + + Modify Description + 修改描述 + + + + + + Bookmark Description + 书签描述 + + + + + + Description: + 描述: + + + Show debug logs in the debug console 在调试主控台中显示调试器输出 - + Remove this search 清除这次搜索 - + Clear all searches 删除所有搜索 - + Breakpoint condition... 断点条件... - + Break point condition 断点条件 - + Enter the condition of the breakpoint: 输入当前断点的生效条件: - + Remove all breakpoints 清除所有断点 - + Rename File 重命名文件 - - + + Add Folder 添加文件夹 - + New folder 新文件夹 - + Folder name: 文件夹: - + Rename Folder 重命名 - + Remove Folder 删除文件夹 - + Sort By Type 按类型排序 - + Sort alphabetically 按名称排序 - + Show inherited members 显示继承的成员 - + Goto declaration 跳转到声明处 - + Goto definition 跳转到定义处 - + Character sets 字符集 - + %1 files autosaved 已自动保存%1个文件 - + Save project 保存项目 - + The project '%1' has modifications. 项目'%1'有改动。 - + Do you want to save it? 需要保存吗? - + Do you really want to clear all breakpoints in this file? 您真的要清除该文件的所有断点吗? - + New project 新建项目 - + Close %1 and start new project? 关闭'%1'以打开新项目? - + Folder not exist 文件夹不存在 - + Folder '%1' doesn't exist. Create it now? 文件夹'%1'不存在。是否创建? - + Can't create folder 无法创建文件夹 - + Failed to create folder '%1'. 创建文件夹'%1'失败。 - + Save new project as - + Red panda Dev-C++ project file (*.dev) 小熊猫Dev-C++项目文件 (*.dev) - + New project fail 新建项目失败 - + Can't assign project template 无法使用模板创建项目 - + Add to project 添加到项目 - + Rename Error 重命名出错 - + Symbol '%1' is defined in system header. 符号'%1'在系统头文件中定义,无法修改。 - + New Name 新名称 - - + + Replace Error 替换出错 - + Can't open file '%1' for replace! 无法打开文件'%1'进行替换! - + Contents has changed since last search! 内容和上次查找时不一致。 - + Rich Text Format Files (*.rtf) RTF格式文件 (*.rtf) - + HTML Files (*.html) HTML文件 (*.html) - - - - - - - - - + + + + + + + + + Error 错误 @@ -3985,75 +4068,75 @@ Are you really want to continue? 项目历史 - + File '%1' was changed. 磁盘文件'%1'已被修改。 - + Reload its content from disk? 是否重新读取它的内容? - + File '%1' was removed. 磁盘文件'%1'已被删除。 - + Keep it open? 是否保持它在小熊猫C++中打开的编辑窗口? - + Open 打开 - + Compile Failed 编译失败 - + Run Failed 运行失败 - - + + Confirm Convertion 确认转换 - - + + The editing file will be saved using %1 encoding. <br />This operation can't be reverted. <br />Are you sure to continue? 当前编辑器中的文件将会使用%1编码保存。<br />这项操作无法被撤回。<br />你确定要继续吗? - + New Watch Expression 新监视表达式 - + Enter Watch Expression (it is recommended to use 'this->' for class members): 输入监视表达式 - + Parsing file %1 of %2: "%3" (%1/%2)正在解析文件"%3" - - + + Done parsing %1 files in %2 seconds 完成%1个文件的解析,用时%2秒 - + (%1 files per second) (每秒%1个文件) @@ -4135,62 +4218,62 @@ Are you really want to continue? 无法保存文件'%1'. - + File Exists 文件已存在 - + File '%1' is already in the project 文件'%1'已在项目中 - + Project Updated 项目已升级 - + Your project was succesfully updated to a newer file format! 已成功将项目升级到新的格式 - + If something has gone wrong, we kept a backup-file: '%1'... 旧项目文件备份在'%1'。 - + Settings need update 设置需要更新 - + The compiler settings format of Dev-C++ has changed. Dev-C++的编译器设置格式已发生改变。 - + Please update your settings at Project >> Project Options >> Compiler and save your project. 请在项目 >> 项目属性 >> 编译器设置中修改您的设置并保存您的项目 - + Compiler not found 未找到编译器 - + The compiler set you have selected for this project, no longer exists. 您为该项目设置的编译器不存在。 - + It will be substituted by the global compiler set. 它将会被全局编译器设置代替。 - + Developed using the Red Panda Dev-C++ IDE 使用小熊猫Dev-C++编辑器开发 @@ -4576,24 +4659,24 @@ Are you really want to continue? ProjectModel - + File exists 文件已存在 - + File '%1' already exists. Delete it now? 文件'%1'已存在。是否删除? - - + + Remove failed 删除失败 - - + + Failed to remove file '%1' 无法删除文件'%1' @@ -4820,12 +4903,12 @@ Are you really want to continue? QObject - + Save 保存 - + Save changes to %1? 将修改保存到"%1"? @@ -5459,17 +5542,17 @@ Are you really want to continue? RegisterModel - + Register 寄存器 - + Value(Hex) 值(HEX) - + Value(Dec) 值(DEC) @@ -6127,8 +6210,8 @@ Are you really want to continue? SynEdit - - + + The highlighter seems to be in an infinite loop 高亮处理进入了死循环 @@ -6297,12 +6380,12 @@ Are you really want to continue? WatchModel - + Expression 表达式 - + Value @@ -6417,7 +6500,7 @@ Are you really want to continue? Mouse Wheel Scroll Speed - 鼠标滚轮卷轴速度(行) + 鼠标滚轮卷轴速度(行) diff --git a/RedPandaIDE/debugger.cpp b/RedPandaIDE/debugger.cpp index 6343e09b..ee803d5a 100644 --- a/RedPandaIDE/debugger.cpp +++ b/RedPandaIDE/debugger.cpp @@ -1812,32 +1812,30 @@ PBreakpoint BreakpointModel::breakpoint(int index) const void BreakpointModel::onFileDeleteLines(const QString &filename, int startLine, int count) { - beginResetModel(); for (int i = mList.count()-1;i>=0;i--){ PBreakpoint breakpoint = mList[i]; if (breakpoint->filename == filename && breakpoint->line>=startLine) { if (breakpoint->line >= startLine+count) { breakpoint->line -= count; + emit dataChanged(createIndex(i,0),createIndex(i,2)); } else { - mList.removeAt(i); + removeBreakpoint(i); } } } - endResetModel(); } void BreakpointModel::onFileInsertLines(const QString &filename, int startLine, int count) { - beginResetModel(); for (int i = mList.count()-1;i>=0;i--){ PBreakpoint breakpoint = mList[i]; if (breakpoint->filename == filename && breakpoint->line>=startLine) { breakpoint->line+=count; + emit dataChanged(createIndex(i,0),createIndex(i,2)); } } - endResetModel(); } diff --git a/RedPandaIDE/editor.cpp b/RedPandaIDE/editor.cpp index 497b6006..3cb29d6e 100644 --- a/RedPandaIDE/editor.cpp +++ b/RedPandaIDE/editor.cpp @@ -779,6 +779,9 @@ void Editor::onGutterPaint(QPainter &painter, int aLine, int X, int Y) } return; } + if (hasBookmark(aLine)) { + painter.drawPixmap(X,Y,*(pIconsManager->bookmark())); + } } } @@ -1500,7 +1503,9 @@ void Editor::onLinesDeleted(int first, int count) { pMainWindow->caretList().linesDeleted(this,first,count); pMainWindow->debugger()->breakpointModel()->onFileDeleteLines(mFilename,first,count); + pMainWindow->bookmarkModel()->onFileDeleteLines(mFilename,first,count); resetBreakpoints(); + resetBookmarks(); if (!pSettings->editor().syntaxCheckWhenLineChanged()) { //todo: update syntax issues } @@ -1510,7 +1515,9 @@ void Editor::onLinesInserted(int first, int count) { pMainWindow->caretList().linesInserted(this,first,count); pMainWindow->debugger()->breakpointModel()->onFileInsertLines(mFilename,first,count); + pMainWindow->bookmarkModel()->onFileInsertLines(mFilename,first,count); resetBreakpoints(); + resetBookmarks(); if (!pSettings->editor().syntaxCheckWhenLineChanged()) { //todo: update syntax issues } @@ -1531,6 +1538,12 @@ bool Editor::isBraceChar(QChar ch) } } +void Editor::resetBookmarks() +{ + mBookmarkLines=pMainWindow->bookmarkModel()->bookmarksInFile(mFilename); + invalidate(); +} + void Editor::resetBreakpoints() { mBreakpointLines.clear(); @@ -3523,6 +3536,25 @@ bool Editor::hasBreakpoint(int line) return mBreakpointLines.contains(line); } +void Editor::addBookmark(int line, const QString& description) +{ + mBookmarkLines.insert(line); + pMainWindow->bookmarkModel()->addBookmark(mFilename,line,description); + invalidateGutterLine(line); +} + +void Editor::removeBookmark(int line) +{ + mBookmarkLines.remove(line); + pMainWindow->bookmarkModel()->removeBookmark(mFilename,line); + invalidateGutterLine(line); +} + +bool Editor::hasBookmark(int line) +{ + return mBookmarkLines.contains(line); +} + void Editor::removeBreakpointFocus() { if (mActiveBreakpointLine!=-1) { diff --git a/RedPandaIDE/editor.h b/RedPandaIDE/editor.h index c433135e..160fcafd 100644 --- a/RedPandaIDE/editor.h +++ b/RedPandaIDE/editor.h @@ -147,6 +147,9 @@ public: void toggleBreakpoint(int line); void clearBreakpoints(); bool hasBreakpoint(int line); + void addBookmark(int line,const QString& description); + void removeBookmark(int line); + bool hasBookmark(int line); void removeBreakpointFocus(); void modifyBreakpointProperty(int line); void setActiveBreakpointFocus(int Line, bool setFocus=true); @@ -177,6 +180,7 @@ private slots: private: bool isBraceChar(QChar ch); + void resetBookmarks(); void resetBreakpoints(); QChar getCurrentChar(); bool handleSymbolCompletion(QChar key); @@ -242,6 +246,7 @@ private: int mLineCount; int mGutterClickedLine; QSet mBreakpointLines; + QSet mBookmarkLines; int mActiveBreakpointLine; PCppParser mParser; std::shared_ptr mCompletionPopup; diff --git a/RedPandaIDE/icons.qrc b/RedPandaIDE/icons.qrc index 7cfe07e6..e33ad503 100644 --- a/RedPandaIDE/icons.qrc +++ b/RedPandaIDE/icons.qrc @@ -486,5 +486,6 @@ images/classparser/var_private.ico images/classparser/var_protected.ico images/classparser/var_public.ico + images/editor/bookmark.png diff --git a/RedPandaIDE/iconsmanager.cpp b/RedPandaIDE/iconsmanager.cpp index 3b644512..52f4f8bb 100644 --- a/RedPandaIDE/iconsmanager.cpp +++ b/RedPandaIDE/iconsmanager.cpp @@ -8,6 +8,8 @@ IconsManager::IconsManager(QObject *parent) : QObject(parent) mSyntaxWarning = std::make_shared(":/icons/images/editor/syntaxwarning.png"); mBreakpoint = std::make_shared(":/icons/images/editor/breakpoint.png"); mActiveBreakpoint = std::make_shared(":/icons/images/editor/currentline.png"); + mBookmark = std::make_shared(":/icons/images/editor/bookmark.png"); + } PIcon IconsManager::syntaxError() const @@ -29,3 +31,8 @@ PIcon IconsManager::activeBreakpoint() const { return mActiveBreakpoint; } + +const PIcon &IconsManager::bookmark() const +{ + return mBookmark; +} diff --git a/RedPandaIDE/iconsmanager.h b/RedPandaIDE/iconsmanager.h index ed4623fb..5a84f44c 100644 --- a/RedPandaIDE/iconsmanager.h +++ b/RedPandaIDE/iconsmanager.h @@ -20,12 +20,15 @@ public: PIcon activeBreakpoint() const; + const PIcon &bookmark() const; + signals: private: PIcon mSyntaxError; PIcon mSyntaxWarning; PIcon mBreakpoint; PIcon mActiveBreakpoint; + PIcon mBookmark; }; extern IconsManager* pIconsManager; diff --git a/RedPandaIDE/mainwindow.cpp b/RedPandaIDE/mainwindow.cpp index 90653968..9525a2be 100644 --- a/RedPandaIDE/mainwindow.cpp +++ b/RedPandaIDE/mainwindow.cpp @@ -149,6 +149,10 @@ MainWindow::MainWindow(QWidget *parent) mCodeSnippetManager->load(); mToolsManager = std::make_shared(); mToolsManager->load(); + mBookmarkModel = std::make_shared(); + mBookmarkModel->load(includeTrailingPathDelimiter(pSettings->dirs().config()) + +DEV_BOOKMARK_FILE); + ui->tableBookmark->setModel(mBookmarkModel.get()); mSearchResultTreeModel = std::make_shared(&mSearchResultModel); mSearchResultListModel = std::make_shared(&mSearchResultModel); mSearchViewDelegate = std::make_shared(mSearchResultTreeModel); @@ -267,6 +271,10 @@ void MainWindow::updateEditorActions() ui->actionClose->setEnabled(false); ui->actionClose_All->setEnabled(false); + + ui->actionAdd_bookmark->setEnabled(false); + ui->actionRemove_Bookmark->setEnabled(false); + ui->actionModify_Bookmark_Description->setEnabled(false); } else { ui->actionAuto_Detect->setEnabled(true); ui->actionEncode_in_ANSI->setEnabled(true); @@ -302,7 +310,12 @@ void MainWindow::updateEditorActions() ui->actionClose->setEnabled(true); ui->actionClose_All->setEnabled(true); - } + + int line = e->caretY(); + ui->actionAdd_bookmark->setEnabled(e->lines()->count()>0 && !e->hasBookmark(line)); + ui->actionRemove_Bookmark->setEnabled(e->hasBookmark(line)); + ui->actionModify_Bookmark_Description->setEnabled(e->hasBookmark(line)); + } updateCompileActions(); @@ -1478,6 +1491,15 @@ void MainWindow::includeOrSkipDirs(const QStringList &dirs, bool skip) } } +void MainWindow::onBookmarkContextMenu(const QPoint &pos) +{ + QMenu menu(this); + menu.addAction(mBookmark_Remove); + menu.addAction(mBookmark_RemoveAll); + menu.addAction(mBookmark_Modify); + menu.exec(ui->tableBookmark->mapToGlobal(pos)); +} + void MainWindow::saveLastOpens() { QString filename = includeTrailingPathDelimiter(pSettings->dirs().config()) + DEV_LASTOPENS_FILE; @@ -1622,6 +1644,45 @@ void MainWindow::buildContextMenus() connect(ui->watchView,&QWidget::customContextMenuRequested, this, &MainWindow::onWatchViewContextMenu); + //context menu signal for the bookmark view + ui->tableBookmark->setContextMenuPolicy(Qt::CustomContextMenu); + connect(ui->tableBookmark,&QWidget::customContextMenuRequested, + this, &MainWindow::onBookmarkContextMenu); + mBookmark_Remove=createActionFor( + tr("Remove"), + ui->tableBookmark); + connect(mBookmark_Remove, &QAction::triggered, + [this]() { + QModelIndex index = ui->tableBookmark->currentIndex(); + if (index.isValid()) { + mBookmarkModel->removeBookmarkAt(index.row()); + } + }); + mBookmark_RemoveAll=createActionFor( + tr("Remove All"), + ui->tableBookmark); + connect(mBookmark_RemoveAll, &QAction::triggered, + [this]() { + mBookmarkModel->clear(); + }); + mBookmark_Modify=createActionFor( + tr("Modify Description"), + ui->tableBookmark); + connect(mBookmark_Modify, &QAction::triggered, + [this]() { + QModelIndex index = ui->tableBookmark->currentIndex(); + if (index.isValid()) { + PBookmark bookmark = mBookmarkModel->bookmark(index.row()); + if (bookmark) { + QString desc = QInputDialog::getText(ui->tableBookmark,tr("Bookmark Description"), + tr("Description:"),QLineEdit::Normal, + bookmark->description); + desc = desc.trimmed(); + mBookmarkModel->updateDescription(bookmark->filename,bookmark->line,desc); + } + mBookmarkModel->clear(); + }); + //context menu signal for the watch view ui->debugConsole->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui->debugConsole,&QWidget::customContextMenuRequested, @@ -2271,7 +2332,9 @@ void MainWindow::onEditorContextMenu(const QPoint &pos) QMenu menu(this); BufferCoord p; mEditorContextMenuPos = pos; + int line; if (editor->getPositionOfMouse(p)) { + line=p.Line; //mouse on editing area menu.addAction(ui->actionCompile_Run); menu.addAction(ui->actionDebug); @@ -2296,6 +2359,10 @@ void MainWindow::onEditorContextMenu(const QPoint &pos) menu.addAction(ui->actionToggle_Breakpoint); menu.addAction(ui->actionClear_all_breakpoints); menu.addSeparator(); + menu.addAction(ui->actionAdd_bookmark); + menu.addAction(ui->actionRemove_Bookmark); + menu.addAction(ui->actionModify_Bookmark_Description); + menu.addSeparator(); menu.addAction(ui->actionFile_Properties); //these actions needs parser @@ -2304,16 +2371,25 @@ void MainWindow::onEditorContextMenu(const QPoint &pos) ui->actionFind_references->setEnabled(!editor->parser()->parsing()); } else { //mouse on gutter - int line; + if (!editor->getLineOfMouse(line)) line=-1; menu.addAction(ui->actionToggle_Breakpoint); menu.addAction(ui->actionBreakpoint_property); menu.addAction(ui->actionClear_all_breakpoints); - ui->actionBreakpoint_property->setEnabled(editor->hasBreakpoint(line)); + menu.addSeparator(); + menu.addAction(ui->actionAdd_bookmark); + menu.addAction(ui->actionRemove_Bookmark); + menu.addAction(ui->actionModify_Bookmark_Description); } + ui->actionBreakpoint_property->setEnabled(editor->hasBreakpoint(line)); + ui->actionAdd_bookmark->setEnabled( + line>=0 && editor->lines()->count()>0 + && !editor->hasBreakpoint(line) + ); + ui->actionRemove_Bookmark->setEnabled(editor->hasBreakpoint(line)); + ui->actionModify_Bookmark_Description->setEnabled(editor->hasBreakpoint(line)); menu.exec(editor->viewport()->mapToGlobal(pos)); - } void MainWindow::onEditorRightTabContextMenu(const QPoint &pos) @@ -2617,6 +2693,8 @@ void MainWindow::closeEvent(QCloseEvent *event) { settings.setLeftPanelIndex(ui->tabInfos->currentIndex()); settings.setLeftPanelOpenned(mLeftPanelOpenned); settings.save(); + mBookmarkModel->save(includeTrailingPathDelimiter(pSettings->dirs().config()) + +DEV_BOOKMARK_FILE); } if (!mShouldRemoveAllSettings && pSettings->editor().autoLoadLastFiles()) { @@ -4313,3 +4391,65 @@ void MainWindow::on_actionEGE_Manual_triggered() QDesktopServices::openUrl(QUrl("https://xege.org/ege-open-source")); } +const PBookmarkModel &MainWindow::bookmarkModel() const +{ + return mBookmarkModel; +} + + +void MainWindow::on_actionAdd_bookmark_triggered() +{ + Editor* editor = mEditorList->getEditor(); + int line; + if (editor && editor->pointToLine(mEditorContextMenuPos,line)) { + if (editor->lines()->count()<=0) + return; + QString desc = QInputDialog::getText(editor,tr("Bookmark Description"), + tr("Description:"),QLineEdit::Normal, + editor->lines()->getString(line-1).trimmed()); + desc = desc.trimmed(); + editor->addBookmark(line,desc); + } +} + + +void MainWindow::on_actionRemove_Bookmark_triggered() +{ + Editor* editor = mEditorList->getEditor(); + int line; + if (editor && editor->pointToLine(mEditorContextMenuPos,line)) { + editor->removeBookmark(line); + } +} + + +void MainWindow::on_tableBookmark_doubleClicked(const QModelIndex &index) +{ + if (!index.isValid()) + return; + PBookmark bookmark = mBookmarkModel->bookmark(index.row()); + if (bookmark) { + Editor *editor= mEditorList->getEditorByFilename(bookmark->filename); + if (editor) { + editor->setCaretPositionAndActivate(bookmark->line,1); + } + } +} + + +void MainWindow::on_actionModify_Bookmark_Description_triggered() +{ + Editor* editor = mEditorList->getEditor(); + int line; + if (editor && editor->pointToLine(mEditorContextMenuPos,line)) { + PBookmark bookmark = mBookmarkModel->bookmark(editor->filename(),line); + if (bookmark) { + QString desc = QInputDialog::getText(editor,tr("Bookmark Description"), + tr("Description:"),QLineEdit::Normal, + bookmark->description); + desc = desc.trimmed(); + mBookmarkModel->updateDescription(editor->filename(),line,desc); + } + } +} + diff --git a/RedPandaIDE/mainwindow.h b/RedPandaIDE/mainwindow.h index 23bd9ca2..0689075c 100644 --- a/RedPandaIDE/mainwindow.h +++ b/RedPandaIDE/mainwindow.h @@ -16,6 +16,7 @@ #include "todoparser.h" #include "toolsmanager.h" #include "widgets/labelwithmenu.h" +#include "widgets/bookmarkmodel.h" QT_BEGIN_NAMESPACE @@ -137,6 +138,8 @@ public: bool shouldRemoveAllSettings() const; + const PBookmarkModel &bookmarkModel() const; + public slots: void onCompileLog(const QString& msg); void onCompileIssue(PCompileIssue issue); @@ -194,6 +197,7 @@ private slots: void onFileChanged(const QString& path); void onWatchViewContextMenu(const QPoint& pos); + void onBookmarkContextMenu(const QPoint& pos); void onTableIssuesContextMenu(const QPoint& pos); void onSearchViewContextMenu(const QPoint& pos); void onBreakpointsViewContextMenu(const QPoint& pos); @@ -407,6 +411,14 @@ private slots: void on_actionEGE_Manual_triggered(); + void on_actionAdd_bookmark_triggered(); + + void on_actionRemove_Bookmark_triggered(); + + void on_tableBookmark_doubleClicked(const QModelIndex &index); + + void on_actionModify_Bookmark_Description_triggered(); + private: Ui::MainWindow *ui; EditorList *mEditorList; @@ -436,6 +448,7 @@ private: TodoModel mTodoModel; SearchResultModel mSearchResultModel; + PBookmarkModel mBookmarkModel; PSearchResultListModel mSearchResultListModel; PSearchResultTreeModel mSearchResultTreeModel; PSearchResultTreeViewDelegate mSearchViewDelegate; @@ -497,6 +510,10 @@ private: QAction * mDebugConsole_Copy; QAction * mDebugConsole_Paste; QAction * mDebugConsole_SelectAll; + //action for bookmarks + QAction * mBookmark_Remove; + QAction * mBookmark_RemoveAll; + QAction * mBookmark_Modify; // QWidget interface protected: diff --git a/RedPandaIDE/mainwindow.ui b/RedPandaIDE/mainwindow.ui index 4f3d3dfc..d207278e 100644 --- a/RedPandaIDE/mainwindow.ui +++ b/RedPandaIDE/mainwindow.ui @@ -295,7 +295,7 @@ QTabWidget::South - 4 + 5 @@ -859,6 +859,33 @@ + + + + :/icons/images/newlook24/039-gobook.png:/icons/images/newlook24/039-gobook.png + + + Bookmark + + + + + + true + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + true + + + + + @@ -956,6 +983,10 @@ + + + + @@ -1885,6 +1916,29 @@ EGE Manual + + + + :/icons/images/newlook24/003-addbook.png:/icons/images/newlook24/003-addbook.png + + + Add Bookmark + + + + + + :/icons/images/newlook24/064-remsrc.png:/icons/images/newlook24/064-remsrc.png + + + Remove Bookmark + + + + + Modify Bookmark Description + + diff --git a/RedPandaIDE/systemconsts.h b/RedPandaIDE/systemconsts.h index 134407f5..6e35b577 100644 --- a/RedPandaIDE/systemconsts.h +++ b/RedPandaIDE/systemconsts.h @@ -47,6 +47,7 @@ #define DEV_AUTOLINK_FILE "autolink.json" #define DEV_SHORTCUT_FILE "shortcuts.json" #define DEV_TOOLS_FILE "tools.json" +#define DEV_BOOKMARK_FILE "bookmarks.json" #ifdef Q_OS_WIN # define PATH_SENSITIVITY Qt::CaseInsensitive diff --git a/RedPandaIDE/widgets/bookmarkmodel.cpp b/RedPandaIDE/widgets/bookmarkmodel.cpp index 0c8462e8..5087c670 100644 --- a/RedPandaIDE/widgets/bookmarkmodel.cpp +++ b/RedPandaIDE/widgets/bookmarkmodel.cpp @@ -1,6 +1,238 @@ #include "bookmarkmodel.h" +#include "../systemconsts.h" -BookmarkModel::BookmarkModel(QObject* parent):QAbstractItemModel(parent) +#include +#include +#include +#include +#include +#include +#include "../utils.h" + +BookmarkModel::BookmarkModel(QObject* parent):QAbstractTableModel(parent) { } + +QSet BookmarkModel::bookmarksInFile(const QString &filename) +{ + QSet lines; + foreach (const PBookmark& bookmark, mBookmarks) { + if (bookmark->filename.compare(filename, PATH_SENSITIVITY) == 0) { + lines.insert(bookmark->line); + } + } + return lines; +} + +void BookmarkModel::addBookmark(const QString &filename, int line, const QString &description) +{ + Q_ASSERT(!isBookmarkExists(filename,line)); + PBookmark bookmark = std::make_shared(); + bookmark->filename = filename; + bookmark->line = line; + bookmark->description = description; + beginInsertRows(QModelIndex(),mBookmarks.count(),mBookmarks.count()); + mBookmarks.append(bookmark); + endInsertRows(); +} + +PBookmark BookmarkModel::bookmark(int i) +{ + return mBookmarks[i]; +} + +PBookmark BookmarkModel::bookmark(const QString &filename, int line) +{ + for (int i=0;ifilename.compare(filename, PATH_SENSITIVITY) == 0 + && bookmark->line == line) { + return bookmark; + } + } + return PBookmark(); +} + +bool BookmarkModel::removeBookmark(const QString &filename, int line) +{ + for (int i=0;ifilename.compare(filename, PATH_SENSITIVITY) == 0 + && bookmark->line == line) { + removeBookmarkAt(i); + return true; + } + } + return false; +} + +void BookmarkModel::clear() +{ + beginResetModel(); + mBookmarks.clear(); + endResetModel(); +} + +bool BookmarkModel::updateDescription(const QString &filename, int line, const QString &description) +{ + for (int i=0;ifilename.compare(filename, PATH_SENSITIVITY) == 0 + && bookmark->line == line) { + bookmark->description = description; + emit dataChanged(createIndex(i,0),createIndex(i,2)); + return true; + } + } + return false; +} + +void BookmarkModel::save(const QString &filename) +{ + QFile file(filename); + if (file.open(QFile::WriteOnly | QFile::Truncate)) { + QJsonArray array; + foreach (const PBookmark& bookmark, mBookmarks) { + QJsonObject obj; + obj["filename"]=bookmark->filename; + obj["line"]=bookmark->line; + obj["description"]=bookmark->description; + array.append(obj); + } + QJsonDocument doc; + doc.setArray(array); + if (file.write(doc.toJson())<0) { + throw FileError(tr("Save file '%1' failed.") + .arg(filename)); + } + } else { + throw FileError(tr("Can't open file '%1' for write.") + .arg(filename)); + } +} + +void BookmarkModel::load(const QString& filename) +{ + clear(); + QFile file(filename); + if (!file.exists()) + return; + if (file.open(QFile::ReadOnly)) { + QByteArray content = file.readAll(); + QJsonParseError error; + QJsonDocument doc(QJsonDocument::fromJson(content,&error)); + if (error.error != QJsonParseError::NoError) { + throw FileError(tr("Error in json file '%1':%2 : %3") + .arg(filename) + .arg(error.offset) + .arg(error.errorString())); + } + QJsonArray array = doc.array(); + for (int i=0;i=0;i--){ + PBookmark bookmark = mBookmarks[i]; + if (bookmark->filename == filename + && bookmark->line>=startLine) { + if (bookmark->line >= startLine+count) { + bookmark->line -= count; + emit dataChanged(createIndex(i,0),createIndex(i,2)); + } else { + removeBookmarkAt(i); + } + } + } +} + +void BookmarkModel::onFileInsertLines(const QString &filename, int startLine, int count) +{ + for (int i = mBookmarks.count()-1;i>=0;i--){ + PBookmark bookmark = mBookmarks[i]; + if (bookmark->filename == filename + && bookmark->line>=startLine) { + bookmark->line+=count; + emit dataChanged(createIndex(i,0),createIndex(i,2)); + } + } +} + +void BookmarkModel::removeBookmarkAt(int i) +{ + beginRemoveRows(QModelIndex(), i,i); + mBookmarks.removeAt(i); + endRemoveRows(); +} + +#ifdef QT_DEBUG +bool BookmarkModel::isBookmarkExists(const QString &filename, int line) +{ + foreach (const PBookmark& bookmark, mBookmarks) { + if (bookmark->filename.compare(filename, PATH_SENSITIVITY) == 0 + && bookmark->line == line) { + return true; + } + } + return false; +} + +int BookmarkModel::rowCount(const QModelIndex &) const +{ + return mBookmarks.count(); +} + +QVariant BookmarkModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + int row = index.row(); + PBookmark bookmark = mBookmarks[row]; + if (role == Qt::DisplayRole) { + switch(index.column()) { + case 0: + return bookmark->description; + case 1: + return bookmark->line; + case 2: + return bookmark->filename; + } + } + return QVariant(); +} + +int BookmarkModel::columnCount(const QModelIndex &) const +{ + return 3; +} + +QVariant BookmarkModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal) { + if (role == Qt::DisplayRole) { + switch(section) { + case 0: + return tr("Description"); + case 1: + return tr("Line"); + case 2: + return tr("Filename"); + } + } + } + return QVariant(); +} +#endif diff --git a/RedPandaIDE/widgets/bookmarkmodel.h b/RedPandaIDE/widgets/bookmarkmodel.h index 00831659..949a52d5 100644 --- a/RedPandaIDE/widgets/bookmarkmodel.h +++ b/RedPandaIDE/widgets/bookmarkmodel.h @@ -1,13 +1,54 @@ #ifndef BOOKMARKMODEL_H #define BOOKMARKMODEL_H -#include +#include +#include +#include -class BookmarkModel : public QAbstractItemModel +struct Bookmark { + QString filename; + int line; + QString description; +}; + +using PBookmark=std::shared_ptr; + +class BookmarkModel : public QAbstractTableModel { Q_OBJECT public: BookmarkModel(QObject* parent=nullptr); + QSet bookmarksInFile(const QString& filename); + void addBookmark(const QString&filename, int line, const QString& description); + PBookmark bookmark(int i); + PBookmark bookmark(const QString&filename, int line); + bool removeBookmark(const QString&filename, int line); + void clear(); + bool updateDescription(const QString&filename, int line, const QString& description); + void save(const QString& filename); + void load(const QString& filename); + void removeBookmarkAt(int i); +public slots: + void onFileDeleteLines(const QString& filename, int startLine, int count); + void onFileInsertLines(const QString& filename, int startLine, int count); +private: +#ifdef QT_DEBUG + bool isBookmarkExists(const QString&filename, int line); +#endif +private: + QList mBookmarks; + + // QAbstractItemModel interface +public: + int rowCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + int columnCount(const QModelIndex &parent) const override; + + // QAbstractItemModel interface +public: + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; }; +using PBookmarkModel = std::shared_ptr; + #endif // BOOKMARKMODEL_H