Code

spellchecker
authorbuliabyak <buliabyak@users.sourceforge.net>
Tue, 17 Feb 2009 20:33:18 +0000 (20:33 +0000)
committerbuliabyak <buliabyak@users.sourceforge.net>
Tue, 17 Feb 2009 20:33:18 +0000 (20:33 +0000)
src/dialogs/Makefile_insert
src/dialogs/spellcheck.cpp [new file with mode: 0644]
src/dialogs/spellcheck.h [new file with mode: 0644]
src/menus-skeleton.h
src/preferences-skeleton.h
src/verbs.cpp
src/verbs.h

index 5d561466849f3cc3c131d46d21ff04d4f6475c4c..1b3358506a5e19cf9cc8e425da712742fa791f4d 100644 (file)
@@ -50,6 +50,8 @@ dialogs_libspdialogs_a_SOURCES =      \
        dialogs/rdf.h                   \
        dialogs/sp-attribute-widget.cpp \
        dialogs/sp-attribute-widget.h   \
+       dialogs/spellcheck.cpp          \
+       dialogs/spellcheck.h                    \
        dialogs/stroke-style.cpp        \
        dialogs/stroke-style.h          \
        dialogs/swatches.cpp            \
diff --git a/src/dialogs/spellcheck.cpp b/src/dialogs/spellcheck.cpp
new file mode 100644 (file)
index 0000000..853c6bc
--- /dev/null
@@ -0,0 +1,834 @@
+/** @file
+ * @brief  Spellcheck dialog
+ */
+/* Authors:
+ *   bulia byak <bulia@users.sf.net>
+ *
+ * Copyright (C) 2009 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "widgets/icon.h"
+#include "message-stack.h"
+
+#include <aspell.h>
+#include <gtk/gtk.h>
+
+#include <glibmm/i18n.h>
+#include "helper/window.h"
+#include "macros.h"
+#include "inkscape.h"
+#include "document.h"
+#include "desktop.h"
+#include "selection.h"
+#include "desktop-handles.h"
+#include "dialog-events.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"
+
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+#define MIN_ONSCREEN_DISTANCE 50
+
+static GtkWidget *dlg = NULL;
+static win_data wd;
+
+// impossible original values to make sure they are read from prefs
+static gint x = -1000, y = -1000, w = 0, h = 0;
+static Glib::ustring const prefs_path = "/dialogs/spellcheck/";
+
+// C++ for the poor: instead of creating a formal C++ class, I just treat this entire file as a
+// class, with the globals as its data fields. In such a simple case as this, when no inheritance
+// or encapsulation are necessary, this is much simpler and less verbose, and mixes easily with
+// plain-C GTK callbacks.
+
+static SPDesktop *_desktop = NULL;
+static AspellSpeller *_speller = NULL;
+static SPObject *_root;
+
+// list of canvasitems (currently just rects) that mark misspelled things on canvas
+static GSList *_rects = NULL;
+
+// list of text objects we have already checked in this session
+static GSList *_seen_objects = NULL;
+
+// the object currently being checked
+static SPItem *_text = NULL;
+// its layout
+static Inkscape::Text::Layout const *_layout = NULL;
+
+// iterators for the start and end of the current word
+static Inkscape::Text::Layout::iterator _begin_w;
+static Inkscape::Text::Layout::iterator _end_w;
+
+// the word we're checking
+static Glib::ustring _word;
+
+// counters for the number of stops and dictionary adds
+static int _stops = 0;
+static int _adds = 0;
+
+// true if we are in the middle of a check
+static bool _working = false;
+
+// connect to the object being checked in case it is modified or deleted by user
+static sigc::connection *_modified_connection = NULL;
+static sigc::connection *_release_connection = NULL;
+
+// true if the spell checker dialog has changed text, to suppress modified callback
+static bool _local_change = false;
+
+
+
+void spellcheck_clear_rects()
+{
+    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();
+        delete _release_connection;
+        _release_connection = NULL;
+    }
+    if (_modified_connection) {
+        _modified_connection->disconnect();
+        delete _modified_connection;
+        _modified_connection = NULL;
+    }
+}
+
+static void sp_spellcheck_dialog_destroy(GtkObject *object, gpointer)
+{
+    spellcheck_clear_rects();
+    spellcheck_disconnect();
+
+    sp_signal_disconnect_by_data (INKSCAPE, object);
+    wd.win = dlg = NULL;
+    wd.stop = 0;
+}
+
+
+static gboolean sp_spellcheck_dialog_delete(GtkObject *, GdkEvent *, gpointer /*data*/)
+{
+    spellcheck_clear_rects();
+    spellcheck_disconnect();
+
+    gtk_window_get_position (GTK_WINDOW (dlg), &x, &y);
+    gtk_window_get_size (GTK_WINDOW (dlg), &w, &h);
+
+    if (x<0) x=0;
+    if (y<0) y=0;
+
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+    prefs->setInt(prefs_path + "x", x);
+    prefs->setInt(prefs_path + "y", y);
+    prefs->setInt(prefs_path + "w", w);
+    prefs->setInt(prefs_path + "h", h);
+
+    return FALSE; // which means, go ahead and destroy it
+}
+
+void
+sp_spellcheck_new_button (GtkWidget *dlg, GtkWidget *hb, const gchar *label, GtkTooltips *tt, const gchar *tip, void (*function) (GObject *, GObject *), const gchar *cookie)
+{
+    GtkWidget *b = gtk_button_new_with_mnemonic (label);
+    gtk_tooltips_set_tip (tt, b, tip, NULL);
+    gtk_box_pack_start (GTK_BOX (hb), b, TRUE, TRUE, 0);
+    g_signal_connect ( G_OBJECT (b), "clicked", G_CALLBACK (function), dlg );
+    gtk_object_set_data (GTK_OBJECT (dlg), cookie, b);
+    gtk_widget_show (b);
+}
+
+
+
+GSList *
+all_text_items (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 (SP_OBJECT_REPR (r)->name(), "svg:metadata"))
+        return l; // we're not interested in metadata
+
+    for (SPObject *child = sp_object_first_child(r); child; child = SP_OBJECT_NEXT (child)) {
+        if (SP_IS_ITEM (child) && !SP_OBJECT_IS_CLONED (child) && !_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 = all_text_items (child, l, hidden, locked);
+    }
+    return l;
+}
+
+bool 
+spellcheck_text_is_valid (SPObject *root, SPItem *text)
+{
+    GSList *l = NULL;
+    l = all_text_items (root, l, false, true);
+    for (GSList *i = l; i; i = i->next) {
+        SPItem *item = (SPItem *) i->data;
+        if (item == text) {
+            g_slist_free (l);
+            return true;
+        }
+    }
+    g_slist_free (l);
+    return false;
+}
+
+gint compare_text_bboxes (gconstpointer a, gconstpointer b)
+{
+    SPItem *i1 = SP_ITEM(a);
+    SPItem *i2 = SP_ITEM(b);
+
+    Geom::OptRect bbox1 = i1->getBounds(sp_item_i2d_affine(i1));
+    Geom::OptRect bbox2 = i2->getBounds(sp_item_i2d_affine(i2));
+    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_get_text (SPObject *root)
+{
+    GSList *l = NULL;
+    l = all_text_items (root, l, false, true);
+    l = g_slist_sort(l, compare_text_bboxes);
+
+    for (GSList *i = l; i; i = i->next) {
+        SPItem *item = (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_sensitive (const gchar *cookie, gboolean gray)
+{
+   GtkWidget *l = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (dlg), cookie));
+   gtk_widget_set_sensitive(l, gray);
+}
+
+static void spellcheck_enable_accept(GtkTreeSelection *selection,
+                                               void *)
+{
+    spellcheck_sensitive ("b_accept", TRUE);
+}
+
+static void spellcheck_obj_modified (SPObject *obj, guint /*flags*/, gpointer /*data*/);
+static void spellcheck_obj_released (SPObject *obj, gpointer /*data*/);
+
+void
+spellcheck_next_text()
+{
+    spellcheck_disconnect();
+
+    _text = spellcheck_get_text(_root);
+    if (_text) {
+        _release_connection = new sigc::connection (SP_OBJECT(_text)->connectRelease(
+             sigc::bind<1>(sigc::ptr_fun(&spellcheck_obj_released), dlg)));
+
+        _modified_connection = new sigc::connection (SP_OBJECT(_text)->connectModified(
+             sigc::bind<2>(sigc::ptr_fun(&spellcheck_obj_modified), dlg)));
+
+        _layout = te_get_layout (_text);
+        _begin_w = _layout->begin();
+    } 
+    _end_w = _begin_w;
+    _word.clear();
+}
+
+bool
+spellcheck_init(SPDesktop *desktop)
+{
+    _desktop = desktop;
+
+    spellcheck_sensitive("suggestions", FALSE);
+    spellcheck_sensitive("b_accept", FALSE);
+    spellcheck_sensitive("b_ignore", FALSE);
+    spellcheck_sensitive("b_add", FALSE);
+    spellcheck_sensitive("b_start", FALSE);
+
+    _stops = 0;
+    _adds = 0;
+
+    spellcheck_clear_rects();
+
+    AspellConfig *config = new_aspell_config();
+
+#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);
+    aspell_config_replace(config, "prefix", exeName);
+#endif
+
+    // take language from prefs
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+    Glib::ustring lang = prefs->getString(prefs_path + "lang");
+    if (lang != "")
+        aspell_config_replace(config, "lang", lang.c_str());
+    else
+        aspell_config_replace(config, "lang", "en_US");
+
+    aspell_config_replace(config, "encoding", "UTF-8");
+
+    // create speller
+    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);
+
+    _root = SP_DOCUMENT_ROOT (sp_desktop_document (desktop));
+
+    // empty the list of objects we've checked
+    g_slist_free (_seen_objects);
+    _seen_objects = NULL;
+
+    // grab first text
+    spellcheck_next_text();
+
+    _working = true;
+
+    return true;
+}
+
+void
+spellcheck_finished ()
+{
+    aspell_speller_save_all_word_lists(_speller);
+    delete_aspell_speller(_speller);
+    _speller = NULL;
+
+    spellcheck_clear_rects();
+    spellcheck_disconnect();
+
+    _desktop->clearWaitingCursor();
+
+    spellcheck_sensitive("suggestions", FALSE);
+    spellcheck_sensitive("b_accept", FALSE);
+    spellcheck_sensitive("b_ignore", FALSE);
+    spellcheck_sensitive("b_add", FALSE);
+    spellcheck_sensitive("b_stop", FALSE);
+    spellcheck_sensitive("b_start", TRUE);
+
+    {
+        GtkWidget *l = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (dlg), "banner"));
+        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"));
+        gtk_label_set_markup (GTK_LABEL(l), label);
+        g_free(label);
+    }
+
+    g_slist_free(_seen_objects);
+    _seen_objects = NULL;
+
+    _desktop = NULL;
+    _root = NULL;
+
+    _working = false;
+}
+
+bool
+spellcheck_next_word()
+{
+    if (!_working)
+        return false;
+
+    if (!_text) {
+        spellcheck_finished();
+        return false;
+    }
+    _word.clear();
+
+    while (_word.size() == 0) {
+        _begin_w = _end_w;
+
+        if (!_layout || _begin_w == _layout->end()) {
+            spellcheck_next_text();
+            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
+    bool digits = false;
+    for (gchar *c = (gchar *) _word.c_str(); *c; c++) {
+        if (g_ascii_isdigit(*c)) {
+            digits = true;
+        }
+    }
+    if (digits) { 
+        return false;
+    }
+
+    //g_print ("%s\n", word.c_str());
+
+    int have = aspell_speller_check(_speller, _word.c_str(), -1);
+    if (have == 0) { // not found
+        _stops ++;
+
+        _desktop->clearWaitingCursor();
+
+        // display it in window
+        {
+            GtkWidget *l = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (dlg), "banner"));
+            gchar *label = g_strdup_printf(_("Not in dictionary: <b>%s</b>"), _word.c_str());
+            gtk_label_set_markup (GTK_LABEL(l), label);
+            g_free(label);
+        }
+
+        spellcheck_sensitive("suggestions", TRUE);
+        spellcheck_sensitive("b_ignore", TRUE);
+        spellcheck_sensitive("b_add", TRUE);
+        spellcheck_sensitive("b_stop", TRUE);
+
+        // draw rect
+        std::vector<Geom::Point> points = 
+            _layout->createSelectionShape(_begin_w, _end_w, sp_item_i2d_affine(_text));
+        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);
+
+        // if in Text tool, position cursor to the beginnign of word
+        // unless it is already in the word
+        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);
+        } else { // just select the object
+            _desktop->selection->set (_text);
+        }
+
+        // get suggestions
+        {
+            const AspellWordList *wl = aspell_speller_suggest(_speller, _word.c_str(), -1);
+            AspellStringEnumeration * els = aspell_word_list_elements(wl);
+            const char *sugg;
+            GtkTreeView *tree_view = 
+                GTK_TREE_VIEW(gtk_object_get_data (GTK_OBJECT (dlg), "suggestions"));
+            GtkListStore *model = gtk_list_store_new (1, G_TYPE_STRING);
+            gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), GTK_TREE_MODEL (model));
+            GtkTreeIter iter;
+            while ((sugg = aspell_string_enumeration_next(els)) != 0) {
+                gtk_list_store_append (GTK_LIST_STORE (model), &iter);
+                gtk_list_store_set (GTK_LIST_STORE (model), 
+                                    &iter,
+                                    0, sugg,
+                                    -1);
+            }
+            delete_aspell_string_enumeration(els);
+            spellcheck_sensitive("b_accept", FALSE); // gray it out until something is chosen
+        }
+
+        return true;
+
+    }
+    return false;
+}
+
+
+
+void
+spellcheck_delete_last_rect ()
+{
+    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
+do_spellcheck ()
+{
+    GtkWidget *l = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (dlg), "banner"));
+    gtk_label_set_markup (GTK_LABEL(l), _("<i>Checking...</i>"));
+    gtk_widget_queue_draw(GTK_WIDGET(dlg));
+    gdk_window_process_updates(GTK_WIDGET(dlg)->window, TRUE);
+
+    _desktop->setWaitingCursor();
+
+    while (_working)
+        if (spellcheck_next_word())
+            break;
+}
+
+static void
+spellcheck_obj_modified (SPObject *obj, guint /*flags*/, gpointer /*data*/)
+{
+    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;
+            spellcheck_delete_last_rect ();
+            do_spellcheck (); // recheck this word and go ahead if it's ok
+        }
+    }
+}
+
+static void
+spellcheck_obj_released (SPObject *obj, gpointer /*data*/)
+{
+    if (_working && _root) { 
+        // the text object was deleted
+        spellcheck_delete_last_rect ();
+        spellcheck_next_text();
+        do_spellcheck (); // get next text and continue
+    }
+}
+
+void
+sp_spellcheck_accept (GObject *, GObject *dlg)
+{
+    // insert chosen suggestion
+    GtkTreeView *tv = 
+        GTK_TREE_VIEW(gtk_object_get_data (GTK_OBJECT (dlg), "suggestions"));
+    GtkTreeSelection *ts = gtk_tree_view_get_selection(tv);
+    GtkTreeModel *model = 0;
+    GtkTreeIter   iter;
+    if (gtk_tree_selection_get_selected(ts, &model, &iter)) {
+        gchar *sugg;
+        gtk_tree_model_get (model, &iter, 0, &sugg, -1);
+        if (sugg) {
+            //g_print("chosen: %s\n", sugg);
+            _local_change = true;
+            sp_te_replace(_text, _begin_w, _end_w, sugg);
+            // find the end of the word anew
+            _end_w = _begin_w;
+            _end_w.nextEndOfWord();
+            sp_document_done (sp_desktop_document(_desktop), SP_VERB_CONTEXT_TEXT,
+                              _("Fix spelling"));
+        }
+    }
+
+    spellcheck_delete_last_rect ();
+
+    do_spellcheck(); // next word or end
+}
+
+void
+sp_spellcheck_ignore (GObject *, GObject *dlg)
+{
+    aspell_speller_add_to_session(_speller, _word.c_str(), -1);
+    spellcheck_delete_last_rect ();
+
+    do_spellcheck(); // next word or end
+}
+
+void
+sp_spellcheck_add (GObject *, GObject *dlg)
+{
+    _adds++;
+    aspell_speller_add_to_personal(_speller, _word.c_str(), -1);
+    spellcheck_delete_last_rect ();
+
+    do_spellcheck(); // next word or end
+}
+
+void
+sp_spellcheck_stop (GObject *, GObject *dlg)
+{
+    spellcheck_finished();
+}
+
+void
+sp_spellcheck_start (GObject *, GObject *)
+{
+    if (spellcheck_init (SP_ACTIVE_DESKTOP))
+        do_spellcheck(); // next word or end
+}
+
+static gboolean spellcheck_desktop_deactivated(Inkscape::Application *application, SPDesktop *desktop, void *data)
+{
+    if (_working) {
+        if (_desktop == desktop) {
+            spellcheck_finished();
+        }
+    }
+    return FALSE;
+}
+
+
+void
+sp_spellcheck_dialog (void)
+{
+    if  (!dlg)
+    {
+        gchar title[500];
+        sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_DIALOG_SPELLCHECK), title);
+        Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+
+        dlg = sp_window_new (title, TRUE);
+        if (x == -1000 || y == -1000) {
+            x = prefs->getInt(prefs_path + "x", -1000);
+            y = prefs->getInt(prefs_path + "y", -1000);
+        }
+        if (w ==0 || h == 0) {
+            w = prefs->getInt(prefs_path + "w", 0);
+            h = prefs->getInt(prefs_path + "h", 0);
+        }
+        
+        if (w && h)
+            gtk_window_resize ((GtkWindow *) dlg, w, h);
+        if (x >= 0 && y >= 0 && (x < (gdk_screen_width()-MIN_ONSCREEN_DISTANCE)) && (y < (gdk_screen_height()-MIN_ONSCREEN_DISTANCE))) {
+            gtk_window_move ((GtkWindow *) dlg, x, y);
+        } else {
+            gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
+        }
+
+        sp_transientize (dlg);
+        wd.win = dlg;
+        wd.stop = 0;
+        g_signal_connect   ( G_OBJECT (INKSCAPE), "activate_desktop", G_CALLBACK (sp_transientize_callback), &wd );
+
+        g_signal_connect( G_OBJECT(INKSCAPE), "deactivate_desktop", G_CALLBACK( spellcheck_desktop_deactivated ), NULL);
+
+
+        gtk_signal_connect ( GTK_OBJECT (dlg), "event", GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg);
+
+        gtk_signal_connect ( GTK_OBJECT (dlg), "destroy", G_CALLBACK (sp_spellcheck_dialog_destroy), NULL );
+        gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event", G_CALLBACK (sp_spellcheck_dialog_delete), dlg);
+        g_signal_connect   ( G_OBJECT (INKSCAPE), "shut_down", G_CALLBACK (sp_spellcheck_dialog_delete), dlg);
+
+        g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_hide", G_CALLBACK (sp_dialog_hide), dlg);
+        g_signal_connect   ( G_OBJECT (INKSCAPE), "dialogs_unhide", G_CALLBACK (sp_dialog_unhide), dlg);
+
+        GtkTooltips *tt = gtk_tooltips_new ();
+
+        gtk_container_set_border_width (GTK_CONTAINER (dlg), 4);
+
+        /* Toplevel vbox */
+        GtkWidget *vb = gtk_vbox_new (FALSE, 4);
+        gtk_container_add (GTK_CONTAINER (dlg), vb);
+
+        {
+            GtkWidget *hb = gtk_hbox_new (FALSE, 0);
+            GtkWidget *l = gtk_label_new (NULL);
+            gtk_object_set_data (GTK_OBJECT (dlg), "banner", l);
+            gtk_box_pack_start (GTK_BOX (hb), l, FALSE, FALSE, 0);
+            gtk_box_pack_start (GTK_BOX (vb), hb, FALSE, FALSE, 0);
+        }
+
+        {
+            GtkWidget *scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+            gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+                                            GTK_POLICY_AUTOMATIC, 
+                                            GTK_POLICY_AUTOMATIC);
+   
+            GtkListStore *model = gtk_list_store_new (1, G_TYPE_STRING);
+            GtkWidget *tree_view = gtk_tree_view_new ();
+            gtk_object_set_data (GTK_OBJECT (dlg), "suggestions", tree_view);
+            gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled_window), 
+                                                   tree_view);
+            gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), GTK_TREE_MODEL (model));
+            GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(tree_view));
+            g_signal_connect (G_OBJECT(selection), "changed", 
+                              G_CALLBACK (spellcheck_enable_accept), NULL);
+            gtk_widget_show (tree_view);
+            GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
+            GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes (_("Suggestions:"),
+                                                                                  cell,
+                                                                                  "text", 0,
+                                                                                  NULL);
+            gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view),
+                                         GTK_TREE_VIEW_COLUMN (column));
+            gtk_box_pack_start (GTK_BOX (vb), scrolled_window, TRUE, TRUE, 0);
+        }
+
+
+        {
+            GtkWidget *hb = gtk_hbox_new (FALSE, 0);
+            sp_spellcheck_new_button (dlg, hb, _("_Accept"), tt, _("Accept the chosen suggestion"), 
+                                      sp_spellcheck_accept, "b_accept");
+            sp_spellcheck_new_button (dlg, hb, _("_Ignore"), tt, _("Ignore this word in this session"), 
+                                      sp_spellcheck_ignore, "b_ignore");
+            sp_spellcheck_new_button (dlg, hb, _("A_dd"), tt, _("Add this word to the dictionary"), 
+                                      sp_spellcheck_add, "b_add");
+            gtk_box_pack_start (GTK_BOX (vb), hb, FALSE, FALSE, 0);
+        }
+
+        {
+            GtkWidget *hs = gtk_hseparator_new ();
+            gtk_box_pack_start (GTK_BOX (vb), hs, FALSE, FALSE, 0);
+        }
+
+        {
+            GtkWidget *hb = gtk_hbox_new (FALSE, 0);
+            sp_spellcheck_new_button (dlg, hb, _("_Stop"), tt, _("Stop the check"), 
+                                      sp_spellcheck_stop, "b_stop");
+            sp_spellcheck_new_button (dlg, hb, _("_Start"), tt, _("Start the check"), 
+                                      sp_spellcheck_start, "b_start");
+            gtk_box_pack_start (GTK_BOX (vb), hb, FALSE, FALSE, 0);
+        }
+
+        gtk_widget_show_all (vb);
+    }
+
+    gtk_window_present ((GtkWindow *) dlg);
+
+    // run it at once
+    sp_spellcheck_start (NULL, NULL);
+}
+
+
+/*
+  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:encoding=utf-8:textwidth=99 :
diff --git a/src/dialogs/spellcheck.h b/src/dialogs/spellcheck.h
new file mode 100644 (file)
index 0000000..b941788
--- /dev/null
@@ -0,0 +1,31 @@
+/** @file
+ * @brief  Spellcheck dialog
+ */
+/* Authors:
+ *   bulia byak <bulia@users.sf.net>
+ *
+ * Copyright (C) 2009 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef SEEN_SPELLCHECK_H
+#define SEEN_SPELLCHECK_H
+
+#include <gtk/gtkstyle.h>
+
+void sp_spellcheck_dialog();
+
+
+#endif /* !SEEN_SPELLCHECK_H */
+
+/*
+  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 :
index 9840a1aa15d246b02da40c52907a5623b80691bb..5d110483dc3144beae7ebbbe6af5709b72f0bb90 100644 (file)
@@ -238,6 +238,8 @@ static char const menus_skeleton[] =
 "       <verb verb-id=\"ObjectFlowtextToText\" />\n"
 "       <separator/>\n"
 "       <verb verb-id=\"SelectionTextRemoveKerns\" />\n"
+"       <separator/>\n"
+"       <verb verb-id=\"DialogSpellcheck\" />\n"
 "   </submenu>\n"
 "   <submenu name=\"" N_("Filter_s") "\">\n"
 "       <filters-list/>\n"
index 64a2b4ba1cd95793de890ba3a8f4b4af1bbbd514..65d30d6a6e120147372ffc8797839a2cc5a6727d 100644 (file)
@@ -174,6 +174,7 @@ static char const preferences_skeleton[] =
 "    <group id=\"align\"/>\n"
 "    <group id=\"xml\"/>\n"
 "    <group id=\"find\"/>\n"
+"    <group id=\"spellcheck\" w=\"200\" h=\"250\" lang=\"en_US\"/>\n"
 "    <group id=\"documentoptions\" state=\"1\"/>\n"
 "    <group id=\"preferences\" state=\"1\"/>\n"
 "    <group id=\"gradienteditor\"/>\n"
index 78c4423f51efb1603199602dff2402360456c583..56d4e667d98040041e04d6bb530326da261b2785 100644 (file)
@@ -42,6 +42,7 @@
 #include "dialogs/xml-tree.h"
 #include "dialogs/item-properties.h"
 #include "dialogs/find.h"
+#include "dialogs/spellcheck.h"
 #include "dialogs/layer-properties.h"
 #include "dialogs/clonetiler.h"
 #include "dialogs/iconpreview.h"
@@ -1791,6 +1792,12 @@ DialogVerb::perform(SPAction *action, void *data, void */*pdata*/)
 //              Please test the new find dialog if you have time:
 //            dt->_dlg_mgr->showDialog("Find");
             break;
+        case SP_VERB_DIALOG_FINDREPLACE:
+            // not implemented yet
+            break;
+        case SP_VERB_DIALOG_SPELLCHECK:
+            sp_spellcheck_dialog();
+            break;
         case SP_VERB_DIALOG_DEBUG:
             dt->_dlg_mgr->showDialog("Messages");
             break;
@@ -2634,6 +2641,10 @@ Verb *Verb::_base_verbs[] = {
                    N_("View and edit the XML tree of the document"), "xml_editor"),
     new DialogVerb(SP_VERB_DIALOG_FIND, "DialogFind", N_("_Find..."),
                    N_("Find objects in document"), GTK_STOCK_FIND ),
+    new DialogVerb(SP_VERB_DIALOG_FINDREPLACE, "DialogFindReplace", N_("Find and _Replace Text..."),
+                   N_("Find and replace text in document"), GTK_STOCK_FIND_AND_REPLACE ),
+    new DialogVerb(SP_VERB_DIALOG_SPELLCHECK, "DialogSpellcheck", N_("Check Spellin_g..."),
+                   N_("Check spelling of text in document"), GTK_STOCK_SPELL_CHECK ),
     new DialogVerb(SP_VERB_DIALOG_DEBUG, "DialogDebug", N_("_Messages..."),
                    N_("View debug messages"), "messages"),
     new DialogVerb(SP_VERB_DIALOG_SCRIPT, "DialogScript", N_("S_cripts..."),
index 7798f63640fec566f999bf96984e543cdb5dc31b..c3b88918b7bac4311e2639d6523f00d557b4edc9 100644 (file)
@@ -231,6 +231,8 @@ enum {
     SP_VERB_DIALOG_TEXT,
     SP_VERB_DIALOG_XML_EDITOR,
     SP_VERB_DIALOG_FIND,
+    SP_VERB_DIALOG_FINDREPLACE,
+    SP_VERB_DIALOG_SPELLCHECK,
     SP_VERB_DIALOG_DEBUG,
     SP_VERB_DIALOG_SCRIPT,
     SP_VERB_DIALOG_TOGGLE,