diff --git a/juci/config.json b/juci/config.json index efc9003..893b7b1 100644 --- a/juci/config.json +++ b/juci/config.json @@ -2,7 +2,6 @@ "source": { "colors": { "text_color": "black", - "search": "orange", "string": "#CC0000", "namespace_ref": "#990099", "type": "#0066FF", diff --git a/juci/entrybox.cc b/juci/entrybox.cc index 59bd9d5..ad714a0 100644 --- a/juci/entrybox.cc +++ b/juci/entrybox.cc @@ -29,11 +29,15 @@ void EntryBox::clear() { } void EntryBox::show() { + std::vector focus_chain; for(auto& entry: entries) { pack_start(entry, Gtk::PACK_SHRINK); + focus_chain.emplace_back(&entry); } for(auto& button: buttons) pack_start(button, Gtk::PACK_SHRINK); + + set_focus_chain(focus_chain); show_all(); if(entries.size()>0) { entries.begin()->grab_focus(); diff --git a/juci/notebook.cc b/juci/notebook.cc index 08e2359..08c8f3b 100644 --- a/juci/notebook.cc +++ b/juci/notebook.cc @@ -2,7 +2,12 @@ #include "notebook.h" #include "logging.h" #include "singletons.h" -#include // c-library +#include //TODO: remove +using namespace std; //TODO: remove + +namespace sigc { + SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE +} Notebook::View::View() { pack2(notebook); @@ -21,6 +26,10 @@ Notebook::Controller::Controller() : CurrentSourceView()->grab_focus(); } }); + view.notebook.signal_switch_page().connect([this](Gtk::Widget* page, guint page_num) { + if(search_entry_shown && CurrentPage()!=-1) + CurrentSourceView()->search_highlight(last_search); + }); INFO("Notebook Controller Success"); } // Constructor @@ -45,27 +54,63 @@ void Notebook::Controller::CreateKeybindings() { //TODO: Also update cursor position menu->action_group->add(Gtk::Action::create("EditFind", "Find"), Gtk::AccelKey(menu->key_map["edit_find"]), [this]() { entry_box.clear(); - entry_box.entries.emplace_back("", [this](const std::string& content){ - search(content, true); + entry_box.entries.emplace_back(last_search, [this](const std::string& content){ + if(CurrentPage()!=-1) + CurrentSourceView()->search_forward(); }); - auto entry_it=entry_box.entries.begin(); - entry_box.buttons.emplace_back("Next", [this, entry_it](){ - search(entry_it->get_text(), true); + auto search_entry_it=entry_box.entries.begin(); + search_entry_it->set_placeholder_text("Find"); + if(CurrentPage()!=-1) + CurrentSourceView()->search_highlight(search_entry_it->get_text()); + search_entry_it->signal_key_press_event().connect([this, search_entry_it](GdkEventKey* event){ + if(event->keyval==GDK_KEY_Return && event->state==GDK_SHIFT_MASK) { + if(CurrentPage()!=-1) + CurrentSourceView()->search_backward(); + } + return false; }); - entry_box.buttons.emplace_back("Previous", [this, entry_it](){ - search(entry_it->get_text(), false); + search_entry_it->signal_changed().connect([this, search_entry_it](){ + last_search=search_entry_it->get_text(); + if(CurrentPage()!=-1) + CurrentSourceView()->search_highlight(search_entry_it->get_text()); }); - entry_box.buttons.emplace_back("Cancel", [this](){ - entry_box.hide(); + + entry_box.entries.emplace_back(last_replace, [this](const std::string &content){ + if(CurrentPage()!=-1) + CurrentSourceView()->replace_forward(content); }); - entry_box.signal_hide().connect([this]() { - auto buffer=CurrentSourceView()->get_buffer(); - buffer->remove_tag_by_name("search", buffer->begin(), buffer->end()); - if(search_context!=NULL) { - gtk_source_search_context_set_highlight(search_context, false); + auto replace_entry_it=entry_box.entries.begin(); + replace_entry_it++; + replace_entry_it->set_placeholder_text("Replace"); + replace_entry_it->signal_key_press_event().connect([this, replace_entry_it](GdkEventKey* event){ + if(event->keyval==GDK_KEY_Return && event->state==GDK_SHIFT_MASK) { + if(CurrentPage()!=-1) + CurrentSourceView()->replace_backward(replace_entry_it->get_text()); } + return false; + }); + replace_entry_it->signal_changed().connect([this, replace_entry_it](){ + last_replace=replace_entry_it->get_text(); + }); + + entry_box.buttons.emplace_back("Find", [this](){ + if(CurrentPage()!=-1) + CurrentSourceView()->search_forward(); + }); + entry_box.buttons.emplace_back("Replace", [this, replace_entry_it](){ + if(CurrentPage()!=-1) + CurrentSourceView()->replace_forward(replace_entry_it->get_text()); }); - search_context=NULL; //TODO: delete content if any? Neither delete nor free worked... Do this on hide + entry_box.buttons.emplace_back("Replace all", [this, replace_entry_it](){ + if(CurrentPage()!=-1) + CurrentSourceView()->replace_all(replace_entry_it->get_text()); + }); + entry_box.signal_hide().connect([this]() { + for(int c=0;cview->search_highlight(""); + search_entry_shown=false; + }); + search_entry_shown=true; entry_box.show(); }); menu->action_group->add(Gtk::Action::create("EditCopy", "Copy"), Gtk::AccelKey(menu->key_map["edit_copy"]), [this]() { @@ -203,48 +248,9 @@ void Notebook::Controller::OnFileNewFile() { entry_box.buttons.emplace_back("Create file", [this, entry_it](){ entry_it->activate(); }); - entry_box.buttons.emplace_back("Cancel", [this](){ - entry_box.hide(); - }); entry_box.show(); } -//TODO: see search TODO earlier -void Notebook::Controller::search(const std::string& text, bool forward) { - INFO("Notebook search"); - if(search_context!=NULL) - gtk_source_search_context_set_highlight(search_context, false); - auto start = CurrentSourceView()->search_start; - auto end = CurrentSourceView()->search_end; - // fetch buffer and greate settings - auto buffer = CurrentSourceView()->get_source_buffer(); - auto settings = gtk_source_search_settings_new(); - gtk_source_search_settings_set_search_text(settings, text.c_str()); - // make sure the search continues - gtk_source_search_settings_set_wrap_around(settings, true); - search_context = gtk_source_search_context_new(buffer->gobj(), settings); - gtk_source_search_context_set_highlight(search_context, true); - auto itr = buffer->get_insert()->get_iter(); - buffer->remove_tag_by_name("search", start ? start : itr, end ? end : itr); - if (forward) { - DEBUG("Doing forward search"); - gtk_source_search_context_forward(search_context, - end ? end.gobj() : itr.gobj(), - start.gobj(), - end.gobj()); - } else { - DEBUG("Doing backward search"); - gtk_source_search_context_backward(search_context, - start ? start.gobj() : itr.gobj(), - start.gobj(), - end.gobj()); - } - buffer->apply_tag_by_name("search", start, end); - CurrentSourceView()->scroll_to(end); - CurrentSourceView()->search_start = start; - CurrentSourceView()->search_end = end; -} - void Notebook::Controller ::OnDirectoryNavigation(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* column) { diff --git a/juci/notebook.h b/juci/notebook.h index 89f34de..7c6b9a8 100644 --- a/juci/notebook.h +++ b/juci/notebook.h @@ -31,13 +31,14 @@ namespace Notebook { Gtk::TreeViewColumn* column); void open_file(std::string filename); int Pages(); - void search(const std::string& text, bool forward); - GtkSourceSearchContext* search_context; View view; std::string OnSaveFileAs(); std::string project_path; Directories::Controller directories; //Todo: make private after creating open_directory() EntryBox entry_box; + std::string last_search; + std::string last_replace; + bool search_entry_shown=false; std::vector > source_views; private: void CreateKeybindings(); diff --git a/juci/source.cc b/juci/source.cc index a970acd..239dc97 100644 --- a/juci/source.cc +++ b/juci/source.cc @@ -7,6 +7,7 @@ #include #include #include "singletons.h" +#include #include //TODO: remove using namespace std; //TODO: remove @@ -37,7 +38,6 @@ file_path(file_path), project_path(project_path) { get_source_buffer()->get_undo_manager()->begin_not_undoable_action(); get_source_buffer()->set_text(s.get_content()); get_source_buffer()->get_undo_manager()->end_not_undoable_action(); - search_start = search_end = this->get_buffer()->end(); override_font(Pango::FontDescription(Singleton::Config::source()->font)); @@ -58,6 +58,82 @@ file_path(file_path), project_path(project_path) { after_user_input=true; return false; }); + + search_settings = gtk_source_search_settings_new(); + gtk_source_search_settings_set_wrap_around(search_settings, true); + search_context = gtk_source_search_context_new(get_source_buffer()->gobj(), search_settings); + //TODO: why does this not work?: Might be best to use the styles from sourceview. These has to be read from file, search-matches got style "search-match" + //TODO: in header if trying again: GtkSourceStyle* search_match_style; + //search_match_style=(GtkSourceStyle*)g_object_new(GTK_SOURCE_TYPE_STYLE, "background-set", 1, "background", "#00FF00", NULL); + //gtk_source_search_context_set_match_style(search_context, search_match_style); +} + +Source::View::~View() { + g_clear_object(&search_context); + g_clear_object(&search_settings); +} + +void Source::View::search_highlight(const std::string &text) { + if(text.size()>0) { + gtk_source_search_settings_set_search_text(search_settings, text.c_str()); + gtk_source_search_context_set_highlight(search_context, true); + } + else + gtk_source_search_context_set_highlight(search_context, false); +} + +void Source::View::search_forward() { + Gtk::TextIter insert, selection_bound; + get_buffer()->get_selection_bounds(insert, selection_bound); + auto& start=selection_bound; + Gtk::TextIter match_start, match_end; + if(gtk_source_search_context_forward(search_context, start.gobj(), match_start.gobj(), match_end.gobj())) { + get_buffer()->select_range(match_start, match_end); + scroll_to(get_buffer()->get_insert()); + } +} + +void Source::View::search_backward() { + Gtk::TextIter insert, selection_bound; + get_buffer()->get_selection_bounds(insert, selection_bound); + auto &start=insert; + Gtk::TextIter match_start, match_end; + if(gtk_source_search_context_backward(search_context, start.gobj(), match_start.gobj(), match_end.gobj())) { + get_buffer()->select_range(match_start, match_end); + scroll_to(get_buffer()->get_insert()); + } +} + +void Source::View::replace_forward(const std::string &replacement) { + Gtk::TextIter insert, selection_bound; + get_buffer()->get_selection_bounds(insert, selection_bound); + auto &start=insert; + Gtk::TextIter match_start, match_end; + if(gtk_source_search_context_forward(search_context, start.gobj(), match_start.gobj(), match_end.gobj())) { + auto offset=match_start.get_offset(); + gtk_source_search_context_replace(search_context, match_start.gobj(), match_end.gobj(), replacement.c_str(), replacement.size(), NULL); + + get_buffer()->select_range(get_buffer()->get_iter_at_offset(offset), get_buffer()->get_iter_at_offset(offset+replacement.size())); + scroll_to(get_buffer()->get_insert()); + } +} + +void Source::View::replace_backward(const std::string &replacement) { + Gtk::TextIter insert, selection_bound; + get_buffer()->get_selection_bounds(insert, selection_bound); + auto &start=selection_bound; + Gtk::TextIter match_start, match_end; + if(gtk_source_search_context_backward(search_context, start.gobj(), match_start.gobj(), match_end.gobj())) { + auto offset=match_start.get_offset(); + gtk_source_search_context_replace(search_context, match_start.gobj(), match_end.gobj(), replacement.c_str(), replacement.size(), NULL); + + get_buffer()->select_range(get_buffer()->get_iter_at_offset(offset), get_buffer()->get_iter_at_offset(offset+replacement.size())); + scroll_to(get_buffer()->get_insert()); + } +} + +void Source::View::replace_all(const std::string &replacement) { + gtk_source_search_context_replace_all(search_context, replacement.c_str(), replacement.size(), NULL); } string Source::View::get_line(size_t line_number) { @@ -163,7 +239,28 @@ clang::Index Source::ClangViewParse::clang_index(0, 0); Source::ClangViewParse::ClangViewParse(const std::string& file_path, const std::string& project_path): Source::View(file_path, project_path), -parse_thread_go(true), parse_thread_mapped(false), parse_thread_stop(false) { +parse_thread_go(true), parse_thread_mapped(false), parse_thread_stop(false) { + //Create underline color tags for diagnostic warnings and errors: + auto diagnostic_tag=get_buffer()->get_tag_table()->lookup("diagnostic_warning"); + auto diagnostic_tag_underline=Gtk::TextTag::create("diagnostic_warning_underline"); + get_buffer()->get_tag_table()->add(diagnostic_tag_underline); + diagnostic_tag_underline->property_underline()=Pango::Underline::UNDERLINE_ERROR; + auto tag_class=G_OBJECT_GET_CLASS(diagnostic_tag_underline->gobj()); //For older GTK+ 3 versions: + auto param_spec=g_object_class_find_property(tag_class, "underline-rgba"); + if(param_spec!=NULL) { + diagnostic_tag_underline->set_property("underline-rgba", diagnostic_tag->property_foreground_rgba().get_value()); + } + diagnostic_tag=get_buffer()->get_tag_table()->lookup("diagnostic_error"); + diagnostic_tag_underline=Gtk::TextTag::create("diagnostic_error_underline"); + get_buffer()->get_tag_table()->add(diagnostic_tag_underline); + diagnostic_tag_underline->property_underline()=Pango::Underline::UNDERLINE_ERROR; + tag_class=G_OBJECT_GET_CLASS(diagnostic_tag_underline->gobj()); //For older GTK+ 3 versions: + param_spec=g_object_class_find_property(tag_class, "underline-rgba"); + if(param_spec!=NULL) { + diagnostic_tag_underline->set_property("underline-rgba", diagnostic_tag->property_foreground_rgba().get_value()); + } + //TODO: clear tag_class and param_spec? + int start_offset = get_source_buffer()->begin().get_offset(); int end_offset = get_source_buffer()->end().get_offset(); auto buffer_map=get_buffer_map(); @@ -323,11 +420,13 @@ void Source::ClangViewParse::update_syntax() { return; } auto buffer = get_source_buffer(); - buffer->remove_all_tags(buffer->begin(), buffer->end()); + for(auto &tag: last_syntax_tags) + buffer->remove_tag_by_name(tag, buffer->begin(), buffer->end()); + last_syntax_tags.clear(); for (auto &range : ranges) { std::string type = std::to_string(range.kind); try { - Singleton::Config::source()->types.at(type); + last_syntax_tags.emplace(Singleton::Config::source()->types.at(type)); } catch (std::exception) { continue; } @@ -341,6 +440,8 @@ void Source::ClangViewParse::update_syntax() { void Source::ClangViewParse::update_diagnostics() { diagnostic_tooltips.clear(); + get_buffer()->remove_tag_by_name("diagnostic_warning_underline", get_buffer()->begin(), get_buffer()->end()); + get_buffer()->remove_tag_by_name("diagnostic_error_underline", get_buffer()->begin(), get_buffer()->end()); auto diagnostics=clang_tu->get_diagnostics(); for(auto &diagnostic: diagnostics) { if(diagnostic.path==file_path) { @@ -362,17 +463,8 @@ void Source::ClangViewParse::update_diagnostics() { return tooltip_buffer; }; diagnostic_tooltips.emplace_back(get_tooltip_buffer, *this, get_buffer()->create_mark(start), get_buffer()->create_mark(end)); - - auto tag=get_buffer()->create_tag(); - tag->property_underline()=Pango::Underline::UNDERLINE_ERROR; - auto tag_class=G_OBJECT_GET_CLASS(tag->gobj()); //For older GTK+ 3 versions: - auto param_spec=g_object_class_find_property(tag_class, "underline-rgba"); - if(param_spec!=NULL) { - auto diagnostic_tag=get_buffer()->get_tag_table()->lookup(diagnostic_tag_name); - if(diagnostic_tag!=0) - tag->set_property("underline-rgba", diagnostic_tag->property_foreground_rgba().get_value()); - } - get_buffer()->apply_tag(tag, start, end); + + get_buffer()->apply_tag_by_name(diagnostic_tag_name+"_underline", start, end); } } } diff --git a/juci/source.h b/juci/source.h index f96d245..cfc955d 100644 --- a/juci/source.h +++ b/juci/source.h @@ -13,6 +13,7 @@ #include "terminal.h" #include "tooltips.h" #include "selectiondialog.h" +#include class Source { public: @@ -47,17 +48,29 @@ public: class View : public Gsv::View { public: View(const std::string& file_path, const std::string& project_path); + ~View(); + + void search_highlight(const std::string &text); + void search_forward(); + void search_backward(); + void replace_forward(const std::string &replacement); + void replace_backward(const std::string &replacement); + void replace_all(const std::string &replacement); + std::string get_line(size_t line_number); std::string get_line_before_insert(); + std::string file_path; std::string project_path; - Gtk::TextIter search_start, search_end; std::function()> get_declaration_location; std::function goto_method; bool after_user_input=false; protected: bool on_key_press_event(GdkEventKey* key); + private: + GtkSourceSearchContext *search_context; + GtkSourceSearchSettings *search_settings; }; // class View class GenericView : public View { @@ -89,6 +102,7 @@ public: int end_offset); int reparse(const std::map &buffers); void update_syntax(); + std::set last_syntax_tags; void update_diagnostics(); void update_types(); Tooltips diagnostic_tooltips; diff --git a/juci/window.cc b/juci/window.cc index 45e51eb..473cf2e 100644 --- a/juci/window.cc +++ b/juci/window.cc @@ -85,6 +85,14 @@ Window::Window() : Singleton::notebook()->entry_box.hide(); return false; }); + Singleton::notebook()->entry_box.signal_show().connect([this](){ + std::vector focus_chain; + focus_chain.emplace_back(&Singleton::notebook()->entry_box); + window_box_.set_focus_chain(focus_chain); + }); + Singleton::notebook()->entry_box.signal_hide().connect([this](){ + window_box_.unset_focus_chain(); + }); INFO("Window created"); } // Window constructor