From 57ffd22aa36cea970eab68ee79a93f63ac5fde24 Mon Sep 17 00:00:00 2001 From: eidheim Date: Mon, 20 Jul 2015 20:03:50 +0200 Subject: [PATCH] Divided SelectionDialog into SelectionDialog and CompleteDialog. SelectionDialog will be used to choose class method to go to. --- juci/config.json | 1 + juci/menu.xml | 1 + juci/notebook.cc | 13 +- juci/selectiondialog.cc | 283 +++++++++++++++++++++++++++------------- juci/selectiondialog.h | 48 ++++--- juci/source.cc | 60 +++++---- juci/source.h | 4 +- 7 files changed, 274 insertions(+), 136 deletions(-) diff --git a/juci/config.json b/juci/config.json index 9c3e0a0..efc9003 100644 --- a/juci/config.json +++ b/juci/config.json @@ -59,6 +59,7 @@ "edit_redo": "y", "edit_find": "f", "goto_declaration": "d", + "goto_method": "m", "compile_and_run": "r", "compile": "r" }, diff --git a/juci/menu.xml b/juci/menu.xml index 5f79da8..d9937d8 100644 --- a/juci/menu.xml +++ b/juci/menu.xml @@ -20,6 +20,7 @@ + diff --git a/juci/notebook.cc b/juci/notebook.cc index 04eb689..da6202d 100644 --- a/juci/notebook.cc +++ b/juci/notebook.cc @@ -82,15 +82,18 @@ void Notebook::Controller::CreateKeybindings() { open_file(location.first); CurrentSourceView()->get_buffer()->place_cursor(CurrentSourceView()->get_buffer()->get_iter_at_offset(location.second)); CurrentSourceView()->scroll_to(CurrentSourceView()->get_buffer()->get_insert(), 0.0, 1.0, 0.5); - - /*CurrentSourceView()->signal_size_allocate().connect([this](Gtk::Allocation& allocation){ - auto adj=scrolled_windows.at(CurrentPage())->get_vadjustment(); - cout << adj->get_upper() << ", " << adj->get_page_size() << endl; - });*/ } } } }); + + menu->action_group->add(Gtk::Action::create("SourceGotoMethod", "Go to method"), Gtk::AccelKey(menu->key_map["goto_method"]), [this]() { + if(CurrentPage()!=-1) { + if(CurrentSourceView()->goto_method) { + CurrentSourceView()->goto_method(); + } + } + }); entry.button_apply_set_filename.signal_clicked().connect([this]() { std::string filename=entry(); diff --git a/juci/selectiondialog.cc b/juci/selectiondialog.cc index 18e9f1a..49b15f4 100644 --- a/juci/selectiondialog.cc +++ b/juci/selectiondialog.cc @@ -1,14 +1,23 @@ #include "selectiondialog.h" -SelectionDialog::SelectionDialog(Gtk::TextView& text_view): text_view(text_view) {} +namespace sigc { + SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE +} -void SelectionDialog::show() { +SelectionDialogBase::SelectionDialogBase(Gtk::TextView& text_view, bool popup): text_view(text_view), popup(popup) {} + +void SelectionDialogBase::show() { if(rows.size()==0) return; - window=std::unique_ptr(new Gtk::Window(Gtk::WindowType::WINDOW_POPUP)); + if(popup) + window=std::unique_ptr(new Gtk::Window(Gtk::WindowType::WINDOW_POPUP)); + else + window=std::unique_ptr(new Gtk::Dialog()); scrolled_window=std::unique_ptr(new Gtk::ScrolledWindow()); list_view_text=std::unique_ptr(new Gtk::ListViewText(1, false, Gtk::SelectionMode::SELECTION_BROWSE)); + search_entry=std::unique_ptr(new Gtk::Entry()); + list_view_text->set_search_entry(*search_entry); window->set_default_size(0, 0); window->property_decorated()=false; @@ -24,43 +33,38 @@ void SelectionDialog::show() { last_selected=-1; - list_view_text->signal_row_activated().connect([this](const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn*) { - if(shown) { - select(); - } - }); list_view_text->signal_cursor_changed().connect(sigc::mem_fun(*this, &SelectionDialog::cursor_changed), true); list_view_text->signal_realize().connect([this](){ resize(); }); - show_offset=text_view.get_buffer()->get_insert()->get_iter().get_offset(); list_view_text->clear_items(); for (auto &i : rows) { list_view_text->append(i.first); } scrolled_window->add(*list_view_text); - window->add(*scrolled_window); - + if(popup) + window->add(*scrolled_window); + else { + auto dialog=(Gtk::Dialog*)window.get(); + dialog->get_vbox()->pack_start(*search_entry, false, false); + dialog->get_vbox()->pack_start(*scrolled_window, true, true); + dialog->set_transient_for((Gtk::Window&)(*text_view.get_toplevel())); + } if(rows.size()>0) { list_view_text->get_selection()->select(*list_view_text->get_model()->children().begin()); list_view_text->scroll_to_row(list_view_text->get_selection()->get_selected_rows()[0]); } - + move(); - window->show_all(); + search_entry->show(); shown=true; row_in_entry=false; - auto text=text_view.get_buffer()->get_text(start_mark->get_iter(), text_view.get_buffer()->get_insert()->get_iter()); - if(text.size()>0) { - search_entry.set_text(text); - list_view_text->set_search_entry(search_entry); - } } -void SelectionDialog::hide() { +void SelectionDialogBase::hide() { shown=false; window->hide(); if(tooltips) @@ -69,7 +73,167 @@ void SelectionDialog::hide() { on_hide(); } -void SelectionDialog::select(bool hide_window) { +void SelectionDialogBase::cursor_changed() { + auto selected=list_view_text->get_selected(); + if(selected.size()>0) { + if(selected[0]!=last_selected || last_selected==-1) { + if(tooltips) + tooltips->hide(); + auto row = rows.at(list_view_text->get_text(selected[0])); + if(row.second.size()>0) { + tooltips=std::unique_ptr(new Tooltips()); + auto tooltip_text=row.second; + auto get_tooltip_buffer=[this, tooltip_text]() { + auto tooltip_buffer=Gtk::TextBuffer::create(text_view.get_buffer()->get_tag_table()); + //TODO: Insert newlines to tooltip_text (use 80 chars, then newline?) + tooltip_buffer->insert_at_cursor(tooltip_text); + return tooltip_buffer; + }; + tooltips->emplace_back(get_tooltip_buffer, text_view, text_view.get_buffer()->create_mark(start_mark->get_iter()), text_view.get_buffer()->create_mark(text_view.get_buffer()->get_insert()->get_iter())); + tooltips->show(true); + } + } + } + else if(tooltips) + tooltips->hide(); + if(selected.size()>0) + last_selected=selected[0]; + else + last_selected=-1; +} + +void SelectionDialogBase::move() { + INFO("SelectionDialog set position"); + Gdk::Rectangle rectangle; + text_view.get_iter_location(start_mark->get_iter(), rectangle); + int buffer_x=rectangle.get_x(); + int buffer_y=rectangle.get_y()+rectangle.get_height(); + int window_x, window_y; + text_view.buffer_to_window_coords(Gtk::TextWindowType::TEXT_WINDOW_TEXT, buffer_x, buffer_y, window_x, window_y); + int root_x, root_y; + text_view.get_window(Gtk::TextWindowType::TEXT_WINDOW_TEXT)->get_root_coords(window_x, window_y, root_x, root_y); + window->move(root_x, root_y+1); //TODO: replace 1 with some margin +} + +void SelectionDialogBase::resize() { + INFO("SelectionDialog set size"); + + if(list_view_text->get_realized()) { + int row_width=0, row_height; + Gdk::Rectangle rect; + list_view_text->get_cell_area(list_view_text->get_model()->get_path(list_view_text->get_model()->children().begin()), *(list_view_text->get_column(0)), rect); + row_width=rect.get_width(); + row_height=rect.get_height(); + + row_width+=rect.get_x()*2; //TODO: Add correct margin x and y + row_height+=rect.get_y()*2; + + if(row_width>text_view.get_width()/2) + row_width=text_view.get_width()/2; + else + scrolled_window->set_policy(Gtk::PolicyType::POLICY_NEVER, Gtk::PolicyType::POLICY_AUTOMATIC); + + int window_height=std::min(row_height*(int)rows.size(), row_height*10); + if(!popup) + window_height+=search_entry->get_height(); + window->resize(row_width, window_height); + } +} + +SelectionDialog::SelectionDialog(Gtk::TextView& text_view) : SelectionDialogBase(text_view, false) {} + +void SelectionDialog::show() { + SelectionDialogBase::show(); + std::shared_ptr search_key(new std::string()); + auto filter_model=Gtk::TreeModelFilter::create(list_view_text->get_model()); + filter_model->set_visible_func([this, search_key](const Gtk::TreeModel::const_iterator& iter){ + std::string row; + iter->get_value(0, row); + if(row.find(*search_key)!=std::string::npos) + return true; + return false; + }); + list_view_text->set_model(filter_model); + list_view_text->set_search_equal_func([this](const Glib::RefPtr& model, int column, const Glib::ustring& key, const Gtk::TreeModel::iterator& iter) { + return false; + }); + search_entry->signal_changed().connect([this, search_key, filter_model](){ + *search_key=search_entry->get_text(); + filter_model->refilter(); + list_view_text->set_search_entry(*search_entry); //TODO:Report the need of this to GTK's git (bug) + }); + search_entry->signal_event().connect([this, search_key, filter_model](GdkEvent* event) { + if(event->type==GDK_KEY_PRESS) { + auto key=(GdkEventKey*)event; + if(key->keyval==GDK_KEY_Down) { + auto it=list_view_text->get_selection()->get_selected(); + if(it) { + it++; + if(it) { + list_view_text->set_cursor(list_view_text->get_model()->get_path(it)); + } + } + else + list_view_text->set_cursor(list_view_text->get_model()->get_path(list_view_text->get_model()->children().begin())); + return true; + } + if(key->keyval==GDK_KEY_Up) { + auto it=list_view_text->get_selection()->get_selected(); + if(it) { + it--; + if(it) { + list_view_text->set_cursor(list_view_text->get_model()->get_path(it)); + } + } + return true; + } + } + return false; + }); + auto activate=[this](){ + if(on_select && list_view_text->get_selected().size()>0) { + auto it=list_view_text->get_selection()->get_selected(); + std::string row; + it->get_value(0, row); + std::string selected = rows.at(row).first; + on_select(selected); + } + window->hide(); + }; + search_entry->signal_activate().connect([this, activate](){ + activate(); + }); + list_view_text->signal_row_activated().connect([this, activate](const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn*) { + activate(); + }); + window->signal_focus_out_event().connect([this](GdkEventFocus*){ + window->hide(); + return true; + }); + list_view_text->set_cursor(list_view_text->get_model()->get_path(list_view_text->get_model()->children().begin())); +} + +CompleteDialog::CompleteDialog(Gtk::TextView& text_view) : SelectionDialogBase(text_view, true) {} + +void CompleteDialog::show() { + SelectionDialogBase::show(); + + show_offset=text_view.get_buffer()->get_insert()->get_iter().get_offset(); + + list_view_text->signal_row_activated().connect([this](const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn*) { + if(shown) { + select(); + } + }); + + auto text=text_view.get_buffer()->get_text(start_mark->get_iter(), text_view.get_buffer()->get_insert()->get_iter()); + if(text.size()>0) { + search_entry->set_text(text); + list_view_text->set_search_entry(*search_entry); + } +} + +void CompleteDialog::select(bool hide_window) { row_in_entry=true; auto selected=list_view_text->get_selected(); std::pair select; @@ -97,7 +261,7 @@ void SelectionDialog::select(bool hide_window) { } } -bool SelectionDialog::on_key_release(GdkEventKey* key) { +bool CompleteDialog::on_key_release(GdkEventKey* key) { if(key->keyval==GDK_KEY_Down || key->keyval==GDK_KEY_Up) return false; @@ -107,15 +271,15 @@ bool SelectionDialog::on_key_release(GdkEventKey* key) { else { auto text=text_view.get_buffer()->get_text(start_mark->get_iter(), text_view.get_buffer()->get_insert()->get_iter()); if(text.size()>0) { - search_entry.set_text(text); - list_view_text->set_search_entry(search_entry); + search_entry->set_text(text); + list_view_text->set_search_entry(*search_entry); } cursor_changed(); } return false; } -bool SelectionDialog::on_key_press(GdkEventKey* key) { +bool CompleteDialog::on_key_press(GdkEventKey* key) { if((key->keyval>=GDK_KEY_0 && key->keyval<=GDK_KEY_9) || (key->keyval>=GDK_KEY_A && key->keyval<=GDK_KEY_Z) || (key->keyval>=GDK_KEY_a && key->keyval<=GDK_KEY_z) || @@ -136,8 +300,7 @@ bool SelectionDialog::on_key_press(GdkEventKey* key) { if(it) { it++; if(it) { - list_view_text->get_selection()->select(it); - list_view_text->scroll_to_row(list_view_text->get_selection()->get_selected_rows()[0]); + list_view_text->set_cursor(list_view_text->get_model()->get_path(it)); } } select(false); @@ -149,8 +312,7 @@ bool SelectionDialog::on_key_press(GdkEventKey* key) { if(it) { it--; if(it) { - list_view_text->get_selection()->select(it); - list_view_text->scroll_to_row(list_view_text->get_selection()->get_selected_rows()[0]); + list_view_text->set_cursor(list_view_text->get_model()->get_path(it)); } } select(false); @@ -164,68 +326,3 @@ bool SelectionDialog::on_key_press(GdkEventKey* key) { hide(); return false; } - -void SelectionDialog::cursor_changed() { - auto selected=list_view_text->get_selected(); - if(selected.size()>0) { - if(selected[0]!=last_selected || last_selected==-1) { - if(tooltips) - tooltips->hide(); - auto row = rows.at(list_view_text->get_text(selected[0])); - if(row.second.size()>0) { - tooltips=std::unique_ptr(new Tooltips()); - auto tooltip_text=row.second; - auto get_tooltip_buffer=[this, tooltip_text]() { - auto tooltip_buffer=Gtk::TextBuffer::create(text_view.get_buffer()->get_tag_table()); - //TODO: Insert newlines to tooltip_text (use 80 chars, then newline?) - tooltip_buffer->insert_at_cursor(tooltip_text); - return tooltip_buffer; - }; - tooltips->emplace_back(get_tooltip_buffer, text_view, text_view.get_buffer()->create_mark(start_mark->get_iter()), text_view.get_buffer()->create_mark(text_view.get_buffer()->get_insert()->get_iter())); - tooltips->show(true); - } - } - } - else if(tooltips) - tooltips->hide(); - if(selected.size()>0) - last_selected=selected[0]; - else - last_selected=-1; -} - -void SelectionDialog::move() { - INFO("SelectionDialog set position"); - Gdk::Rectangle rectangle; - text_view.get_iter_location(start_mark->get_iter(), rectangle); - int buffer_x=rectangle.get_x(); - int buffer_y=rectangle.get_y()+rectangle.get_height(); - int window_x, window_y; - text_view.buffer_to_window_coords(Gtk::TextWindowType::TEXT_WINDOW_TEXT, buffer_x, buffer_y, window_x, window_y); - int root_x, root_y; - text_view.get_window(Gtk::TextWindowType::TEXT_WINDOW_TEXT)->get_root_coords(window_x, window_y, root_x, root_y); - window->move(root_x, root_y+1); //TODO: replace 1 with some margin -} - -void SelectionDialog::resize() { - INFO("SelectionDialog set size"); - - if(list_view_text->get_realized()) { - int row_width=0, row_height; - Gdk::Rectangle rect; - list_view_text->get_cell_area(list_view_text->get_model()->get_path(list_view_text->get_model()->children().begin()), *(list_view_text->get_column(0)), rect); - row_width=rect.get_width(); - row_height=rect.get_height(); - - row_width+=rect.get_x()*2; //TODO: Add correct margin x and y - row_height+=rect.get_y()*2; - - if(row_width>text_view.get_width()/2) - row_width=text_view.get_width()/2; - else - scrolled_window->set_policy(Gtk::PolicyType::POLICY_NEVER, Gtk::PolicyType::POLICY_AUTOMATIC); - - int window_height=std::min(row_height*(int)rows.size(), row_height*10); - window->resize(row_width, window_height); - } -} diff --git a/juci/selectiondialog.h b/juci/selectiondialog.h index b497d6c..0f1c793 100644 --- a/juci/selectiondialog.h +++ b/juci/selectiondialog.h @@ -5,35 +5,53 @@ #include "logging.h" #include "tooltips.h" -class SelectionDialog { +class SelectionDialogBase { public: - SelectionDialog(Gtk::TextView& text_view); - void show(); - void hide(); - bool close(GdkEventFocus*); - void move(); - bool on_key_release(GdkEventKey* key); - bool on_key_press(GdkEventKey* key); - std::function on_hide; + SelectionDialogBase(Gtk::TextView& text_view, bool popup); + virtual void show(); + virtual void hide(); + virtual void move(); std::map > rows; + std::function on_hide; bool shown=false; Glib::RefPtr start_mark; -private: - void resize(); - void select(bool hide_window=true); - void cursor_changed(); +protected: + virtual void resize(); + virtual void cursor_changed(); - Gtk::Entry search_entry; - int show_offset; bool row_in_entry; Gtk::TextView& text_view; std::unique_ptr window; std::unique_ptr scrolled_window; std::unique_ptr list_view_text; + std::unique_ptr search_entry; std::unique_ptr tooltips; int last_selected; +private: + bool popup; +}; + +class SelectionDialog : public SelectionDialogBase { +public: + SelectionDialog(Gtk::TextView& text_view); + void show(); + std::function on_select; +}; + +class CompleteDialog : public SelectionDialogBase { +public: + CompleteDialog(Gtk::TextView& text_view); + void show(); + bool on_key_release(GdkEventKey* key); + bool on_key_press(GdkEventKey* key); + +private: + void select(bool hide_window=true); + + int show_offset; + bool row_in_entry; }; #endif // JUCI_SELECTIONDIALOG_H_ \ No newline at end of file diff --git a/juci/source.cc b/juci/source.cc index 187ffc6..8f1bdc9 100644 --- a/juci/source.cc +++ b/juci/source.cc @@ -547,32 +547,32 @@ bool Source::ClangViewParse::on_key_press_event(GdkEventKey* key) { //// ClangViewAutocomplete /// ////////////////////////////// Source::ClangViewAutocomplete::ClangViewAutocomplete(const std::string& file_path, const std::string& project_path): -Source::ClangViewParse(file_path, project_path), selection_dialog(*this), autocomplete_cancel_starting(false) { - selection_dialog.on_hide=[this](){ +Source::ClangViewParse(file_path, project_path), complete_dialog(*this), autocomplete_cancel_starting(false) { + complete_dialog.on_hide=[this](){ start_reparse(); }; get_buffer()->signal_changed().connect([this](){ - if(selection_dialog.shown) + if(complete_dialog.shown) delayed_reparse_connection.disconnect(); start_autocomplete(); }); get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator& iterator, const Glib::RefPtr& mark){ if(mark->get_name()=="insert") { autocomplete_cancel_starting=true; - if(selection_dialog.shown) { - selection_dialog.hide(); + if(complete_dialog.shown) { + complete_dialog.hide(); } } }); signal_scroll_event().connect([this](GdkEventScroll* event){ - if(selection_dialog.shown) - selection_dialog.hide(); + if(complete_dialog.shown) + complete_dialog.hide(); return false; }, false); signal_key_release_event().connect([this](GdkEventKey* key){ - if(selection_dialog.shown) { - if(selection_dialog.on_key_release(key)) + if(complete_dialog.shown) { + if(complete_dialog.on_key_release(key)) return true; } @@ -582,16 +582,16 @@ Source::ClangViewParse(file_path, project_path), selection_dialog(*this), autoco bool Source::ClangViewAutocomplete::on_key_press_event(GdkEventKey *key) { last_keyval=key->keyval; - if(selection_dialog.shown) { - if(selection_dialog.on_key_press(key)) + if(complete_dialog.shown) { + if(complete_dialog.on_key_press(key)) return true; } return ClangViewParse::on_key_press_event(key); } bool Source::ClangViewAutocomplete::on_focus_out_event(GdkEventFocus* event) { - if(selection_dialog.shown) { - selection_dialog.hide(); + if(complete_dialog.shown) { + complete_dialog.hide(); } return Source::ClangViewParse::on_focus_out_event(event); @@ -613,7 +613,7 @@ void Source::ClangViewAutocomplete::start_autocomplete() { prefix_mutex.lock(); prefix=sm[3].str(); prefix_mutex.unlock(); - if((prefix.size()==0 || prefix[0]<'0' || prefix[0]>'9') && !autocomplete_starting && !selection_dialog.shown) { + if((prefix.size()==0 || prefix[0]<'0' || prefix[0]>'9') && !autocomplete_starting && !complete_dialog.shown) { autocomplete(); } else if(last_keyval=='.' && autocomplete_starting) @@ -623,13 +623,13 @@ void Source::ClangViewAutocomplete::start_autocomplete() { prefix_mutex.lock(); prefix=sm[3].str(); prefix_mutex.unlock(); - if((prefix.size()==0 || prefix[0]<'0' || prefix[0]>'9') && !autocomplete_starting && !selection_dialog.shown) { + if((prefix.size()==0 || prefix[0]<'0' || prefix[0]>'9') && !autocomplete_starting && !complete_dialog.shown) { autocomplete(); } } else autocomplete_cancel_starting=true; - if(autocomplete_starting || selection_dialog.shown) + if(autocomplete_starting || complete_dialog.shown) delayed_reparse_connection.disconnect(); } } @@ -644,12 +644,12 @@ void Source::ClangViewAutocomplete::autocomplete() { autocomplete_done_connection=autocomplete_done.connect([this, ac_data](){ autocomplete_starting=false; if(!autocomplete_cancel_starting) { - if(selection_dialog.start_mark) - get_buffer()->delete_mark(selection_dialog.start_mark); + if(complete_dialog.start_mark) + get_buffer()->delete_mark(complete_dialog.start_mark); auto start_iter=get_buffer()->get_insert()->get_iter(); for(size_t c=0;ccreate_mark(start_iter); + complete_dialog.start_mark=get_buffer()->create_mark(start_iter); std::map > rows; for (auto &data : *ac_data) { @@ -673,8 +673,8 @@ void Source::ClangViewAutocomplete::autocomplete() { if (rows.empty()) { rows["No suggestions found..."] = std::pair(); } - selection_dialog.rows=std::move(rows); - selection_dialog.show(); + complete_dialog.rows=std::move(rows); + complete_dialog.show(); } else start_autocomplete(); @@ -740,7 +740,7 @@ get_autocomplete_suggestions(int line_number, int column, std::mapcreate_tag(); similar_tokens_tag->property_weight()=Pango::WEIGHT_BOLD; @@ -803,6 +803,22 @@ Source::ClangViewAutocomplete(file_path, project_path) { } return location; }; + + goto_method=[this](){ + if(selection_dialog.start_mark) + get_buffer()->delete_mark(selection_dialog.start_mark); + selection_dialog.start_mark=get_buffer()->create_mark(get_buffer()->get_insert()->get_iter()); + + std::map > rows; + rows["Not implemented yet"]=std::pair("1", ""); + rows["but you can try the selection search"]=std::pair("2", ""); + rows["search for instance for 'try'"]=std::pair("3", ""); + selection_dialog.rows=std::move(rows); + selection_dialog.on_select=[this](std::string selected) { + cout << selected << endl; + }; + selection_dialog.show(); + }; } //////////////// diff --git a/juci/source.h b/juci/source.h index 53828cd..1a3683d 100644 --- a/juci/source.h +++ b/juci/source.h @@ -54,6 +54,7 @@ public: 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); @@ -119,7 +120,7 @@ public: private: void start_autocomplete(); void autocomplete(); - SelectionDialog selection_dialog; + CompleteDialog complete_dialog; std::vector get_autocomplete_suggestions(int line_number, int column, std::map& buffer_map); Glib::Dispatcher autocomplete_done; sigc::connection autocomplete_done_connection; @@ -136,6 +137,7 @@ public: private: Glib::RefPtr similar_tokens_tag; std::string last_similar_tokens_tagged; + SelectionDialog selection_dialog; }; class ClangView : public ClangViewRefactor {