From 2ed5b6f12bf7d068f2f94051f07eec1122984e95 Mon Sep 17 00:00:00 2001 From: eidheim Date: Sun, 30 Aug 2015 08:51:39 +0200 Subject: [PATCH] Replaced spellcheck tooltips with selectiondialog where one can select a correct word. --- src/selectiondialog.cc | 42 +++++++++++++- src/selectiondialog.h | 3 +- src/source.cc | 122 ++++++++++++++++++++++++++--------------- src/source.h | 10 +++- 4 files changed, 128 insertions(+), 49 deletions(-) diff --git a/src/selectiondialog.cc b/src/selectiondialog.cc index 8b73072..395672d 100644 --- a/src/selectiondialog.cc +++ b/src/selectiondialog.cc @@ -148,7 +148,7 @@ void SelectionDialogBase::resize() { } } -SelectionDialog::SelectionDialog(Gtk::TextView& text_view, Glib::RefPtr start_mark) : SelectionDialogBase(text_view, start_mark, true) {} +SelectionDialog::SelectionDialog(Gtk::TextView& text_view, Glib::RefPtr start_mark, bool show_search_entry) : SelectionDialogBase(text_view, start_mark, show_search_entry) {} void SelectionDialog::show() { SelectionDialogBase::show(); @@ -239,6 +239,46 @@ void SelectionDialog::show() { } } +bool SelectionDialog::on_key_press(GdkEventKey* key) { + if(key->keyval==GDK_KEY_Down && list_view_text.get_model()->children().size()>0) { + 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 && list_view_text.get_model()->children().size()>0) { + 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 { + auto last_it=list_view_text.get_model()->children().end(); + last_it--; + list_view_text.set_cursor(list_view_text.get_model()->get_path(last_it)); + } + return true; + } + if(key->keyval==GDK_KEY_Return || key->keyval==GDK_KEY_ISO_Left_Tab || key->keyval==GDK_KEY_Tab) { + auto it=list_view_text.get_selection()->get_selected(); + auto column=list_view_text.get_column(0); + list_view_text.row_activated(list_view_text.get_model()->get_path(it), *column); + return true; + } + hide(); + if(key->keyval==GDK_KEY_Escape) + return true; + return false; +} + CompletionDialog::CompletionDialog(Gtk::TextView& text_view, Glib::RefPtr start_mark) : SelectionDialogBase(text_view, start_mark, false) {} void CompletionDialog::show() { diff --git a/src/selectiondialog.h b/src/selectiondialog.h index e454a3c..499c86f 100644 --- a/src/selectiondialog.h +++ b/src/selectiondialog.h @@ -37,7 +37,8 @@ private: class SelectionDialog : public SelectionDialogBase { public: - SelectionDialog(Gtk::TextView& text_view, Glib::RefPtr start_mark); + SelectionDialog(Gtk::TextView& text_view, Glib::RefPtr start_mark, bool show_search_entry=true); + bool on_key_press(GdkEventKey* key); void show(); }; diff --git a/src/source.cc b/src/source.cc index c4f43d4..e89914f 100644 --- a/src/source.cc +++ b/src/source.cc @@ -173,6 +173,8 @@ Source::View::View(const boost::filesystem::path &file_path): file_path(file_pat tag->property_underline()=Pango::Underline::UNDERLINE_ERROR; get_buffer()->signal_changed().connect([this](){ + delayed_spellcheck_suggestions_connection.disconnect(); + auto iter=get_buffer()->get_insert()->get_iter(); if(iter.backward_char()) { auto context_iter=iter; @@ -183,16 +185,60 @@ Source::View::View(const boost::filesystem::path &file_path): file_path(file_pat auto second=iter; if(first.backward_char() && second.forward_char()) { get_buffer()->remove_tag_by_name("spellcheck_error", first, second); - spellcheck(first); - spellcheck(second); + auto word=spellcheck_get_word(first); + spellcheck_word(word.first, word.second); + word=spellcheck_get_word(second); + spellcheck_word(word.first, word.second); } } - else - spellcheck(iter); + else { + auto word=spellcheck_get_word(iter); + spellcheck_word(word.first, word.second); + } } } } }); + + get_buffer()->signal_mark_set().connect([this](const Gtk::TextBuffer::iterator& iter, const Glib::RefPtr& mark) { + if(mark->get_name()=="insert") { + if(spellcheck_suggestions_dialog_shown) + spellcheck_suggestions_dialog->hide(); + delayed_spellcheck_suggestions_connection.disconnect(); + delayed_spellcheck_suggestions_connection=Glib::signal_timeout().connect([this]() { + auto tags=get_buffer()->get_insert()->get_iter().get_tags(); + bool need_suggestions=false; + for(auto &tag: tags) { + if(tag->property_name()=="spellcheck_error") { + need_suggestions=true; + break; + } + } + if(need_suggestions) { + spellcheck_suggestions_dialog=std::unique_ptr(new SelectionDialog(*this, get_buffer()->create_mark(get_buffer()->get_insert()->get_iter()), false)); + spellcheck_suggestions_dialog->on_hide=[this](){ + spellcheck_suggestions_dialog_shown=false; + }; + auto word=spellcheck_get_word(get_buffer()->get_insert()->get_iter()); + auto suggestions=spellcheck_get_suggestions(word.first, word.second); + if(suggestions.size()==0) + return false; + for(auto &suggestion: suggestions) + spellcheck_suggestions_dialog->add_row(suggestion); + spellcheck_suggestions_dialog->on_select=[this, word](const std::string& selected, bool hide_window) { + get_source_buffer()->begin_user_action(); + get_buffer()->erase(word.first, word.second); + get_buffer()->insert(get_buffer()->get_insert()->get_iter(), selected); + get_source_buffer()->end_user_action(); + delayed_tooltips_connection.disconnect(); + }; + spellcheck_suggestions_dialog->show(); + spellcheck_suggestions_dialog_shown=true; + } + return false; + }, 500); + } + }); } set_tooltip_events(); @@ -212,13 +258,11 @@ void Source::View::set_tooltip_events() { type_tooltips.show(rectangle); diagnostic_tooltips.show(rectangle); } - spellcheck_tooltips.show(rectangle); return false; }, 100); } type_tooltips.hide(); diagnostic_tooltips.hide(); - spellcheck_tooltips.hide(); } on_motion_last_x=event->x; on_motion_last_y=event->y; @@ -244,12 +288,10 @@ void Source::View::set_tooltip_events() { type_tooltips.show(rectangle); diagnostic_tooltips.show(rectangle); } - spellcheck_tooltips.show(rectangle); return false; }, 500); type_tooltips.hide(); diagnostic_tooltips.hide(); - spellcheck_tooltips.hide(); } }); @@ -257,7 +299,6 @@ void Source::View::set_tooltip_events() { delayed_tooltips_connection.disconnect(); type_tooltips.hide(); diagnostic_tooltips.hide(); - spellcheck_tooltips.hide(); return false; }); @@ -265,7 +306,6 @@ void Source::View::set_tooltip_events() { delayed_tooltips_connection.disconnect(); type_tooltips.hide(); diagnostic_tooltips.hide(); - spellcheck_tooltips.hide(); return false; }); } @@ -453,6 +493,11 @@ string Source::View::get_line_before_insert() { //Basic indentation bool Source::View::on_key_press_event(GdkEventKey* key) { + if(spellcheck_suggestions_dialog_shown) { + if(spellcheck_suggestions_dialog->on_key_press(key)) + return true; + } + get_source_buffer()->begin_user_action(); //Indent as in next or previous line if(key->keyval==GDK_KEY_Return && key->state==0 && !get_buffer()->get_has_selection()) { @@ -637,7 +682,7 @@ std::pair Source::View::find_tab_char_and_size() { return {found_tab_char, found_tab_size}; } -void Source::View::spellcheck(Gtk::TextIter iter) { +std::pair Source::View::spellcheck_get_word(Gtk::TextIter iter) { auto start=iter; auto end=iter; @@ -650,46 +695,37 @@ void Source::View::spellcheck(Gtk::TextIter iter) { if(!end.forward_char()) break; } - for(auto it=spellcheck_tooltips.begin();it!=spellcheck_tooltips.end();it++) { - if(it->start_mark->get_iter()==start || it->start_mark->get_iter()==it->end_mark->get_iter()) - it=spellcheck_tooltips.erase(it); - } + + return {start, end}; +} + +void Source::View::spellcheck_word(const Gtk::TextIter& start, const Gtk::TextIter& end) { auto word=get_buffer()->get_text(start, end); - std::vector words; if(word.size()>0) { auto correct = aspell_speller_check(spellcheck_checker, word.data(), word.bytes()); - if(correct==0) { + if(correct==0) get_buffer()->apply_tag_by_name("spellcheck_error", start, end); - - const AspellWordList *suggestions = aspell_speller_suggest(spellcheck_checker, word.data(), word.bytes()); - AspellStringEnumeration *elements = aspell_word_list_elements(suggestions); - - auto words=std::make_shared(); - const char *word; - while ((word = aspell_string_enumeration_next(elements))!= NULL) { - if(words->size()==0) { - *words="Suggestions:\n"; - (*words)+=word; - } - else - (*words)+=std::string(", ")+word; - } - delete_aspell_string_enumeration(elements); - - if(words->size()>0) { - auto create_tooltip_buffer=[this, words]() { - auto tooltip_buffer=Gtk::TextBuffer::create(get_buffer()->get_tag_table()); - tooltip_buffer->insert_with_tag(tooltip_buffer->get_insert()->get_iter(), *words, "def:note"); - return tooltip_buffer; - }; - spellcheck_tooltips.emplace_back(create_tooltip_buffer, *this, get_buffer()->create_mark(start), get_buffer()->create_mark(end)); - } - } else get_buffer()->remove_tag_by_name("spellcheck_error", start, end); } } +std::vector Source::View::spellcheck_get_suggestions(const Gtk::TextIter& start, const Gtk::TextIter& end) { + auto word_with_error=get_buffer()->get_text(start, end); + + const AspellWordList *suggestions = aspell_speller_suggest(spellcheck_checker, word_with_error.data(), word_with_error.bytes()); + AspellStringEnumeration *elements = aspell_word_list_elements(suggestions); + + std::vector words; + const char *word; + while ((word = aspell_string_enumeration_next(elements))!= NULL) { + words.emplace_back(word); + } + delete_aspell_string_enumeration(elements); + + return words; +} + ///////////////////// //// GenericView //// @@ -775,7 +811,6 @@ Source::View(file_path), project_path(project_path) { start_reparse(); type_tooltips.hide(); diagnostic_tooltips.hide(); - spellcheck_tooltips.hide(); }); bracket_regex=std::regex(std::string("^(")+tab_char+"*).*\\{ *$"); @@ -786,7 +821,6 @@ Source::View(file_path), project_path(project_path) { void Source::ClangViewParse::init_parse() { type_tooltips.hide(); diagnostic_tooltips.hide(); - spellcheck_tooltips.hide(); get_buffer()->remove_all_tags(get_buffer()->begin(), get_buffer()->end()); source_readable=false; parse_thread_go=true; diff --git a/src/source.h b/src/source.h index 95fa854..32bdb0d 100644 --- a/src/source.h +++ b/src/source.h @@ -80,7 +80,6 @@ namespace Source { bool source_readable; Tooltips diagnostic_tooltips; Tooltips type_tooltips; - Tooltips spellcheck_tooltips; gdouble on_motion_last_x; gdouble on_motion_last_y; sigc::connection delayed_tooltips_connection; @@ -109,8 +108,13 @@ namespace Source { static AspellConfig* spellcheck_config; AspellCanHaveError *spellcheck_possible_err; AspellSpeller *spellcheck_checker; - void spellcheck(Gtk::TextIter iter); - }; // class View + std::pair spellcheck_get_word(Gtk::TextIter iter); + void spellcheck_word(const Gtk::TextIter& start, const Gtk::TextIter& end); + std::vector spellcheck_get_suggestions(const Gtk::TextIter& start, const Gtk::TextIter& end); + std::unique_ptr spellcheck_suggestions_dialog; + bool spellcheck_suggestions_dialog_shown=false; + sigc::connection delayed_spellcheck_suggestions_connection; + }; class GenericView : public View { public: