summaryrefslogtreecommitdiffstats
path: root/src/ui/dialog/spellcheck.cpp
diff options
context:
space:
mode:
authorJohn Smith <john.smith7545@yahoo.com>2012-02-21 13:26:58 +0000
committerJohn Smith <removethis.john.q.public@bigmail.com>2012-02-21 13:26:58 +0000
commit5a97256cdaa9cc74e50b05aaf6a491f2df6fda0f (patch)
tree243844f1ed4140f61458fbd435f0194ed37b6d42 /src/ui/dialog/spellcheck.cpp
parentCopy missing libintl-8.dll in build.xml (diff)
downloadinkscape-5a97256cdaa9cc74e50b05aaf6a491f2df6fda0f.tar.gz
inkscape-5a97256cdaa9cc74e50b05aaf6a491f2df6fda0f.zip
Fix for 934721 : Convert SpellCheck dialog to a dockable dialog
(bzr r11006)
Diffstat (limited to 'src/ui/dialog/spellcheck.cpp')
-rw-r--r--src/ui/dialog/spellcheck.cpp871
1 files changed, 871 insertions, 0 deletions
diff --git a/src/ui/dialog/spellcheck.cpp b/src/ui/dialog/spellcheck.cpp
new file mode 100644
index 000000000..1f46d9a0d
--- /dev/null
+++ b/src/ui/dialog/spellcheck.cpp
@@ -0,0 +1,871 @@
+/**
+ * @file
+ * Spellcheck dialog.
+ */
+/* Authors:
+ * bulia byak <bulia@users.sf.net>
+ * Jon A. Cruz <jon@joncruz.org>
+ * Abhishek Sharma
+ *
+ * Copyright (C) 2009 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "widgets/icon.h"
+#include "message-stack.h"
+
+#include <gtk/gtk.h>
+
+#include "helper/window.h"
+#include "macros.h"
+#include "inkscape.h"
+#include "document.h"
+#include "selection.h"
+#include "desktop-handles.h"
+#include "tools-switch.h"
+#include "text-context.h"
+#include "interface.h"
+#include "preferences.h"
+#include "sp-text.h"
+#include "sp-flowtext.h"
+#include "text-editing.h"
+#include "sp-tspan.h"
+#include "sp-tref.h"
+#include "sp-defs.h"
+#include "selection-chemistry.h"
+#include <xml/repr.h>
+#include "display/canvas-bpath.h"
+#include "display/curve.h"
+#include "document-undo.h"
+#include "sp-root.h"
+#include "verbs.h"
+#include "spellcheck.h"
+
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+SpellCheck::SpellCheck (void) :
+ UI::Widget::Panel ("", "/dialogs/spellcheck/", SP_VERB_DIALOG_SPELLCHECK),
+ _speller(NULL),
+ _speller2(NULL),
+ _speller3(NULL),
+ _rects(NULL),
+ _seen_objects(NULL),
+ _text(NULL),
+ _layout(NULL),
+ _stops(0),
+ _adds(0),
+ _working(false),
+ _local_change(false),
+ _prefs(NULL),
+ _lang("en"),
+ _lang2(""),
+ _lang3(""),
+ accept_button(_("_Accept"), true),
+ ignoreonce_button(_("_Ignore once"), true),
+ ignore_button(_("_Ignore"), true),
+ add_button(_("A_dd"), true),
+ dictionary_hbox(false, 0),
+ stop_button(_("_Stop"), true),
+ start_button(_("_Start"), true),
+ desktop(NULL),
+ deskTrack()
+{
+ _prefs = Inkscape::Preferences::get();
+
+ // take languages from prefs
+ _lang = _prefs->getString(_prefs_path + "lang");
+ _lang2 = _prefs->getString(_prefs_path + "lang2");
+ _lang3 = _prefs->getString(_prefs_path + "lang3");
+ if (_lang == "")
+ _lang = "en";
+
+ banner_hbox.set_layout(Gtk::BUTTONBOX_START);
+ banner_hbox.add(banner_label);
+
+ scrolled_window.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
+ scrolled_window.set_shadow_type(Gtk::SHADOW_IN);
+ scrolled_window.set_size_request(120, 96);
+ scrolled_window.add(tree_view);
+
+ model = Gtk::ListStore::create(tree_columns);
+ tree_view.set_model(model);
+ tree_view.append_column(_("Suggestions:"), tree_columns.suggestions);
+
+ {
+// Backward compatibility fix: The GtkComboBoxText API was introduced with
+// GTK+ 2.24. This check should eventually be dropped when we bump our
+// GTK dependency.
+#if GTK_CHECK_VERSION(2, 24, 0)
+ dictionary_combo = gtk_combo_box_text_new();
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (dictionary_combo), _lang.c_str());
+ if (_lang2 != "") {
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (dictionary_combo), _lang2.c_str());
+ }
+ if (_lang3 != "") {
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (dictionary_combo), _lang3.c_str());
+ }
+#else
+ dictionary_combo = gtk_combo_box_new_text();
+ gtk_combo_box_append_text (GTK_COMBO_BOX (dictionary_combo), _lang.c_str());
+ if (_lang2 != "") {
+ gtk_combo_box_append_text (GTK_COMBO_BOX (dictionary_combo), _lang2.c_str());
+ }
+ if (_lang3 != "") {
+ gtk_combo_box_append_text (GTK_COMBO_BOX (dictionary_combo), _lang3.c_str());
+ }
+#endif
+ gtk_combo_box_set_active (GTK_COMBO_BOX (dictionary_combo), 0);
+ gtk_widget_show_all (dictionary_combo);
+ }
+
+ accept_button.set_tooltip_text(_("Accept the chosen suggestion"));
+ ignoreonce_button.set_tooltip_text(_("Ignore this word only once"));
+ ignore_button.set_tooltip_text(_("Ignore this word in this session"));
+ add_button.set_tooltip_text(_("Add this word to the chosen dictionary"));
+
+ dictionary_hbox.pack_start(add_button, true, true, 0);
+ dictionary_hbox.pack_start(*Gtk::manage(Glib::wrap(dictionary_combo)), false, false, 0);
+
+ changebutton_vbox.set_spacing(4);
+ changebutton_vbox.pack_start(accept_button, false, false, 0);
+ changebutton_vbox.pack_start(ignoreonce_button, false, false, 0);
+ changebutton_vbox.pack_start(ignore_button, false, false, 0);
+ changebutton_vbox.pack_start(dictionary_hbox, false, false, 0);
+
+ suggestion_hbox.pack_start (scrolled_window, true, true, 4);
+ suggestion_hbox.pack_end (changebutton_vbox, false, false, 0);
+
+ stop_button.set_tooltip_text(_("Stop the check"));
+ start_button.set_tooltip_text(_("Start the check"));
+
+ actionbutton_hbox.set_layout(Gtk::BUTTONBOX_END);
+ actionbutton_hbox.set_spacing(4);
+ actionbutton_hbox.add(stop_button);
+ actionbutton_hbox.add(start_button);
+
+ /**
+ * Main dialog
+ */
+ Gtk::Box *contents = _getContents();
+ contents->set_spacing(6);
+ contents->pack_start (banner_hbox, false, false, 0);
+ contents->pack_start (suggestion_hbox, true, true, 0);
+ contents->pack_start (action_sep, false, false, 6);
+ contents->pack_start (actionbutton_hbox, false, false, 0);
+
+ /**
+ * Signal handlers
+ */
+ accept_button.signal_clicked().connect(sigc::mem_fun(*this, &SpellCheck::onAccept));
+ ignoreonce_button.signal_clicked().connect(sigc::mem_fun(*this, &SpellCheck::onIgnoreOnce));
+ ignore_button.signal_clicked().connect(sigc::mem_fun(*this, &SpellCheck::onIgnore));
+ add_button.signal_clicked().connect(sigc::mem_fun(*this, &SpellCheck::onAdd));
+ start_button.signal_clicked().connect(sigc::mem_fun(*this, &SpellCheck::onStart));
+ stop_button.signal_clicked().connect(sigc::mem_fun(*this, &SpellCheck::onStop));
+ tree_view.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &SpellCheck::onTreeSelectionChange));
+ desktopChangeConn = deskTrack.connectDesktopChanged( sigc::mem_fun(*this, &SpellCheck::setTargetDesktop) );
+ deskTrack.connect(GTK_WIDGET(gobj()));
+
+ show_all_children ();
+
+ // run it at once
+ onStart ();
+}
+
+SpellCheck::~SpellCheck(void)
+{
+ clearRects();
+ disconnect();
+
+ desktopChangeConn.disconnect();
+ deskTrack.disconnect();
+}
+
+void SpellCheck::setDesktop(SPDesktop *desktop)
+{
+ Panel::setDesktop(desktop);
+ deskTrack.setBase(desktop);
+}
+
+void SpellCheck::setTargetDesktop(SPDesktop *desktop)
+{
+ if (this->desktop != desktop) {
+ this->desktop = desktop;
+ if (_working) {
+ // Stop and start on the new desktop
+ finished();
+ onStart();
+ }
+ }
+}
+
+void SpellCheck::clearRects()
+{
+ for (GSList *it = _rects; it; it = it->next) {
+ sp_canvas_item_hide((SPCanvasItem*) it->data);
+ gtk_object_destroy((SPCanvasItem*) it->data);
+ }
+ g_slist_free(_rects);
+ _rects = NULL;
+}
+
+void SpellCheck::disconnect()
+{
+ if (_release_connection) {
+ _release_connection.disconnect();
+ }
+ if (_modified_connection) {
+ _modified_connection.disconnect();
+ }
+}
+
+GSList *SpellCheck::allTextItems (SPObject *r, GSList *l, bool hidden, bool locked)
+{
+ if (!desktop)
+ return l; // no desktop to check
+
+ if (SP_IS_DEFS(r))
+ return l; // we're not interested in items in defs
+
+ if (!strcmp(r->getRepr()->name(), "svg:metadata")) {
+ return l; // we're not interested in metadata
+ }
+
+ for (SPObject *child = r->firstChild(); child; child = child->next) {
+ if (SP_IS_ITEM (child) && !child->cloned && !desktop->isLayer(SP_ITEM(child))) {
+ if ((hidden || !desktop->itemIsHidden(SP_ITEM(child))) && (locked || !SP_ITEM(child)->isLocked())) {
+ if (SP_IS_TEXT(child) || SP_IS_FLOWTEXT(child))
+ l = g_slist_prepend (l, child);
+ }
+ }
+ l = allTextItems (child, l, hidden, locked);
+ }
+ return l;
+}
+
+bool
+SpellCheck::textIsValid (SPObject *root, SPItem *text)
+{
+ GSList *l = NULL;
+ l = allTextItems (root, l, false, true);
+ for (GSList *i = l; i; i = i->next) {
+ SPItem *item = static_cast<SPItem *>(i->data);
+ if (item == text) {
+ g_slist_free (l);
+ return true;
+ }
+ }
+ g_slist_free (l);
+ return false;
+}
+
+gint SpellCheck::compareTextBboxes (gconstpointer a, gconstpointer b)
+{
+ SPItem *i1 = SP_ITEM(a);
+ SPItem *i2 = SP_ITEM(b);
+
+ Geom::OptRect bbox1 = i1->desktopVisualBounds();
+ Geom::OptRect bbox2 = i2->desktopVisualBounds();
+ if (!bbox1 || !bbox2) {
+ return 0;
+ }
+
+ // vector between top left corners
+ Geom::Point diff = Geom::Point(bbox2->min()[Geom::X], bbox2->max()[Geom::Y]) -
+ Geom::Point(bbox1->min()[Geom::X], bbox1->max()[Geom::Y]);
+
+ // sort top to bottom, left to right, but:
+ // if i2 is higher only 0.2 or less times it is righter than i1, put i1 first
+ if (diff[Geom::Y] > 0.2 * diff[Geom::X])
+ return 1;
+ else
+ return -1;
+
+ return 0;
+}
+
+// We regenerate and resort the list every time, because user could have changed it while the
+// dialog was waiting
+SPItem *SpellCheck::getText (SPObject *root)
+{
+ GSList *l = NULL;
+ l = allTextItems (root, l, false, true);
+ l = g_slist_sort(l, (GCompareFunc)SpellCheck::compareTextBboxes);
+
+ for (GSList *i = l; i; i = i->next) {
+ SPItem *item = static_cast<SPItem *>(i->data);
+ if (!g_slist_find (_seen_objects, item)) {
+ _seen_objects = g_slist_prepend(_seen_objects, item);
+ g_slist_free(l);
+ return item;
+ }
+ }
+
+ g_slist_free(l);
+ return NULL;
+}
+
+void
+SpellCheck::nextText()
+{
+ disconnect();
+
+ _text = getText(_root);
+ if (_text) {
+
+ _modified_connection = ((SPObject*) _text)->connectModified(sigc::mem_fun(*this, &SpellCheck::onObjModified));
+ _release_connection = ((SPObject*) _text)->connectRelease(sigc::mem_fun(*this, &SpellCheck::onObjReleased));
+
+ _layout = te_get_layout (_text);
+ _begin_w = _layout->begin();
+ }
+ _end_w = _begin_w;
+ _word.clear();
+}
+
+bool
+SpellCheck::init(SPDesktop *d)
+{
+ desktop = d;
+
+ tree_view.set_sensitive(false);
+ accept_button.set_sensitive(false);
+ ignore_button.set_sensitive(false);
+ ignoreonce_button.set_sensitive(false);
+ add_button.set_sensitive(false);
+ gtk_widget_set_sensitive(dictionary_combo, false);
+ start_button.set_sensitive(false);
+
+#ifdef WIN32
+ // on windows, dictionaries are in a lib/aspell-0.60 subdir off inkscape's executable dir;
+ // this is some black magick to find out the executable path to give it to aspell
+ char exeName[MAX_PATH+1];
+ GetModuleFileName(NULL, exeName, MAX_PATH);
+ char *slashPos = strrchr(exeName, '\\');
+ if (slashPos)
+ *slashPos = '\0';
+ g_print ("%s\n", exeName);
+#endif
+
+ _stops = 0;
+ _adds = 0;
+ clearRects();
+
+ {
+ AspellConfig *config = new_aspell_config();
+#ifdef WIN32
+ aspell_config_replace(config, "prefix", exeName);
+#endif
+ aspell_config_replace(config, "lang", _lang.c_str());
+ aspell_config_replace(config, "encoding", "UTF-8");
+ AspellCanHaveError *ret = new_aspell_speller(config);
+ delete_aspell_config(config);
+ if (aspell_error(ret) != 0) {
+ g_warning("Error: %s\n", aspell_error_message(ret));
+ delete_aspell_can_have_error(ret);
+ return false;
+ }
+ _speller = to_aspell_speller(ret);
+ }
+
+ if (_lang2 != "") {
+ AspellConfig *config = new_aspell_config();
+#ifdef WIN32
+ aspell_config_replace(config, "prefix", exeName);
+#endif
+ aspell_config_replace(config, "lang", _lang2.c_str());
+ aspell_config_replace(config, "encoding", "UTF-8");
+ AspellCanHaveError *ret = new_aspell_speller(config);
+ delete_aspell_config(config);
+ if (aspell_error(ret) != 0) {
+ g_warning("Error: %s\n", aspell_error_message(ret));
+ delete_aspell_can_have_error(ret);
+ return false;
+ }
+ _speller2 = to_aspell_speller(ret);
+ }
+
+ if (_lang3 != "") {
+ AspellConfig *config = new_aspell_config();
+#ifdef WIN32
+ aspell_config_replace(config, "prefix", exeName);
+#endif
+ aspell_config_replace(config, "lang", _lang3.c_str());
+ aspell_config_replace(config, "encoding", "UTF-8");
+ AspellCanHaveError *ret = new_aspell_speller(config);
+ delete_aspell_config(config);
+ if (aspell_error(ret) != 0) {
+ g_warning("Error: %s\n", aspell_error_message(ret));
+ delete_aspell_can_have_error(ret);
+ return false;
+ }
+ _speller3 = to_aspell_speller(ret);
+ }
+
+ _root = sp_desktop_document(desktop)->getRoot();
+
+ // empty the list of objects we've checked
+ g_slist_free (_seen_objects);
+ _seen_objects = NULL;
+
+ // grab first text
+ nextText();
+
+ _working = true;
+
+ return true;
+}
+
+void
+SpellCheck::finished ()
+{
+ aspell_speller_save_all_word_lists(_speller);
+ delete_aspell_speller(_speller);
+ _speller = NULL;
+ if (_speller2) {
+ aspell_speller_save_all_word_lists(_speller2);
+ delete_aspell_speller(_speller2);
+ _speller2 = NULL;
+ }
+ if (_speller3) {
+ aspell_speller_save_all_word_lists(_speller3);
+ delete_aspell_speller(_speller3);
+ _speller3 = NULL;
+ }
+
+ clearRects();
+ disconnect();
+
+ //desktop->clearWaitingCursor();
+
+ tree_view.set_sensitive(false);
+ accept_button.set_sensitive(false);
+ ignore_button.set_sensitive(false);
+ ignoreonce_button.set_sensitive(false);
+ gtk_widget_set_sensitive(dictionary_combo, false);
+ add_button.set_sensitive(false);
+ stop_button.set_sensitive(false);
+ start_button.set_sensitive(true);
+
+ {
+ gchar *label;
+ if (_stops)
+ label = g_strdup_printf(_("<b>Finished</b>, <b>%d</b> words added to dictionary"), _adds);
+ else
+ label = g_strdup_printf(_("<b>Finished</b>, nothing suspicious found"));
+ banner_label.set_markup(label);
+ g_free(label);
+ }
+
+ g_slist_free(_seen_objects);
+ _seen_objects = NULL;
+
+ desktop = NULL;
+ _root = NULL;
+
+ _working = false;
+}
+
+bool
+SpellCheck::nextWord()
+{
+ if (!_working)
+ return false;
+
+ if (!_text) {
+ finished();
+ return false;
+ }
+ _word.clear();
+
+ while (_word.size() == 0) {
+ _begin_w = _end_w;
+
+ if (!_layout || _begin_w == _layout->end()) {
+ nextText();
+ return false;
+ }
+
+ if (!_layout->isStartOfWord(_begin_w)) {
+ _begin_w.nextStartOfWord();
+ }
+
+ _end_w = _begin_w;
+ _end_w.nextEndOfWord();
+ _word = sp_te_get_string_multiline (_text, _begin_w, _end_w);
+ }
+
+ // try to link this word with the next if separated by '
+ void *rawptr;
+ Glib::ustring::iterator text_iter;
+ _layout->getSourceOfCharacter(_end_w, &rawptr, &text_iter);
+ SPObject *char_item = SP_OBJECT(rawptr);
+ if (SP_IS_STRING(char_item)) {
+ int this_char = *text_iter;
+ if (this_char == '\'' || this_char == 0x2019) {
+ Inkscape::Text::Layout::iterator end_t = _end_w;
+ end_t.nextCharacter();
+ _layout->getSourceOfCharacter(end_t, &rawptr, &text_iter);
+ SPObject *char_item = SP_OBJECT(rawptr);
+ if (SP_IS_STRING(char_item)) {
+ int this_char = *text_iter;
+ if (g_ascii_isalpha(this_char)) { // 's
+ _end_w.nextEndOfWord();
+ _word = sp_te_get_string_multiline (_text, _begin_w, _end_w);
+ }
+ }
+ }
+ }
+
+ // skip words containing digits
+ if (_prefs->getInt(_prefs_path + "ignorenumbers") != 0) {
+ bool digits = false;
+ for (unsigned int i = 0; i < _word.size(); i++) {
+ if (g_unichar_isdigit(_word[i])) {
+ digits = true;
+ break;
+ }
+ }
+ if (digits) {
+ return false;
+ }
+ }
+
+ // skip ALL-CAPS words
+ if (_prefs->getInt(_prefs_path + "ignoreallcaps") != 0) {
+ bool allcaps = true;
+ for (unsigned int i = 0; i < _word.size(); i++) {
+ if (!g_unichar_isupper(_word[i])) {
+ allcaps = false;
+ break;
+ }
+ }
+ if (allcaps) {
+ return false;
+ }
+ }
+
+ // run it by all active spellers
+ int have = aspell_speller_check(_speller, _word.c_str(), -1);
+ if (_speller2)
+ have += aspell_speller_check(_speller2, _word.c_str(), -1);
+ if (_speller3)
+ have += aspell_speller_check(_speller3, _word.c_str(), -1);
+
+ if (have == 0) { // not found in any!
+ _stops ++;
+
+ //desktop->clearWaitingCursor();
+
+ // display it in window
+ {
+ Glib::ustring langs = _lang;
+ if (_lang2 != "")
+ langs = langs + ", " + _lang2;
+ if (_lang3 != "")
+ langs = langs + ", " + _lang3;
+ gchar *label = g_strdup_printf(_("Not in dictionary (%s): <b>%s</b>"), langs.c_str(), _word.c_str());
+ banner_label.set_markup(label);
+ g_free(label);
+ }
+
+ tree_view.set_sensitive(true);
+ ignore_button.set_sensitive(true);
+ ignoreonce_button.set_sensitive(true);
+ gtk_widget_set_sensitive(dictionary_combo, true);
+ add_button.set_sensitive(true);
+ stop_button.set_sensitive(true);
+
+ // draw rect
+ std::vector<Geom::Point> points =
+ _layout->createSelectionShape(_begin_w, _end_w, _text->i2dt_affine());
+ Geom::Point tl, br;
+ tl = br = points.front();
+ for (unsigned i = 0 ; i < points.size() ; i ++) {
+ if (points[i][Geom::X] < tl[Geom::X])
+ tl[Geom::X] = points[i][Geom::X];
+ if (points[i][Geom::Y] < tl[Geom::Y])
+ tl[Geom::Y] = points[i][Geom::Y];
+ if (points[i][Geom::X] > br[Geom::X])
+ br[Geom::X] = points[i][Geom::X];
+ if (points[i][Geom::Y] > br[Geom::Y])
+ br[Geom::Y] = points[i][Geom::Y];
+ }
+
+ // expand slightly
+ Geom::Rect area = Geom::Rect(tl, br);
+ double mindim = fabs(tl[Geom::Y] - br[Geom::Y]);
+ if (fabs(tl[Geom::X] - br[Geom::X]) < mindim)
+ mindim = fabs(tl[Geom::X] - br[Geom::X]);
+ area.expandBy(MAX(0.05 * mindim, 1));
+
+ // create canvas path rectangle, red stroke
+ SPCanvasItem *rect = sp_canvas_bpath_new(sp_desktop_sketch(desktop), NULL);
+ sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(rect), 0xff0000ff, 3.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
+ sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(rect), 0, SP_WIND_RULE_NONZERO);
+ SPCurve *curve = new SPCurve();
+ curve->moveto(area.corner(0));
+ curve->lineto(area.corner(1));
+ curve->lineto(area.corner(2));
+ curve->lineto(area.corner(3));
+ curve->lineto(area.corner(0));
+ sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(rect), curve);
+ sp_canvas_item_show(rect);
+ _rects = g_slist_prepend(_rects, rect);
+
+ // scroll to make it all visible
+ Geom::Point const center = desktop->get_display_area().midpoint();
+ area.expandBy(0.5 * mindim);
+ Geom::Point scrollto;
+ double dist = 0;
+ for (unsigned corner = 0; corner < 4; corner ++) {
+ if (Geom::L2(area.corner(corner) - center) > dist) {
+ dist = Geom::L2(area.corner(corner) - center);
+ scrollto = area.corner(corner);
+ }
+ }
+ desktop->scroll_to_point (scrollto, 1.0);
+
+ // select text; if in Text tool, position cursor to the beginning of word
+ // unless it is already in the word
+ if (desktop->selection->singleItem() != _text)
+ desktop->selection->set (_text);
+ if (tools_isactive(desktop, TOOLS_TEXT)) {
+ Inkscape::Text::Layout::iterator *cursor =
+ sp_text_context_get_cursor_position(SP_TEXT_CONTEXT(desktop->event_context), _text);
+ if (!cursor) // some other text is selected there
+ desktop->selection->set (_text);
+ else if (*cursor <= _begin_w || *cursor >= _end_w)
+ sp_text_context_place_cursor (SP_TEXT_CONTEXT(desktop->event_context), _text, _begin_w);
+ }
+
+ // get suggestions
+ {
+ model = Gtk::ListStore::create(tree_columns);
+ tree_view.set_model(model);
+
+ {
+ const AspellWordList *wl = aspell_speller_suggest(_speller, _word.c_str(), -1);
+ AspellStringEnumeration * els = aspell_word_list_elements(wl);
+ const char *sugg;
+ Gtk::TreeModel::iterator iter;
+
+ while ((sugg = aspell_string_enumeration_next(els)) != 0) {
+ iter = model->append();
+ Gtk::TreeModel::Row row = *iter;
+ row[tree_columns.suggestions] = sugg;
+ }
+ delete_aspell_string_enumeration(els);
+ }
+
+ if (_speller2) {
+ const AspellWordList *wl = aspell_speller_suggest(_speller2, _word.c_str(), -1);
+ AspellStringEnumeration * els = aspell_word_list_elements(wl);
+ const char *sugg;
+ Gtk::TreeModel::iterator iter;
+ while ((sugg = aspell_string_enumeration_next(els)) != 0) {
+ iter = model->append();
+ Gtk::TreeModel::Row row = *iter;
+ row[tree_columns.suggestions] = sugg;
+ }
+ delete_aspell_string_enumeration(els);
+ }
+
+ if (_speller3) {
+ const AspellWordList *wl = aspell_speller_suggest(_speller3, _word.c_str(), -1);
+ AspellStringEnumeration * els = aspell_word_list_elements(wl);
+ const char *sugg;
+ Gtk::TreeModel::iterator iter;
+ while ((sugg = aspell_string_enumeration_next(els)) != 0) {
+ iter = model->append();
+ Gtk::TreeModel::Row row = *iter;
+ row[tree_columns.suggestions] = sugg;
+ }
+ delete_aspell_string_enumeration(els);
+ }
+
+ accept_button.set_sensitive(false); // gray it out until something is chosen
+ }
+
+ return true;
+
+ }
+ return false;
+}
+
+
+
+void
+SpellCheck::deleteLastRect ()
+{
+ if (_rects) {
+ sp_canvas_item_hide(SP_CANVAS_ITEM(_rects->data));
+ gtk_object_destroy(GTK_OBJECT(_rects->data));
+ _rects = _rects->next; // pop latest-prepended rect
+ }
+}
+
+void SpellCheck::doSpellcheck ()
+{
+ banner_label.set_markup(_("<i>Checking...</i>"));
+
+ //desktop->setWaitingCursor();
+
+ while (_working)
+ if (nextWord())
+ break;
+}
+
+void SpellCheck::onTreeSelectionChange()
+{
+ accept_button.set_sensitive(true);
+}
+
+void SpellCheck::onObjModified (SPObject* /* blah */, unsigned int /* bleh */)
+{
+ if (_local_change) { // this was a change by this dialog, i.e. an Accept, skip it
+ _local_change = false;
+ return;
+ }
+
+ if (_working && _root) {
+ // user may have edited the text we're checking; try to do the most sensible thing in this
+ // situation
+
+ // just in case, re-get text's layout
+ _layout = te_get_layout (_text);
+
+ // re-get the word
+ _layout->validateIterator(&_begin_w);
+ _end_w = _begin_w;
+ _end_w.nextEndOfWord();
+ Glib::ustring word_new = sp_te_get_string_multiline (_text, _begin_w, _end_w);
+ if (word_new != _word) {
+ _end_w = _begin_w;
+ deleteLastRect ();
+ doSpellcheck (); // recheck this word and go ahead if it's ok
+ }
+ }
+}
+
+void SpellCheck::onObjReleased (SPObject* /* blah */)
+{
+ if (_working && _root) {
+ // the text object was deleted
+ deleteLastRect ();
+ nextText();
+ doSpellcheck (); // get next text and continue
+ }
+}
+
+void SpellCheck::onAccept ()
+{
+ // insert chosen suggestion
+
+ Glib::RefPtr<Gtk::TreeSelection> selection = tree_view.get_selection();
+ Gtk::TreeModel::iterator iter = selection->get_selected();
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+ Glib::ustring sugg = row[tree_columns.suggestions];
+
+ if (sugg.length() > 0) {
+ //g_print("chosen: %s\n", sugg);
+ _local_change = true;
+ sp_te_replace(_text, _begin_w, _end_w, sugg.c_str());
+ // find the end of the word anew
+ _end_w = _begin_w;
+ _end_w.nextEndOfWord();
+ DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_TEXT,
+ _("Fix spelling"));
+ }
+ }
+
+ deleteLastRect();
+ doSpellcheck();
+}
+
+void
+SpellCheck::onIgnore ()
+{
+ aspell_speller_add_to_session(_speller, _word.c_str(), -1);
+ if (_speller2)
+ aspell_speller_add_to_session(_speller2, _word.c_str(), -1);
+ if (_speller3)
+ aspell_speller_add_to_session(_speller3, _word.c_str(), -1);
+
+ deleteLastRect();
+ doSpellcheck();
+}
+
+void
+SpellCheck::onIgnoreOnce ()
+{
+ deleteLastRect();
+ doSpellcheck();
+}
+
+void
+SpellCheck::onAdd ()
+{
+ _adds++;
+
+ gint num = gtk_combo_box_get_active((GtkComboBox *)dictionary_combo);
+ switch (num) {
+ case 0:
+ aspell_speller_add_to_personal(_speller, _word.c_str(), -1);
+ break;
+ case 1:
+ if (_speller2)
+ aspell_speller_add_to_personal(_speller2, _word.c_str(), -1);
+ break;
+ case 2:
+ if (_speller3)
+ aspell_speller_add_to_personal(_speller3, _word.c_str(), -1);
+ break;
+ default:
+ break;
+ }
+
+ deleteLastRect();
+ doSpellcheck();
+}
+
+void
+SpellCheck::onStop ()
+{
+ finished();
+}
+
+void
+SpellCheck::onStart ()
+{
+ if (init (SP_ACTIVE_DESKTOP))
+ doSpellcheck();
+}
+
+
+}
+}
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :