diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d483a9..477b55b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required (VERSION 2.8.8) project(juci) -set(JUCI_VERSION "1.2.1.6") +set(JUCI_VERSION "1.2.1.7") set(CPACK_PACKAGE_NAME "jucipp") set(CPACK_PACKAGE_CONTACT "Ole Christian Eidheim ") diff --git a/src/files.h b/src/files.h index f62b028..4b53c0e 100644 --- a/src/files.h +++ b/src/files.h @@ -105,6 +105,8 @@ R"RAW( "source_indentation_auto_indent_buffer": "i", "source_goto_line": "g", "source_center_cursor": "l", + "source_cursor_history_back": "Left", + "source_cursor_history_forward": "Right", "source_find_symbol_ctags": "f", "source_comments_toggle": "slash", "source_comments_add_documentation": "slash", diff --git a/src/menu.cc b/src/menu.cc index ac6a464..0d8e4fd 100644 --- a/src/menu.cc +++ b/src/menu.cc @@ -167,6 +167,17 @@ Menu::Menu() { _Center _Cursor app.source_center_cursor + + _Cursor _History + + _Back + app.source_cursor_history_back + + + _Forward + app.source_cursor_history_forward + +
diff --git a/src/notebook.cc b/src/notebook.cc index 9320d11..2dca6a6 100644 --- a/src/notebook.cc +++ b/src/notebook.cc @@ -303,9 +303,69 @@ void Notebook::open(const boost::filesystem::path &file_path, size_t notebook_in source_view->update_tab_label(source_view); }); - source_view->signal_focus_in_event().connect([this, source_view](GdkEventFocus *) { - set_current_view(source_view); - return false; + //Cursor history + auto update_cursor_locations=[this, source_view](const Gtk::TextBuffer::iterator &iter) { + bool mark_moved=false; + if(current_cursor_location!=static_cast(-1)) { + auto &cursor_location=cursor_locations.at(current_cursor_location); + if(cursor_location.view==source_view && abs(cursor_location.mark->get_iter().get_line()-iter.get_line())<=2) { + source_view->get_buffer()->move_mark(cursor_location.mark, iter); + mark_moved=true; + } + } + if(!mark_moved) { + if(current_cursor_location!=static_cast(-1)) { + for(auto it=cursor_locations.begin()+current_cursor_location+1;it!=cursor_locations.end();) { + it->view->get_buffer()->delete_mark(it->mark); + it=cursor_locations.erase(it); + } + } + cursor_locations.emplace_back(source_view, source_view->get_buffer()->create_mark(iter)); + current_cursor_location=cursor_locations.size()-1; + } + + // Combine adjacent cursor histories that are similar + if(!cursor_locations.empty()) { + size_t cursor_locations_index=1; + auto last_it=cursor_locations.begin(); + for(auto it=cursor_locations.begin()+1;it!=cursor_locations.end();) { + if(last_it->view==it->view && abs(last_it->mark->get_iter().get_line()-it->mark->get_iter().get_line())<=2) { + last_it->view->get_buffer()->delete_mark(last_it->mark); + last_it->mark=it->mark; + it=cursor_locations.erase(it); + if(current_cursor_location!=static_cast(-1) && current_cursor_location>cursor_locations_index) + --current_cursor_location; + } + else { + ++it; + ++last_it; + ++cursor_locations_index; + } + } + } + + // Remove start of cache if cache limit is exceeded + while(cursor_locations.size()>10) { + cursor_locations.begin()->view->get_buffer()->delete_mark(cursor_locations.begin()->mark); + cursor_locations.erase(cursor_locations.begin()); + if(current_cursor_location!=static_cast(-1)) + --current_cursor_location; + } + + if(current_cursor_location>=cursor_locations.size()) + current_cursor_location=cursor_locations.size()-1; + }; + source_view->get_buffer()->signal_mark_set().connect([this, source_view, update_cursor_locations](const Gtk::TextBuffer::iterator &iter, const Glib::RefPtr &mark) { + if(mark->get_name()=="insert") { + if(disable_next_update_cursor_locations) { + disable_next_update_cursor_locations=false; + return; + } + update_cursor_locations(iter); + } + }); + source_view->get_buffer()->signal_changed().connect([source_view, update_cursor_locations] { + update_cursor_locations(source_view->get_buffer()->get_insert()->get_iter()); }); #ifdef JUCI_ENABLE_DEBUG @@ -331,6 +391,11 @@ void Notebook::open(const boost::filesystem::path &file_path, size_t notebook_in } #endif + source_view->signal_focus_in_event().connect([this, source_view](GdkEventFocus *) { + set_current_view(source_view); + return false; + }); + if(notebook_index==static_cast(-1)) { if(!split) notebook_index=0; @@ -455,6 +520,21 @@ bool Notebook::close(size_t index) { if(on_close_page) on_close_page(view); + size_t cursor_locations_index=0; + for(auto it=cursor_locations.begin();it!=cursor_locations.end();) { + if(it->view==view) { + it=cursor_locations.erase(it); + if(current_cursor_location!=static_cast(-1) && current_cursor_location>cursor_locations_index) + --current_cursor_location; + } + else { + ++it; + ++cursor_locations_index; + } + } + if(current_cursor_location>=cursor_locations.size()) + current_cursor_location=cursor_locations.size()-1; + if(auto clang_view=dynamic_cast(view)) clang_view->async_delete(); else diff --git a/src/notebook.h b/src/notebook.h index f7b9e39..b2d3097 100644 --- a/src/notebook.h +++ b/src/notebook.h @@ -16,6 +16,13 @@ class Notebook : public Gtk::HPaned { Gtk::Label label; }; + class CursorLocation { + public: + CursorLocation(Source::View *view, Glib::RefPtr mark) : view(view), mark(mark) {} + Source::View *view; + Glib::RefPtr mark; + }; + private: Notebook(); public: @@ -24,7 +31,6 @@ public: return singleton; } - //Source::View* get_view(int page); size_t size(); Source::View* get_view(size_t index); Source::View* get_current_view(); @@ -52,6 +58,12 @@ public: std::function on_change_page; std::function on_close_page; + + /// Cursor history + std::vector cursor_locations; + size_t current_cursor_location=-1; + bool disable_next_update_cursor_locations=false; + private: size_t get_index(Source::View *view); Source::View *get_view(size_t notebook_index, int page); diff --git a/src/window.cc b/src/window.cc index 0a5147e..ca19a96 100644 --- a/src/window.cc +++ b/src/window.cc @@ -502,6 +502,38 @@ void Window::set_menu_actions() { if(auto view=Notebook::get().get_current_view()) view->scroll_to_cursor_delayed(view, true, false); }); + menu.add_action("source_cursor_history_back", [this]() { + if(Notebook::get().cursor_locations.size()<=1) + return; + if(Notebook::get().current_cursor_location==static_cast(-1)) + Notebook::get().current_cursor_location=Notebook::get().cursor_locations.size()-1; + if(Notebook::get().current_cursor_location<1) + return; + + --Notebook::get().current_cursor_location; + auto &cursor_location=Notebook::get().cursor_locations.at(Notebook::get().current_cursor_location); + if(Notebook::get().get_current_view()!=cursor_location.view) + Notebook::get().open(cursor_location.view->file_path); + Notebook::get().disable_next_update_cursor_locations=true; + cursor_location.view->get_buffer()->place_cursor(cursor_location.mark->get_iter()); + cursor_location.view->scroll_to_cursor_delayed(cursor_location.view, true, false); + }); + menu.add_action("source_cursor_history_forward", [this]() { + if(Notebook::get().cursor_locations.size()<=1) + return; + if(Notebook::get().current_cursor_location==static_cast(-1)) + Notebook::get().current_cursor_location=Notebook::get().cursor_locations.size()-1; + if(Notebook::get().current_cursor_location==Notebook::get().cursor_locations.size()-1) + return; + + ++Notebook::get().current_cursor_location; + auto &cursor_location=Notebook::get().cursor_locations.at(Notebook::get().current_cursor_location); + if(Notebook::get().get_current_view()!=cursor_location.view) + Notebook::get().open(cursor_location.view->file_path); + Notebook::get().disable_next_update_cursor_locations=true; + cursor_location.view->get_buffer()->place_cursor(cursor_location.mark->get_iter()); + cursor_location.view->scroll_to_cursor_delayed(cursor_location.view, true, false); + }); menu.add_action("source_find_symbol_ctags", [this]() { if(auto view=Notebook::get().get_current_view()) {