/* Katoob * Copyright (c) 2002,2003 Arabeyes, Mohammed Sameer. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef HAVE_CONFIG_H #include #endif #ifdef HAVE_SPELL #include "katoob.h" #include #include #include #include "katoobdocument.h" #include "spelldlg.h" #include "misc.h" #include "mdi.h" #include "spell-private.h" GtkWidget *spelldialog = NULL; GtkTreeModel *model; GtkWidget *entry; AspellSpeller *speller; KatoobDocument *doc; GtkWidget *fubar; GtkWidget *yes_img; GtkWidget *no_img; static GtkTreeModel *create_model (); static void add_suggestion (gchar * s); static void clear_suggestions (); static gboolean katoob_spelldlg_check_range (KatoobDocument * doc, GtkTextBuffer * buffer, gchar ** word, gboolean mark); static void tree_selection_changed (GtkTreeSelection * selection, gpointer data); static void build_suggestions (gchar * word); static GtkWidget *create_spelldlg (); static void spelldlg_closeb_clicked (); static void katoob_destroy_spelldlg (); static void add_cb (); static void ignore_cb (); static void change_cb (); static void check_cb (); static void get_next_word (); void katoob_create_spelldlg () { extern UI *katoob; gchar *word = NULL; doc = katoob_get_active_doc (); if (!doc) { return; } if (spelldialog) { gtk_window_present (GTK_WINDOW (spelldialog)); return; } if ((!katoob_document_get_modified (doc)) && (!katoob_document_get_file (doc))) { katoob_info (_("The document is empty.")); return; } /* First we check if there is a mispelled word or not. */ if (!katoob_spelldlg_check_range (doc, katoob_document_get_buffer (doc), &word, FALSE)) { katoob_info (_("No misspelled words.")); return; } spelldialog = create_spelldlg (word); gtk_window_set_transient_for (GTK_WINDOW (spelldialog), GTK_WINDOW (katoob->win)); gtk_entry_set_text (GTK_ENTRY (entry), word); gtk_widget_show_all (spelldialog); gtk_widget_hide (yes_img); speller = katoob_document_get_speller (doc); build_suggestions (word); g_free (word); /* NOTE: If you ommit these lines, The first mispelled word'll not be highlighted, Any ideas ?? */ katoob_spelldlg_check_range (doc, katoob_document_get_buffer (doc), &word, FALSE); g_free (word); } static GtkWidget * create_spelldlg (gchar * word) { GtkWidget *spelldlg; GtkWidget *dialog_vbox; GtkWidget *vbox; GtkWidget *table; GtkWidget *check; GtkWidget *alignment2; GtkWidget *hbox3; GtkWidget *image3; GtkWidget *label5; GtkWidget *mispelled; GtkWidget *change; GtkWidget *hbox; GtkWidget *vbox2; GtkWidget *change_b; GtkWidget *ignore; GtkWidget *add; GtkWidget *alignment1; GtkWidget *hbox2; GtkWidget *image1; GtkWidget *label1; GtkWidget *dialog_action_area; GtkWidget *closebutton; GtkWidget *img_box; GtkTreeSelection *select; GtkWidget *treeview; GtkTreeViewColumn *column; GtkCellRenderer *renderer; GtkWidget *sw; spelldlg = gtk_dialog_new (); gtk_window_set_title (GTK_WINDOW (spelldlg), _("Check Spelling")); gtk_window_set_modal (GTK_WINDOW (spelldlg), TRUE); gtk_window_set_resizable (GTK_WINDOW (spelldlg), FALSE); gtk_window_set_position (GTK_WINDOW (spelldlg), GTK_WIN_POS_CENTER); dialog_vbox = GTK_DIALOG (spelldlg)->vbox; vbox = gtk_vbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (dialog_vbox), vbox, TRUE, TRUE, 5); gtk_container_set_border_width (GTK_CONTAINER (vbox), 5); img_box = gtk_hbox_new (FALSE, 0); yes_img = gtk_image_new_from_stock ("gtk-yes", GTK_ICON_SIZE_BUTTON); no_img = gtk_image_new_from_stock ("gtk-no", GTK_ICON_SIZE_BUTTON); gtk_box_pack_start (GTK_BOX (img_box), yes_img, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (img_box), no_img, FALSE, FALSE, 0); table = gtk_table_new (2, 3, FALSE); gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0); gtk_container_set_border_width (GTK_CONTAINER (table), 5); gtk_table_set_col_spacings (GTK_TABLE (table), 5); gtk_table_attach (GTK_TABLE (table), img_box, 2, 3, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); check = gtk_button_new (); gtk_table_attach (GTK_TABLE (table), check, 2, 3, 1, 2, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); alignment2 = gtk_alignment_new (0.5, 0.5, 0, 0); gtk_container_add (GTK_CONTAINER (check), alignment2); hbox3 = gtk_hbox_new (FALSE, 2); gtk_container_add (GTK_CONTAINER (alignment2), hbox3); image3 = gtk_image_new_from_stock ("gtk-spell-check", GTK_ICON_SIZE_BUTTON); gtk_box_pack_start (GTK_BOX (hbox3), image3, FALSE, FALSE, 0); label5 = gtk_label_new_with_mnemonic (_("Check _Spelling")); gtk_box_pack_start (GTK_BOX (hbox3), label5, FALSE, FALSE, 0); gtk_label_set_justify (GTK_LABEL (label5), GTK_JUSTIFY_LEFT); mispelled = gtk_label_new (_("Mispelled:")); gtk_table_attach (GTK_TABLE (table), mispelled, 0, 1, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 5, 5); gtk_label_set_justify (GTK_LABEL (mispelled), GTK_JUSTIFY_LEFT); gtk_misc_set_alignment (GTK_MISC (mispelled), 0, 0.5); change = gtk_label_new (_("Change to:")); gtk_table_attach (GTK_TABLE (table), change, 0, 1, 1, 2, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 5, 5); gtk_label_set_justify (GTK_LABEL (change), GTK_JUSTIFY_LEFT); gtk_misc_set_alignment (GTK_MISC (change), 0, 0.5); fubar = gtk_label_new (word); gtk_table_attach (GTK_TABLE (table), fubar, 1, 2, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 5, 5); gtk_label_set_justify (GTK_LABEL (fubar), GTK_JUSTIFY_LEFT); gtk_misc_set_alignment (GTK_MISC (fubar), 0, 0.5); entry = gtk_entry_new (); gtk_table_attach (GTK_TABLE (table), entry, 1, 2, 1, 2, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE); hbox = gtk_hbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0); /* Damn tree view! */ model = create_model (); sw = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); treeview = gtk_tree_view_new_with_model (model); gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (treeview), TRUE); /* gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE); */ g_object_unref (G_OBJECT (model)); gtk_container_add (GTK_CONTAINER (sw), treeview); gtk_box_pack_start (GTK_BOX (hbox), sw, TRUE, TRUE, 0); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (_("Suggestions"), renderer, "text", 0, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); select = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)); gtk_tree_selection_set_mode (select, GTK_SELECTION_SINGLE); g_signal_connect (G_OBJECT (select), "changed", G_CALLBACK (tree_selection_changed), NULL); model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview)); /* WTH! */ /* End tree view! */ vbox2 = gtk_vbox_new (TRUE, 5); gtk_box_pack_start (GTK_BOX (hbox), vbox2, FALSE, TRUE, 0); gtk_container_set_border_width (GTK_CONTAINER (vbox2), 3); change_b = gtk_button_new_with_mnemonic (_("C_hange")); gtk_box_pack_start (GTK_BOX (vbox2), change_b, FALSE, TRUE, 0); ignore = gtk_button_new_with_mnemonic (_("_Ignore")); gtk_box_pack_start (GTK_BOX (vbox2), ignore, FALSE, TRUE, 0); add = gtk_button_new (); gtk_box_pack_start (GTK_BOX (vbox2), add, FALSE, TRUE, 0); alignment1 = gtk_alignment_new (0.5, 0.5, 0, 0); gtk_container_add (GTK_CONTAINER (add), alignment1); hbox2 = gtk_hbox_new (FALSE, 2); gtk_container_add (GTK_CONTAINER (alignment1), hbox2); image1 = gtk_image_new_from_stock ("gtk-add", GTK_ICON_SIZE_BUTTON); gtk_box_pack_start (GTK_BOX (hbox2), image1, FALSE, FALSE, 0); label1 = gtk_label_new_with_mnemonic (_("_Add to user dictionary")); gtk_box_pack_start (GTK_BOX (hbox2), label1, FALSE, FALSE, 0); gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT); dialog_action_area = GTK_DIALOG (spelldlg)->action_area; gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area), GTK_BUTTONBOX_END); closebutton = gtk_button_new_from_stock ("gtk-close"); gtk_dialog_add_action_widget (GTK_DIALOG (spelldlg), closebutton, GTK_RESPONSE_CLOSE); GTK_WIDGET_SET_FLAGS (closebutton, GTK_CAN_DEFAULT); g_signal_connect (G_OBJECT (closebutton), "clicked", G_CALLBACK (spelldlg_closeb_clicked), NULL); g_signal_connect (G_OBJECT (spelldlg), "destroy", G_CALLBACK (katoob_destroy_spelldlg), NULL); g_signal_connect (G_OBJECT (add), "clicked", G_CALLBACK (add_cb), NULL); g_signal_connect (G_OBJECT (ignore), "clicked", G_CALLBACK (ignore_cb), NULL); g_signal_connect (G_OBJECT (change_b), "clicked", G_CALLBACK (change_cb), NULL); g_signal_connect (G_OBJECT (check), "clicked", G_CALLBACK (check_cb), NULL); return spelldlg; } static void add_cb () { katoob_debug (__FUNCTION__); aspell_speller_add_to_personal (speller, gtk_entry_get_text (GTK_ENTRY (entry)), -1); get_next_word (); } static void ignore_cb () { katoob_debug (__FUNCTION__); get_next_word (); } static void change_cb () { GtkTextIter start, end; gchar *oldword, *newword; GtkTextBuffer *buffer = katoob_document_get_buffer (doc); katoob_debug (__FUNCTION__); gtk_text_buffer_get_selection_bounds (buffer, &start, &end); newword = (gchar *) gtk_entry_get_text (GTK_ENTRY (entry)); oldword = gtk_text_buffer_get_text (buffer, &start, &end, FALSE); gtk_text_buffer_delete (buffer, &start, &end); gtk_text_buffer_insert (buffer, &start, newword, -1); aspell_speller_store_replacement (katoob_document_get_speller (doc), oldword, strlen (oldword), newword, strlen (newword)); g_free (oldword); get_next_word (); /* TODO: undo/redo */ } static void check_cb () { gchar *word = (gchar *) gtk_entry_get_text (GTK_ENTRY (entry)); if (!word) { return; } clear_suggestions (); if (!aspell_speller_check (katoob_document_get_speller (doc), word, -1)) { gtk_widget_hide (yes_img); gtk_widget_show (no_img); build_suggestions (word); } else { gtk_widget_hide (no_img); gtk_widget_show (yes_img); } katoob_debug (__FUNCTION__); } static void get_next_word () { gchar *word; if (katoob_spelldlg_check_range (doc, katoob_document_get_buffer (doc), &word, TRUE)) { clear_suggestions (); gtk_entry_set_text (GTK_ENTRY (entry), word); gtk_label_set_text (GTK_LABEL (fubar), word); build_suggestions (word); g_free (word); } else { /* Destroy the dialog */ spelldlg_closeb_clicked (); katoob_info (_("No more mispelled word.")); } } static void spelldlg_closeb_clicked () { katoob_debug (__FUNCTION__); g_signal_emit_by_name (G_OBJECT (spelldialog), "destroy"); } static void katoob_destroy_spelldlg () { katoob_debug (__FUNCTION__); gtk_widget_destroyed (spelldialog, &spelldialog); } static GtkTreeModel * create_model () { GtkListStore *store; store = gtk_list_store_new (1, G_TYPE_STRING); return GTK_TREE_MODEL (store); } static void add_suggestion (gchar * s) { GtkListStore *store; GtkTreeIter iter; store = GTK_LIST_STORE (model); gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, 0, s, -1); } static void clear_suggestions () { GtkListStore *store; store = GTK_LIST_STORE (model); gtk_list_store_clear (store); } static void tree_selection_changed (GtkTreeSelection * selection, gpointer data) { GtkTreeIter iter; gchar *word = NULL; if (!gtk_tree_selection_get_selected (selection, &model, &iter)) { return; } gtk_tree_model_get (model, &iter, 0, &word, -1); gtk_entry_set_text (GTK_ENTRY (entry), word); gtk_widget_hide (no_img); gtk_widget_show (yes_img); g_free (word); } static gboolean katoob_spelldlg_check_range (KatoobDocument * doc, GtkTextBuffer * buffer, gchar ** word, gboolean mark) { GtkTextIter start, end; GtkTextIter wstart, wend; gint x, y; katoob_debug (__FUNCTION__); if (mark) { katoob_debug ("Iter from insert mark"); gtk_text_buffer_get_iter_at_mark (buffer, &start, katoob_document_get_mark (doc)); gtk_text_buffer_get_end_iter (buffer, &end); } else { katoob_debug ("iter from beginning"); gtk_text_buffer_get_bounds (buffer, &start, &end); } if (gtk_text_iter_is_end (&start)) { return FALSE; } if (!gtk_text_iter_starts_word (&start)) { if (gtk_text_iter_inside_word (&start) || gtk_text_iter_ends_word (&start)) { gtk_text_iter_backward_word_start (&start); } else { /* if we're neither at the beginning nor inside a word, * me must be in some spaces. * skip forward to the beginning of the next word. */ if (gtk_text_iter_forward_word_end (&start)) { gtk_text_iter_backward_word_start (&start); } } } wstart = start; while (gtk_text_iter_compare (&wstart, &end) < 0) { /* move wend to the end of the current word. */ wend = wstart; gtk_text_iter_forward_word_end (&wend); *word = gtk_text_buffer_get_text (buffer, &wstart, &wend, FALSE); katoob_debug (*word); if (((unsigned char) *word[0] == 0xd8) || ((unsigned char) *word[0] == 0xd9)) { /* Duali */ g_free (*word); } else { if (!aspell_speller_check (katoob_document_get_speller (doc), *word, -1)) { GtkTextIter tmp = wend; /* Move tmp to the beginning of the next word, */ gtk_text_iter_forward_word_end (&tmp); gtk_text_iter_backward_word_start (&tmp); /* If we are in the same place, then we have reached the end. */ x = gtk_text_iter_get_offset (&tmp); y = gtk_text_iter_get_offset (&wend); if (x != (y + 1)) { /* FWD. the mark to the end */ katoob_debug ("Almost finished!"); gtk_text_iter_forward_to_end (&tmp); } katoob_debug ("Found mispelled!"); gtk_text_buffer_move_mark (buffer, katoob_document_get_mark (doc), &tmp); /* gtk_text_buffer_place_cursor (buffer, &wend); gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", &wstart); */ gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", &wstart); gtk_text_buffer_move_mark_by_name (buffer, "insert", &wend); gtk_text_view_scroll_mark_onscreen (katoob_document_get_text_view (doc), gtk_text_buffer_get_insert (buffer)); if (spelldialog) { gtk_widget_hide (yes_img); gtk_widget_show (no_img); } return TRUE; } else { g_free (*word); } } /* now move wend to the beginning of the next word, */ gtk_text_iter_forward_word_end (&wend); gtk_text_iter_backward_word_start (&wend); /* make sure we've actually advanced * (we don't advance in some corner cases), */ if (gtk_text_iter_equal (&wstart, &wend)) { break; /* we're done in these cases.. */ } /* and then pick this as the new next word beginning. */ wstart = wend; } if (spelldialog) { gtk_widget_hide (no_img); gtk_widget_show (yes_img); } return FALSE; } static void build_suggestions (gchar * word) { const AspellWordList *suggestions; AspellStringEnumeration *elements; gchar *suggestion; suggestions = aspell_speller_suggest (speller, word, -1); elements = aspell_word_list_elements (suggestions); suggestion = (gchar *) aspell_string_enumeration_next (elements); if (suggestion) { gtk_entry_set_text (GTK_ENTRY (entry), suggestion); if (spelldialog) { gtk_widget_show (yes_img); gtk_widget_hide (no_img); } while (suggestion) { katoob_debug (suggestion); add_suggestion (suggestion); suggestion = (gchar *) aspell_string_enumeration_next (elements); } } else { if (spelldialog) { gtk_widget_show (no_img); gtk_widget_hide (yes_img); } } delete_aspell_string_enumeration (elements); } #endif /* HAVE_SPELL */