1 /* gtkaspell - a spell-checking addon for GtkText
2 * Copyright (c) 2000 Evan Martin (original code for ispell).
3 * Copyright (c) 2002 Melvin Hadasht.
4 * Copyright (C) 2001-2021 the Claws Mail Team
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20 * Stuphead: (C) 2000,2001 Grigroy Bakunov, Sergey Pinaev
21 * Adapted for Sylpheed (Claws) (c) 2001-2002 by Hiroyuki Yamamoto &
22 * The Claws Mail Team.
23 * Adapted for pspell (c) 2001-2002 Melvin Hadasht
24 * Adapted for GNU/aspell (c) 2002 Melvin Hadasht
29 #include "claws-features.h"
37 #include <sys/types.h>
39 # include <sys/wait.h>
50 #include <glib/gi18n.h>
54 #include <gdk/gdkkeysyms.h>
57 #include "alertpanel.h"
58 #include "gtkaspell.h"
59 #include "gtk/gtkutils.h"
60 #include "gtk/combobox.h"
62 #define ASPELL_FASTMODE 1
63 #define ASPELL_NORMALMODE 2
64 #define ASPELL_BADSPELLERMODE 3
66 /* size of the text buffer used in various word-processing routines. */
69 /* number of suggestions to display on each menu. */
73 SET_GTKASPELL_NAME
= 0,
74 SET_GTKASPELL_FULLNAME
= 1,
78 typedef struct _GtkAspellCheckers
{
80 GSList
*dictionary_list
;
84 /******************************************************************************/
86 static GtkAspellCheckers
*gtkaspellcheckers
;
88 /* Error message storage */
89 static void gtkaspell_checkers_error_message (gchar
*message
);
92 static gboolean
key_press_cb (GtkWidget
*text_view
,
94 GtkAspell
*gtkaspell
);
95 static void entry_insert_cb (GtkTextBuffer
*textbuf
,
99 GtkAspell
*gtkaspell
);
100 static void entry_delete_cb (GtkTextBuffer
*textbuf
,
101 GtkTextIter
*startiter
,
102 GtkTextIter
*enditer
,
103 GtkAspell
*gtkaspell
);
104 /*static gint button_press_intercept_cb (GtkTextView *gtktext,
106 GtkAspell *gtkaspell);
108 static void button_press_intercept_cb(GtkTextView
*gtktext
,
109 GtkMenu
*menu
, GtkAspell
*gtkaspell
);
111 /* Checker creation */
112 static GtkAspeller
* gtkaspeller_new (Dictionary
*dict
);
113 static GtkAspeller
* gtkaspeller_real_new (Dictionary
*dict
);
114 static GtkAspeller
* gtkaspeller_delete (GtkAspeller
*gtkaspeller
);
115 static GtkAspeller
* gtkaspeller_real_delete (GtkAspeller
*gtkaspeller
);
117 /* Checker configuration */
118 static EnchantDict
*set_dictionary (EnchantBroker
*broker
,
120 static void set_use_both_cb (GtkMenuItem
*w
,
121 GtkAspell
*gtkaspell
);
123 /* Checker actions */
124 static gboolean
check_at (GtkAspell
*gtkaspell
,
126 static gboolean
check_at_cb (gpointer data
);
127 static GList
* misspelled_suggest (GtkAspell
*gtkaspell
,
129 static gboolean
find_misspelled_cb (gpointer data
,
131 static void add_word_to_session_cb (GtkWidget
*w
,
133 static void add_word_to_personal_cb (GtkWidget
*w
,
135 static void replace_with_create_dialog_cb (GtkWidget
*w
,
137 static void replace_with_supplied_word_cb (GtkWidget
*w
,
138 GtkAspell
*gtkaspell
);
139 static void replace_word_cb (GtkWidget
*w
,
141 static void replace_real_word (GtkAspell
*gtkaspell
,
142 const gchar
*newword
);
143 static void replace_real_word_cb (gpointer data
,
144 const gchar
*newword
);
145 static void check_with_alternate_cb (GtkWidget
*w
,
147 static void toggle_check_while_typing_cb (GtkWidget
*w
,
151 static GSList
* make_sug_menu (GtkAspell
*gtkaspell
);
152 static GSList
* populate_submenu (GtkAspell
*gtkaspell
);
153 GSList
* gtkaspell_make_config_menu (GtkAspell
*gtkaspell
);
154 static void set_menu_pos (GtkMenu
*menu
,
159 /* Other menu callbacks */
160 static gboolean
aspell_key_pressed (GtkWidget
*widget
,
162 GtkAspell
*gtkaspell
);
163 static void change_dict_cb (GtkWidget
*w
,
164 GtkAspell
*gtkaspell
);
165 static void switch_to_alternate_cb (GtkWidget
*w
,
168 /* Misc. helper functions */
169 static void set_point_continue (GtkAspell
*gtkaspell
);
170 static void continue_check (gpointer
*gtkaspell
);
171 static gboolean
iswordsep (gunichar c
);
172 static gunichar
get_text_index_whar (GtkAspell
*gtkaspell
,
174 static gboolean
get_word_from_pos (GtkAspell
*gtkaspell
,
180 static void allocate_color (GtkAspell
*gtkaspell
,
182 static void change_color (GtkAspell
*gtkaspell
,
187 static gint
compare_dict (Dictionary
*a
,
189 static void dictionary_delete (Dictionary
*dict
);
190 static Dictionary
* dictionary_dup (const Dictionary
*dict
);
191 static void reset_theword_data (GtkAspell
*gtkaspell
);
192 static void free_checkers (gpointer elt
,
195 static void destroy_menu(GtkWidget
*widget
, gpointer user_data
);
197 /******************************************************************************/
198 static gint
get_textview_buffer_charcount(GtkTextView
*view
);
200 static void gtkaspell_free_dictionary_list (GSList
*list
);
201 static GSList
* gtkaspell_get_dictionary_list (gint refresh
);
203 static void gtkaspell_uncheck_all (GtkAspell
*gtkaspell
);
205 static gint
get_textview_buffer_charcount(GtkTextView
*view
)
207 GtkTextBuffer
*buffer
;
209 cm_return_val_if_fail(view
, 0);
211 buffer
= gtk_text_view_get_buffer(view
);
212 cm_return_val_if_fail(buffer
, 0);
214 return gtk_text_buffer_get_char_count(buffer
);
216 static gint
get_textview_buffer_offset(GtkTextView
*view
)
218 GtkTextBuffer
* buffer
;
222 cm_return_val_if_fail(view
, 0);
224 buffer
= gtk_text_view_get_buffer(view
);
225 cm_return_val_if_fail(buffer
, 0);
227 mark
= gtk_text_buffer_get_insert(buffer
);
228 cm_return_val_if_fail(mark
, 0);
230 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
232 return gtk_text_iter_get_offset(&iter
);
234 static void set_textview_buffer_offset(GtkTextView
*view
, gint offset
)
236 GtkTextBuffer
*buffer
;
239 cm_return_if_fail(view
);
241 buffer
= gtk_text_view_get_buffer(view
);
242 cm_return_if_fail(buffer
);
244 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, offset
);
245 gtk_text_buffer_place_cursor(buffer
, &iter
);
247 /******************************************************************************/
249 void gtkaspell_checkers_init(void)
251 gtkaspellcheckers
= g_new(GtkAspellCheckers
, 1);
252 gtkaspellcheckers
->checkers
= NULL
;
253 gtkaspellcheckers
->dictionary_list
= NULL
;
254 gtkaspellcheckers
->error_message
= NULL
;
257 void gtkaspell_checkers_quit(void)
262 if (gtkaspellcheckers
== NULL
)
265 if ((checkers
= gtkaspellcheckers
->checkers
)) {
266 debug_print("Aspell: number of running checkers to delete %d\n",
267 g_slist_length(checkers
));
269 g_slist_foreach(checkers
, free_checkers
, NULL
);
270 g_slist_free(checkers
);
271 gtkaspellcheckers
->checkers
= NULL
;
274 if ((dict_list
= gtkaspellcheckers
->dictionary_list
)) {
275 debug_print("Aspell: number of dictionaries to delete %d\n",
276 g_slist_length(dict_list
));
278 gtkaspell_free_dictionary_list(dict_list
);
279 gtkaspellcheckers
->dictionary_list
= NULL
;
282 g_free(gtkaspellcheckers
->error_message
);
283 gtkaspellcheckers
->error_message
= NULL
;
287 static void gtkaspell_checkers_error_message (gchar
*message
)
290 if (gtkaspellcheckers
->error_message
) {
291 tmp
= g_strdup_printf("%s\n%s",
292 gtkaspellcheckers
->error_message
,
295 g_free(gtkaspellcheckers
->error_message
);
296 gtkaspellcheckers
->error_message
= tmp
;
298 gtkaspellcheckers
->error_message
= message
;
303 const char *gtkaspell_checkers_strerror(void)
305 cm_return_val_if_fail(gtkaspellcheckers
, "");
306 return gtkaspellcheckers
->error_message
;
309 void gtkaspell_checkers_reset_error(void)
311 cm_return_if_fail(gtkaspellcheckers
);
313 g_free(gtkaspellcheckers
->error_message
);
315 gtkaspellcheckers
->error_message
= NULL
;
318 GtkAspell
*gtkaspell_new(const gchar
*dictionary
,
319 const gchar
*alt_dictionary
,
320 const gchar
*encoding
, /* unused */
321 GdkRGBA misspelled_color
,
322 gboolean check_while_typing
,
323 gboolean recheck_when_changing_dict
,
324 gboolean use_alternate
,
325 gboolean use_both_dicts
,
326 GtkTextView
*gtktext
,
327 GtkWindow
*parent_win
,
328 void (dict_changed_cb
)(void *data
),
329 void (*spell_menu_cb
)(void *data
),
333 GtkAspell
*gtkaspell
;
334 GtkAspeller
*gtkaspeller
;
335 GtkTextBuffer
*buffer
;
337 cm_return_val_if_fail(gtktext
, NULL
);
338 if (!dictionary
|| !*dictionary
) {
339 gtkaspell_checkers_error_message(
340 g_strdup(_("No dictionary selected.")));
344 buffer
= gtk_text_view_get_buffer(gtktext
);
346 dict
= g_new0(Dictionary
, 1);
347 if (strrchr(dictionary
, '/')) {
348 dict
->fullname
= g_strdup(strrchr(dictionary
, '/')+1);
349 dict
->dictname
= g_strdup(strrchr(dictionary
, '/')+1);
351 dict
->fullname
= g_strdup(dictionary
);
352 dict
->dictname
= g_strdup(dictionary
);
355 if (strchr(dict
->fullname
, '-')) {
356 *(strchr(dict
->fullname
, '-')) = '\0';
357 *(strchr(dict
->dictname
, '-')) = '\0';
359 gtkaspeller
= gtkaspeller_new(dict
);
360 dictionary_delete(dict
);
363 gtkaspell_checkers_error_message(
364 g_strdup_printf(_("Couldn't initialize %s speller."), dictionary
));
368 gtkaspell
= g_new0(GtkAspell
, 1);
370 gtkaspell
->gtkaspeller
= gtkaspeller
;
372 if (use_alternate
&& alt_dictionary
&& *alt_dictionary
) {
373 Dictionary
*alt_dict
;
374 GtkAspeller
*alt_gtkaspeller
;
376 alt_dict
= g_new0(Dictionary
, 1);
377 if (strrchr(alt_dictionary
, '/')) {
378 alt_dict
->fullname
= g_strdup(strrchr(alt_dictionary
, '/')+1);
379 alt_dict
->dictname
= g_strdup(strrchr(alt_dictionary
, '/')+1);
381 alt_dict
->fullname
= g_strdup(alt_dictionary
);
382 alt_dict
->dictname
= g_strdup(alt_dictionary
);
384 if (strchr(alt_dict
->fullname
, '-')) {
385 *(strchr(alt_dict
->fullname
, '-')) = '\0';
386 *(strchr(alt_dict
->dictname
, '-')) = '\0';
389 alt_gtkaspeller
= gtkaspeller_new(alt_dict
);
390 dictionary_delete(alt_dict
);
392 if (!alt_gtkaspeller
) {
393 gtkaspell_checkers_error_message(
394 g_strdup_printf(_("Couldn't initialize %s speller."), dictionary
));
395 gtkaspeller_delete(gtkaspeller
);
400 gtkaspell
->alternate_speller
= alt_gtkaspeller
;
402 gtkaspell
->alternate_speller
= NULL
;
405 gtkaspell
->theword
[0] = 0x00;
406 gtkaspell
->start_pos
= 0;
407 gtkaspell
->end_pos
= 0;
408 gtkaspell
->orig_pos
= -1;
409 gtkaspell
->end_check_pos
= -1;
410 gtkaspell
->misspelled
= -1;
411 gtkaspell
->check_while_typing
= check_while_typing
;
412 gtkaspell
->recheck_when_changing_dict
= recheck_when_changing_dict
;
413 gtkaspell
->continue_check
= NULL
;
414 gtkaspell
->replace_entry
= NULL
;
415 gtkaspell
->gtktext
= gtktext
;
416 gtkaspell
->max_sug
= -1;
417 gtkaspell
->suggestions_list
= NULL
;
418 gtkaspell
->use_alternate
= use_alternate
;
419 gtkaspell
->use_both_dicts
= use_both_dicts
;
420 gtkaspell
->parent_window
= GTK_WIDGET(parent_win
);
421 gtkaspell
->dict_changed_cb
= dict_changed_cb
;
422 gtkaspell
->menu_changed_cb
= spell_menu_cb
;
423 gtkaspell
->menu_changed_data
= data
;
425 allocate_color(gtkaspell
, misspelled_color
);
427 g_signal_connect(G_OBJECT(gtktext
), "key_press_event",
428 G_CALLBACK(key_press_cb
), gtkaspell
);
429 g_signal_connect_after(G_OBJECT(buffer
), "insert-text",
430 G_CALLBACK(entry_insert_cb
), gtkaspell
);
431 g_signal_connect_after(G_OBJECT(buffer
), "delete-range",
432 G_CALLBACK(entry_delete_cb
), gtkaspell
);
433 /*g_signal_connect(G_OBJECT(gtktext), "button-press-event",
434 G_CALLBACK(button_press_intercept_cb),
436 g_signal_connect(G_OBJECT(gtktext
), "populate-popup",
437 G_CALLBACK(button_press_intercept_cb
), gtkaspell
);
439 debug_print("Aspell: created gtkaspell %p\n", gtkaspell
);
444 void gtkaspell_delete(GtkAspell
*gtkaspell
)
446 GtkTextView
*gtktext
= gtkaspell
->gtktext
;
448 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext
),
449 G_CALLBACK(key_press_cb
),
451 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext
),
452 G_CALLBACK(entry_insert_cb
),
454 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext
),
455 G_CALLBACK(entry_delete_cb
),
457 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext
),
458 G_CALLBACK(button_press_intercept_cb
),
461 gtkaspell_uncheck_all(gtkaspell
);
463 gtkaspeller_delete(gtkaspell
->gtkaspeller
);
465 if (gtkaspell
->alternate_speller
)
466 gtkaspeller_delete(gtkaspell
->alternate_speller
);
468 if (gtkaspell
->suggestions_list
)
469 gtkaspell_free_suggestions_list(gtkaspell
);
471 debug_print("Aspell: deleting gtkaspell %p\n", gtkaspell
);
478 void gtkaspell_dict_changed(GtkAspell
*gtkaspell
)
480 if(!gtkaspell
|| !gtkaspell
->dict_changed_cb
||
481 !gtkaspell
->menu_changed_data
)
484 gtkaspell
->dict_changed_cb(gtkaspell
->menu_changed_data
);
487 static gboolean
key_press_cb (GtkWidget
*text_view
,
489 GtkAspell
*gtkaspell
)
493 cm_return_val_if_fail(gtkaspell
->gtkaspeller
->speller
, FALSE
);
495 if (!gtkaspell
->check_while_typing
)
498 switch (event
->keyval
) {
504 case GDK_KEY_Page_Up
:
505 case GDK_KEY_Page_Down
:
508 pos
= get_textview_buffer_offset(GTK_TEXT_VIEW(text_view
));
510 check_at(gtkaspell
, pos
- 1);
512 check_at(gtkaspell
, pos
);
521 static void entry_insert_cb(GtkTextBuffer
*textbuf
,
525 GtkAspell
*gtkaspell
)
529 cm_return_if_fail(gtkaspell
->gtkaspeller
->speller
);
531 if (!gtkaspell
->check_while_typing
)
534 pos
= gtk_text_iter_get_offset(iter
);
536 if (iswordsep(g_utf8_get_char(newtext
))) {
537 /* did we just end a word? */
539 check_at(gtkaspell
, pos
- 2);
541 /* did we just split a word? */
542 if (pos
< gtk_text_buffer_get_char_count(textbuf
))
543 check_at(gtkaspell
, pos
+ 1);
545 /* check as they type, *except* if they're typing at the end (the most
548 if (pos
< gtk_text_buffer_get_char_count(textbuf
) &&
549 !iswordsep(get_text_index_whar(gtkaspell
, pos
))) {
550 check_at(gtkaspell
, pos
- 1);
555 static void entry_delete_cb(GtkTextBuffer
*textbuf
,
556 GtkTextIter
*startiter
,
557 GtkTextIter
*enditer
,
558 GtkAspell
*gtkaspell
)
563 cm_return_if_fail(gtkaspell
->gtkaspeller
->speller
);
565 if (!gtkaspell
->check_while_typing
)
568 start
= gtk_text_iter_get_offset(startiter
);
569 origpos
= get_textview_buffer_offset(gtkaspell
->gtktext
);
571 check_at(gtkaspell
, start
- 1);
572 check_at(gtkaspell
, start
);
575 set_textview_buffer_offset(gtkaspell
->gtktext
, origpos
);
576 /* this is to *UNDO* the selection, in case they were holding shift
577 * while hitting backspace. */
578 /* needed with textview ??? */
579 /* gtk_editable_select_region(GTK_EDITABLE(gtktext), origpos, origpos); */
582 void gtkaspell_make_context_menu(GtkMenu
*menu
, GtkAspell
*gtkaspell
)
584 GtkMenuItem
*menuitem
;
585 GSList
*spell_menu
= NULL
;
587 gboolean suggest
= FALSE
;
589 if (gtkaspell
->misspelled
&&
590 misspelled_suggest(gtkaspell
, gtkaspell
->theword
)) {
591 spell_menu
= make_sug_menu(gtkaspell
);
595 spell_menu
= gtkaspell_make_config_menu(gtkaspell
);
597 menuitem
= GTK_MENU_ITEM(gtk_separator_menu_item_new());
598 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), GTK_WIDGET(menuitem
));
599 gtk_widget_show(GTK_WIDGET(menuitem
));
601 spell_menu
= g_slist_reverse(spell_menu
);
602 for (items
= spell_menu
;
603 items
; items
= items
->next
) {
604 menuitem
= GTK_MENU_ITEM(items
->data
);
605 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), GTK_WIDGET(menuitem
));
606 gtk_widget_show(GTK_WIDGET(menuitem
));
608 g_slist_free(spell_menu
);
610 g_signal_connect(G_OBJECT(menu
), "deactivate",
611 G_CALLBACK(destroy_menu
),
614 g_signal_connect(G_OBJECT(menu
),
616 G_CALLBACK(aspell_key_pressed
),
620 static void set_position_cb(gpointer data
, gint pos
)
622 GtkAspell
*gtkaspell
= (GtkAspell
*) data
;
623 set_textview_buffer_offset(gtkaspell
->gtktext
, pos
);
626 void gtkaspell_context_set(GtkAspell
*gtkaspell
)
628 gtkaspell
->ctx
.set_position
= set_position_cb
;
629 gtkaspell
->ctx
.set_menu_pos
= set_menu_pos
;
630 gtkaspell
->ctx
.find_misspelled
= find_misspelled_cb
;
631 gtkaspell
->ctx
.check_word
= check_at_cb
;
632 gtkaspell
->ctx
.replace_word
= replace_real_word_cb
;
633 gtkaspell
->ctx
.data
= (gpointer
) gtkaspell
;
636 static void button_press_intercept_cb(GtkTextView
*gtktext
,
637 GtkMenu
*menu
, GtkAspell
*gtkaspell
)
639 gtktext
= gtkaspell
->gtktext
;
640 gtkaspell
->orig_pos
= get_textview_buffer_offset(gtktext
);
641 gtkaspell
->misspelled
= check_at(gtkaspell
, gtkaspell
->orig_pos
);
643 gtkaspell_context_set(gtkaspell
);
644 gtkaspell_make_context_menu(menu
, gtkaspell
);
646 /* Checker creation */
647 static GtkAspeller
*gtkaspeller_new(Dictionary
*dictionary
)
649 GtkAspeller
*gtkaspeller
= NULL
;
652 cm_return_val_if_fail(gtkaspellcheckers
, NULL
);
654 cm_return_val_if_fail(dictionary
, NULL
);
656 if (dictionary
->dictname
== NULL
)
657 gtkaspell_checkers_error_message(
658 g_strdup(_("No dictionary selected.")));
660 cm_return_val_if_fail(dictionary
->fullname
, NULL
);
662 dict
= dictionary_dup(dictionary
);
664 if ((gtkaspeller
= gtkaspeller_real_new(dict
)) != NULL
) {
665 gtkaspellcheckers
->checkers
= g_slist_append(
666 gtkaspellcheckers
->checkers
,
669 debug_print("Aspell: Created a new gtkaspeller %p\n",
672 dictionary_delete(dict
);
674 debug_print("Aspell: Could not create spell checker.\n");
677 debug_print("Aspell: number of existing checkers %d\n",
678 g_slist_length(gtkaspellcheckers
->checkers
));
683 static GtkAspeller
*gtkaspeller_real_new(Dictionary
*dict
)
685 GtkAspeller
*gtkaspeller
;
686 EnchantBroker
*broker
;
687 EnchantDict
*speller
;
689 cm_return_val_if_fail(gtkaspellcheckers
, NULL
);
690 cm_return_val_if_fail(dict
, NULL
);
692 gtkaspeller
= g_new(GtkAspeller
, 1);
694 gtkaspeller
->dictionary
= dict
;
696 broker
= enchant_broker_init();
699 gtkaspell_checkers_error_message(
700 g_strdup(_("Couldn't initialize Enchant broker.")));
704 if ((speller
= set_dictionary(broker
, dict
)) == NULL
) {
705 gtkaspell_checkers_error_message(
706 g_strdup_printf(_("Couldn't initialize %s dictionary:"), dict
->fullname
));
707 gtkaspell_checkers_error_message(
708 g_strdup(enchant_broker_get_error(broker
)));
712 gtkaspeller
->speller
= speller
;
713 gtkaspeller
->broker
= broker
;
718 static GtkAspeller
*gtkaspeller_delete(GtkAspeller
*gtkaspeller
)
720 cm_return_val_if_fail(gtkaspellcheckers
, NULL
);
722 gtkaspellcheckers
->checkers
=
723 g_slist_remove(gtkaspellcheckers
->checkers
,
726 debug_print("Aspell: Deleting gtkaspeller %p.\n",
729 gtkaspeller_real_delete(gtkaspeller
);
731 debug_print("Aspell: number of existing checkers %d\n",
732 g_slist_length(gtkaspellcheckers
->checkers
));
737 static GtkAspeller
*gtkaspeller_real_delete(GtkAspeller
*gtkaspeller
)
739 cm_return_val_if_fail(gtkaspeller
, NULL
);
740 cm_return_val_if_fail(gtkaspeller
->speller
, NULL
);
742 enchant_broker_free_dict(gtkaspeller
->broker
, gtkaspeller
->speller
);
743 enchant_broker_free(gtkaspeller
->broker
);
745 dictionary_delete(gtkaspeller
->dictionary
);
747 debug_print("Aspell: gtkaspeller %p deleted.\n",
755 /*****************************************************************************/
756 /* Checker configuration */
758 static EnchantDict
*set_dictionary(EnchantBroker
*broker
, Dictionary
*dict
)
760 cm_return_val_if_fail(broker
, FALSE
);
761 cm_return_val_if_fail(dict
, FALSE
);
763 return enchant_broker_request_dict(broker
, dict
->dictname
);
766 static void set_use_both_cb(GtkMenuItem
*w
, GtkAspell
*gtkaspell
)
768 gtkaspell
->use_both_dicts
= gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w
));
769 gtkaspell_dict_changed(gtkaspell
);
771 if (gtkaspell
->menu_changed_cb
)
772 gtkaspell
->menu_changed_cb(gtkaspell
->menu_changed_data
);
775 /* misspelled_suggest() - Create a suggestion list for word */
776 static GList
*misspelled_suggest(GtkAspell
*gtkaspell
, gchar
*word
)
781 cm_return_val_if_fail(word
, NULL
);
786 gtkaspell_free_suggestions_list(gtkaspell
);
788 suggestions
= enchant_dict_suggest(gtkaspell
->gtkaspeller
->speller
, word
, strlen(word
), &num_sug
);
789 list
= g_list_append(list
, g_strdup(word
));
790 if (suggestions
== NULL
|| num_sug
== 0) {
791 gtkaspell
->max_sug
= -1;
792 gtkaspell
->suggestions_list
= list
;
795 for (i
= 0; i
< num_sug
; i
++)
796 list
= g_list_append(list
, g_strdup((gchar
*)suggestions
[i
]));
798 gtkaspell
->max_sug
= num_sug
- 1;
799 gtkaspell
->suggestions_list
= list
;
800 enchant_dict_free_string_list(gtkaspell
->gtkaspeller
->speller
, suggestions
);
804 /* misspelled_test() - Just test if word is correctly spelled */
805 int gtkaspell_misspelled_test(GtkAspell
*gtkaspell
, char *word
)
808 cm_return_val_if_fail(word
, 0);
813 result
= enchant_dict_check(gtkaspell
->gtkaspeller
->speller
, word
, strlen(word
));
815 if (result
&& gtkaspell
->use_both_dicts
&& gtkaspell
->alternate_speller
) {
816 gtkaspell_use_alternate_dict(gtkaspell
);
817 result
= enchant_dict_check(gtkaspell
->gtkaspeller
->speller
, word
, strlen(word
));
818 gtkaspell_use_alternate_dict(gtkaspell
);
820 return (result
&& strcasecmp(word
, "sylpheed") &&
821 strcasecmp(word
, "claws-mail"));
825 static gboolean
iswordsep(gunichar c
)
827 return (g_unichar_isspace(c
) || g_unichar_ispunct(c
)) && c
!= (gunichar
)'\'';
830 static gunichar
get_text_index_whar(GtkAspell
*gtkaspell
, int pos
)
832 GtkTextView
*view
= gtkaspell
->gtktext
;
833 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(view
);
834 GtkTextIter start
, end
;
838 gtk_text_buffer_get_iter_at_offset(buffer
, &start
, pos
);
839 gtk_text_buffer_get_iter_at_offset(buffer
, &end
, pos
+1);
841 utf8chars
= gtk_text_iter_get_text(&start
, &end
);
842 a
= g_utf8_get_char(utf8chars
);
848 /* get_word_from_pos () - return the word pointed to. */
849 /* Handles correctly the quotes. */
850 static gboolean
get_word_from_pos(GtkAspell
*gtkaspell
, gint pos
,
851 char* buf
, gint buflen
,
852 gint
*pstart
, gint
*pend
)
855 /* TODO : when correcting a word into quotes, change the color of */
856 /* the quotes too, as may be they were highlighted before. To do */
857 /* this, we can use two others pointers that points to the whole */
858 /* word including quotes. */
864 GtkTextView
*gtktext
;
866 gtktext
= gtkaspell
->gtktext
;
867 if (iswordsep(get_text_index_whar(gtkaspell
, pos
)))
870 /* The apostrophe character is somtimes used for quotes
871 * So include it in the word only if it is not surrounded
872 * by other characters.
875 for (start
= pos
; start
>= 0; --start
) {
876 c
= get_text_index_whar(gtkaspell
, start
);
877 if (c
== (gunichar
)'\'') {
879 if (g_unichar_isspace(get_text_index_whar(gtkaspell
,
881 || g_unichar_ispunct(get_text_index_whar(gtkaspell
,
883 || g_unichar_isdigit(get_text_index_whar(gtkaspell
,
885 /* start_quote = TRUE; */
890 /* start_quote = TRUE; */
894 else if (g_unichar_isspace(c
) || g_unichar_ispunct(c
) || g_unichar_isdigit(c
))
900 for (end
= pos
; end
< get_textview_buffer_charcount(gtktext
); end
++) {
901 c
= get_text_index_whar(gtkaspell
, end
);
902 if (c
== (gunichar
)'\'') {
903 if (end
< get_textview_buffer_charcount(gtktext
)) {
904 if (g_unichar_isspace(get_text_index_whar(gtkaspell
,
906 || g_unichar_ispunct(get_text_index_whar(gtkaspell
,
908 || g_unichar_isdigit(get_text_index_whar(gtkaspell
,
910 /* end_quote = TRUE; */
915 /* end_quote = TRUE; */
919 else if (g_unichar_isspace(c
) || g_unichar_ispunct(c
) || g_unichar_isdigit(c
))
929 if (end
- start
< buflen
) {
930 GtkTextIter iterstart
, iterend
;
932 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(gtktext
);
933 gtk_text_buffer_get_iter_at_offset(buffer
, &iterstart
, start
);
934 gtk_text_buffer_get_iter_at_offset(buffer
, &iterend
, end
);
935 tmp
= gtk_text_buffer_get_text(buffer
, &iterstart
, &iterend
, FALSE
);
936 strncpy(buf
, tmp
, buflen
-1);
946 static gboolean
check_at(GtkAspell
*gtkaspell
, gint from_pos
)
949 char buf
[GTKASPELLWORDSIZE
];
951 cm_return_val_if_fail(from_pos
>= 0, FALSE
);
953 if (!get_word_from_pos(gtkaspell
, from_pos
, buf
, sizeof(buf
),
957 if (gtkaspell_misspelled_test(gtkaspell
, buf
)) {
958 strncpy(gtkaspell
->theword
, (gchar
*)buf
, GTKASPELLWORDSIZE
- 1);
959 gtkaspell
->theword
[GTKASPELLWORDSIZE
- 1] = 0;
960 gtkaspell
->start_pos
= start
;
961 gtkaspell
->end_pos
= end
;
962 gtkaspell_free_suggestions_list(gtkaspell
);
964 change_color(gtkaspell
, start
, end
, (gchar
*)buf
, TRUE
);
967 change_color(gtkaspell
, start
, end
, (gchar
*)buf
, FALSE
);
972 static gboolean
check_at_cb(gpointer data
)
974 GtkAspell
*gtkaspell
= (GtkAspell
*)data
;
975 return check_at(gtkaspell
, gtkaspell
->start_pos
);
978 static gboolean
find_misspelled_cb(gpointer data
, gboolean forward
)
980 GtkAspell
*gtkaspell
= (GtkAspell
*)data
;
988 maxpos
= gtkaspell
->end_check_pos
;
996 pos
= get_textview_buffer_offset(gtkaspell
->gtktext
);
997 gtkaspell
->orig_pos
= pos
;
998 while (iswordsep(get_text_index_whar(gtkaspell
, pos
)) &&
999 pos
> minpos
&& pos
<= maxpos
)
1001 while (!(misspelled
= check_at(gtkaspell
, pos
)) &&
1002 pos
> minpos
&& pos
<= maxpos
) {
1004 while (!iswordsep(get_text_index_whar(gtkaspell
, pos
)) &&
1005 pos
> minpos
&& pos
<= maxpos
)
1008 while (iswordsep(get_text_index_whar(gtkaspell
, pos
)) &&
1009 pos
> minpos
&& pos
<= maxpos
)
1016 gboolean
gtkaspell_check_next_prev(GtkAspell
*gtkaspell
, gboolean forward
)
1018 gboolean misspelled
= gtkaspell
->ctx
.find_misspelled(gtkaspell
->ctx
.data
,
1023 misspelled_suggest(gtkaspell
, gtkaspell
->theword
);
1026 gtkaspell
->orig_pos
= gtkaspell
->end_pos
;
1028 gtkaspell
->ctx
.set_position(gtkaspell
->ctx
.data
, gtkaspell
->end_pos
);
1030 /* only execute when in textview context */
1031 if (gtkaspell
== (GtkAspell
*)gtkaspell
->ctx
.data
) {
1032 /* scroll line to window center */
1033 gtk_text_view_scroll_to_mark(gtkaspell
->gtktext
,
1034 gtk_text_buffer_get_insert(
1035 gtk_text_view_get_buffer(gtkaspell
->gtktext
)),
1036 0.0, TRUE
, 0.0, 0.5);
1037 /* let textview recalculate coordinates (set_menu_pos) */
1038 while (gtk_events_pending ())
1039 gtk_main_iteration ();
1042 list
= make_sug_menu(gtkaspell
);
1043 menu
= gtk_menu_new();
1044 for (cur
= list
; cur
; cur
= cur
->next
)
1045 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), GTK_WIDGET(cur
->data
));
1047 gtk_menu_popup_at_pointer(GTK_MENU(menu
), NULL
);
1048 g_signal_connect(G_OBJECT(menu
), "deactivate",
1049 G_CALLBACK(destroy_menu
),
1051 g_signal_connect(G_OBJECT(menu
),
1053 G_CALLBACK(aspell_key_pressed
),
1058 reset_theword_data(gtkaspell
);
1060 alertpanel_notice(_("No misspelled word found."));
1061 gtkaspell
->ctx
.set_position(gtkaspell
->ctx
.data
,
1062 gtkaspell
->orig_pos
);
1068 void gtkaspell_check_backwards(GtkAspell
*gtkaspell
)
1070 gtkaspell
->continue_check
= NULL
;
1071 gtkaspell
->end_check_pos
=
1072 get_textview_buffer_charcount(gtkaspell
->gtktext
);
1073 gtkaspell_context_set(gtkaspell
);
1074 gtkaspell_check_next_prev(gtkaspell
, FALSE
);
1077 void gtkaspell_check_forwards_go(GtkAspell
*gtkaspell
)
1080 gtkaspell
->continue_check
= NULL
;
1081 gtkaspell
->end_check_pos
=
1082 get_textview_buffer_charcount(gtkaspell
->gtktext
);
1083 gtkaspell_context_set(gtkaspell
);
1084 gtkaspell_check_next_prev(gtkaspell
, TRUE
);
1087 void gtkaspell_check_all(GtkAspell
*gtkaspell
)
1089 GtkTextView
*gtktext
;
1091 GtkTextBuffer
*buffer
;
1092 GtkTextIter startiter
, enditer
;
1094 cm_return_if_fail(gtkaspell
);
1095 cm_return_if_fail(gtkaspell
->gtktext
);
1097 gtktext
= gtkaspell
->gtktext
;
1098 buffer
= gtk_text_view_get_buffer(gtktext
);
1099 gtk_text_buffer_get_selection_bounds(buffer
, &startiter
, &enditer
);
1100 start
= gtk_text_iter_get_offset(&startiter
);
1101 end
= gtk_text_iter_get_offset(&enditer
);
1105 end
= gtk_text_buffer_get_char_count(buffer
);
1106 } else if (start
> end
) {
1114 set_textview_buffer_offset(gtktext
, start
);
1116 gtkaspell
->continue_check
= continue_check
;
1117 gtkaspell
->end_check_pos
= end
;
1119 gtkaspell_context_set(gtkaspell
);
1120 gtkaspell
->misspelled
= gtkaspell_check_next_prev(gtkaspell
, TRUE
);
1123 static void continue_check(gpointer
*data
)
1125 GtkAspell
*gtkaspell
= (GtkAspell
*) data
;
1126 gint pos
= get_textview_buffer_offset(gtkaspell
->gtktext
);
1127 if (pos
< gtkaspell
->end_check_pos
&& gtkaspell
->misspelled
)
1128 gtkaspell
->misspelled
= gtkaspell_check_next_prev(gtkaspell
, TRUE
);
1130 gtkaspell
->continue_check
= NULL
;
1133 void gtkaspell_highlight_all(GtkAspell
*gtkaspell
)
1138 GtkTextView
*gtktext
;
1140 cm_return_if_fail(gtkaspell
->gtkaspeller
->speller
);
1142 gtktext
= gtkaspell
->gtktext
;
1144 len
= get_textview_buffer_charcount(gtktext
);
1146 origpos
= get_textview_buffer_offset(gtktext
);
1150 iswordsep(get_text_index_whar(gtkaspell
, pos
)))
1153 !iswordsep(get_text_index_whar(gtkaspell
, pos
)))
1156 check_at(gtkaspell
, pos
- 1);
1158 set_textview_buffer_offset(gtktext
, origpos
);
1161 static void replace_with_supplied_word_cb(GtkWidget
*w
, GtkAspell
*gtkaspell
)
1164 GdkEvent
*e
= (GdkEvent
*) gtk_get_current_event();
1166 newword
= gtk_editable_get_chars(GTK_EDITABLE(gtkaspell
->replace_entry
),
1169 if (strcmp(newword
, gtkaspell
->theword
)) {
1170 gtkaspell
->ctx
.replace_word(gtkaspell
->ctx
.data
, newword
);
1172 if ((e
->type
== GDK_KEY_PRESS
&&
1173 ((GdkEventKey
*) e
)->state
& GDK_CONTROL_MASK
)) {
1174 enchant_dict_store_replacement(gtkaspell
->gtkaspeller
->speller
,
1175 gtkaspell
->theword
, strlen(gtkaspell
->theword
),
1176 newword
, strlen(newword
));
1178 gtkaspell
->replace_entry
= NULL
;
1183 if (w
&& GTK_IS_DIALOG(w
)) {
1184 gtk_widget_destroy(w
);
1187 set_point_continue(gtkaspell
);
1191 static void replace_word_cb(GtkWidget
*w
, gpointer data
)
1193 const char *newword
;
1194 GtkAspell
*gtkaspell
= (GtkAspell
*) data
;
1195 GdkEvent
*e
= (GdkEvent
*) gtk_get_current_event();
1197 newword
= gtk_label_get_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN((w
)))));
1199 gtkaspell
->ctx
.replace_word(gtkaspell
->ctx
.data
, newword
);
1201 if ((e
->type
== GDK_KEY_PRESS
&&
1202 ((GdkEventKey
*) e
)->state
& GDK_CONTROL_MASK
) ||
1203 (e
->type
== GDK_BUTTON_RELEASE
&&
1204 ((GdkEventButton
*) e
)->state
& GDK_CONTROL_MASK
)) {
1205 enchant_dict_store_replacement(gtkaspell
->gtkaspeller
->speller
,
1206 gtkaspell
->theword
, strlen(gtkaspell
->theword
),
1207 newword
, strlen(newword
));
1210 gtk_menu_shell_deactivate(GTK_MENU_SHELL(gtk_widget_get_parent(w
)));
1212 set_point_continue(gtkaspell
);
1215 void gtkaspell_block_check(GtkAspell
*gtkaspell
)
1217 GtkTextView
*gtktext
;
1219 if (gtkaspell
== NULL
)
1222 gtktext
= gtkaspell
->gtktext
;
1223 g_signal_handlers_block_by_func(G_OBJECT(gtktext
),
1224 G_CALLBACK(key_press_cb
),
1226 g_signal_handlers_block_by_func(G_OBJECT(gtktext
),
1227 G_CALLBACK(entry_insert_cb
),
1229 g_signal_handlers_block_by_func(G_OBJECT(gtktext
),
1230 G_CALLBACK(entry_delete_cb
),
1234 void gtkaspell_unblock_check(GtkAspell
*gtkaspell
)
1236 GtkTextView
*gtktext
;
1238 if (gtkaspell
== NULL
)
1241 gtktext
= gtkaspell
->gtktext
;
1242 g_signal_handlers_unblock_by_func(G_OBJECT(gtktext
),
1243 G_CALLBACK(key_press_cb
),
1245 g_signal_handlers_unblock_by_func(G_OBJECT(gtktext
),
1246 G_CALLBACK(entry_insert_cb
),
1248 g_signal_handlers_unblock_by_func(G_OBJECT(gtktext
),
1249 G_CALLBACK(entry_delete_cb
),
1253 static void replace_real_word(GtkAspell
*gtkaspell
, const gchar
*newword
)
1258 GtkTextView
*gtktext
;
1259 GtkTextBuffer
*textbuf
;
1260 GtkTextIter startiter
, enditer
;
1262 if (!newword
) return;
1264 gtktext
= gtkaspell
->gtktext
;
1265 textbuf
= gtk_text_view_get_buffer(gtktext
);
1267 origpos
= gtkaspell
->orig_pos
;
1269 oldlen
= gtkaspell
->end_pos
- gtkaspell
->start_pos
;
1271 newlen
= strlen(newword
); /* FIXME: multybyte characters? */
1273 gtkaspell_block_check(gtkaspell
);
1275 gtk_text_buffer_get_iter_at_offset(textbuf
, &startiter
,
1276 gtkaspell
->start_pos
);
1277 gtk_text_buffer_get_iter_at_offset(textbuf
, &enditer
,
1278 gtkaspell
->end_pos
);
1279 g_signal_emit_by_name(G_OBJECT(textbuf
), "delete-range",
1280 &startiter
, &enditer
, gtkaspell
);
1281 g_signal_emit_by_name(G_OBJECT(textbuf
), "insert-text",
1282 &startiter
, newword
, newlen
, gtkaspell
);
1284 gtkaspell_unblock_check(gtkaspell
);
1286 /* Put the point and the position where we clicked with the mouse
1287 * It seems to be a hack, as I must thaw,freeze,thaw the widget
1288 * to let it update correctly the word insertion and then the
1289 * point & position position. If not, SEGV after the first replacement
1290 * If the new word ends before point, put the point at its end.
1293 if (origpos
- gtkaspell
->start_pos
< oldlen
&&
1294 origpos
- gtkaspell
->start_pos
>= 0) {
1295 /* Original point was in the word.
1296 * Let it there unless point is going to be outside of the word
1298 if (origpos
- gtkaspell
->start_pos
>= newlen
) {
1299 pos
= gtkaspell
->start_pos
+ newlen
;
1302 else if (origpos
>= gtkaspell
->end_pos
) {
1303 /* move the position according to the change of length */
1304 pos
= origpos
+ newlen
- oldlen
;
1307 gtkaspell
->end_pos
= gtkaspell
->start_pos
+ strlen(newword
); /* FIXME: multibyte characters? */
1309 if (get_textview_buffer_charcount(gtktext
) < pos
)
1310 pos
= get_textview_buffer_charcount(gtktext
);
1311 gtkaspell
->orig_pos
= pos
;
1313 set_textview_buffer_offset(gtktext
, gtkaspell
->orig_pos
);
1316 static void replace_real_word_cb(gpointer data
, const gchar
*newword
)
1318 replace_real_word((GtkAspell
*)data
, newword
);
1321 /* Accept this word for this session */
1322 static void add_word_to_session_cb(GtkWidget
*w
, gpointer data
)
1324 GtkAspell
*gtkaspell
= (GtkAspell
*) data
;
1326 enchant_dict_add_to_session(gtkaspell
->gtkaspeller
->speller
, gtkaspell
->theword
, strlen(gtkaspell
->theword
));
1328 gtkaspell
->ctx
.check_word(gtkaspell
->ctx
.data
);
1329 gtkaspell_dict_changed(gtkaspell
);
1331 gtk_menu_shell_deactivate(GTK_MENU_SHELL(gtk_widget_get_parent(w
)));
1333 set_point_continue(gtkaspell
);
1336 /* add_word_to_personal_cb() - add word to personal dict. */
1337 static void add_word_to_personal_cb(GtkWidget
*w
, gpointer data
)
1339 GtkAspell
*gtkaspell
= (GtkAspell
*) data
;
1341 enchant_dict_add(gtkaspell
->gtkaspeller
->speller
, gtkaspell
->theword
, strlen(gtkaspell
->theword
));
1343 gtkaspell
->ctx
.check_word(gtkaspell
->ctx
.data
);
1344 gtkaspell_dict_changed(gtkaspell
);
1346 gtk_menu_shell_deactivate(GTK_MENU_SHELL(gtk_widget_get_parent(w
)));
1347 set_point_continue(gtkaspell
);
1350 static void check_with_alternate_cb(GtkWidget
*w
, gpointer data
)
1352 GtkAspell
*gtkaspell
= (GtkAspell
*)data
;
1355 gtk_menu_shell_deactivate(GTK_MENU_SHELL(gtk_widget_get_parent(w
)));
1357 gtkaspell_use_alternate_dict(gtkaspell
);
1358 misspelled
= gtkaspell
->ctx
.check_word(gtkaspell
->ctx
.data
);
1360 if (!gtkaspell
->continue_check
) {
1362 gtkaspell
->misspelled
= misspelled
;
1364 if (gtkaspell
->misspelled
) {
1367 misspelled_suggest(gtkaspell
, gtkaspell
->theword
);
1369 gtkaspell
->ctx
.set_position(gtkaspell
->ctx
.data
,
1370 gtkaspell
->end_pos
);
1372 list
= make_sug_menu(gtkaspell
);
1373 menu
= gtk_menu_new();
1374 for (cur
= list
; cur
; cur
= cur
->next
)
1375 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), GTK_WIDGET(cur
->data
));
1377 gtk_menu_popup_at_pointer(GTK_MENU(menu
), NULL
);
1378 g_signal_connect(G_OBJECT(menu
), "deactivate",
1379 G_CALLBACK(destroy_menu
),
1381 g_signal_connect(G_OBJECT(menu
),
1383 G_CALLBACK(aspell_key_pressed
),
1388 gtkaspell
->orig_pos
= gtkaspell
->start_pos
;
1390 set_point_continue(gtkaspell
);
1393 static gboolean
replace_key_pressed(GtkWidget
*widget
,
1395 GtkAspell
*gtkaspell
)
1397 if (event
&& event
->keyval
== GDK_KEY_Escape
) {
1398 gtk_widget_destroy(widget
);
1400 } else if (event
&& (event
->keyval
== GDK_KEY_KP_Enter
||
1401 event
->keyval
== GDK_KEY_Return
)) {
1402 replace_with_supplied_word_cb(widget
, gtkaspell
);
1408 static void replace_with_create_dialog_cb(GtkWidget
*w
, gpointer data
)
1410 static PangoFontDescription
*font_desc
;
1416 GtkWidget
*ok_button
;
1417 GtkWidget
*cancel_button
;
1419 GtkWidget
*parent_window
;
1420 GtkWidget
*content_area
;
1421 gchar
*utf8buf
, *thelabel
, *format
;
1423 GtkAspell
*gtkaspell
= (GtkAspell
*) data
;
1425 cm_return_if_fail(w
!= NULL
);
1426 parent_window
= gtk_widget_get_parent(w
);
1427 cm_return_if_fail(parent_window
!= NULL
);
1428 gdk_window_get_origin(gtk_widget_get_window(parent_window
), &xx
, &yy
);
1430 gtk_menu_shell_deactivate(GTK_MENU_SHELL(gtk_widget_get_parent(w
)));
1432 dialog
= gtk_dialog_new();
1433 content_area
= gtk_dialog_get_content_area(GTK_DIALOG(dialog
));
1435 gtk_window_set_resizable(GTK_WINDOW(dialog
), FALSE
);
1436 gtk_window_set_title(GTK_WINDOW(dialog
),_("Replace unknown word"));
1437 gtk_window_move(GTK_WINDOW(dialog
), xx
, yy
);
1439 g_signal_connect_swapped(G_OBJECT(dialog
), "destroy",
1440 G_CALLBACK(gtk_widget_destroy
),
1443 gtk_box_set_spacing (GTK_BOX (content_area
), 14);
1444 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 12);
1445 gtk_container_set_border_width (GTK_CONTAINER (hbox
), 5);
1446 gtk_widget_show (hbox
);
1447 gtk_box_pack_start (GTK_BOX (content_area
), hbox
, FALSE
, FALSE
, 0);
1449 utf8buf
= g_strdup(gtkaspell
->theword
);
1451 format
= g_strconcat("<span weight=\"bold\" size=\"larger\">",
1452 _("Replace \"%s\" with: "), "</span>", NULL
);
1453 thelabel
= g_strdup_printf(format
, utf8buf
);
1456 icon
= gtk_image_new_from_icon_name("dialog-question",
1457 GTK_ICON_SIZE_DIALOG
);
1458 gtk_widget_set_halign(icon
, GTK_ALIGN_CENTER
);
1459 gtk_widget_set_valign(icon
, GTK_ALIGN_START
);
1460 gtk_box_pack_start (GTK_BOX (hbox
), icon
, FALSE
, FALSE
, 0);
1462 vbox
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, 12);
1463 gtk_box_pack_start (GTK_BOX (hbox
), vbox
, TRUE
, TRUE
, 0);
1464 gtk_widget_show (vbox
);
1466 label
= gtk_label_new(thelabel
);
1467 gtk_label_set_xalign(GTK_LABEL(label
), 0.0);
1468 gtk_label_set_justify(GTK_LABEL(label
), GTK_JUSTIFY_LEFT
);
1469 gtk_label_set_use_markup (GTK_LABEL (label
), TRUE
);
1470 gtk_box_pack_start(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
1471 gtk_label_set_line_wrap(GTK_LABEL(label
), TRUE
);
1475 size
= pango_font_description_get_size
1476 (gtk_widget_get_style(label
)->font_desc
);
1477 font_desc
= pango_font_description_new();
1478 pango_font_description_set_weight
1479 (font_desc
, PANGO_WEIGHT_BOLD
);
1480 pango_font_description_set_size
1481 (font_desc
, size
* PANGO_SCALE_LARGE
);
1484 gtk_widget_override_font(label
, font_desc
);
1487 entry
= gtk_entry_new();
1488 gtkaspell
->replace_entry
= entry
;
1489 gtk_entry_set_text(GTK_ENTRY(entry
), utf8buf
);
1490 gtk_editable_select_region(GTK_EDITABLE(entry
), 0, -1);
1491 g_signal_connect(G_OBJECT(dialog
),
1493 G_CALLBACK(replace_key_pressed
), gtkaspell
);
1494 gtk_box_pack_start(GTK_BOX(vbox
), entry
, FALSE
, FALSE
, 0);
1497 label
= gtk_label_new(_("Holding down Control key while pressing "
1498 "Enter\nwill learn from mistake.\n"));
1499 gtk_label_set_xalign(GTK_LABEL(label
), 0.0);
1500 gtk_label_set_justify(GTK_LABEL(label
), GTK_JUSTIFY_LEFT
);
1501 gtk_box_pack_start(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
1502 gtk_widget_show(label
);
1504 cancel_button
= gtk_dialog_add_button(GTK_DIALOG(dialog
), _("_Cancel"),
1506 ok_button
= gtk_dialog_add_button(GTK_DIALOG(dialog
),_("_OK"),
1508 g_signal_connect(G_OBJECT(ok_button
), "clicked",
1509 G_CALLBACK(replace_with_supplied_word_cb
),
1511 g_signal_connect_swapped(G_OBJECT(ok_button
), "clicked",
1512 G_CALLBACK(gtk_widget_destroy
),
1515 g_signal_connect_swapped(G_OBJECT(cancel_button
), "clicked",
1516 G_CALLBACK(gtk_widget_destroy
),
1519 gtk_widget_grab_focus(entry
);
1521 gtk_window_set_modal(GTK_WINDOW(dialog
), TRUE
);
1523 gtk_widget_show_all(dialog
);
1526 static void gtkaspell_uncheck_all(GtkAspell
* gtkaspell
)
1528 GtkTextView
*gtktext
;
1529 GtkTextBuffer
*buffer
;
1530 GtkTextIter startiter
, enditer
;
1532 gtktext
= gtkaspell
->gtktext
;
1534 buffer
= gtk_text_view_get_buffer(gtktext
);
1535 gtk_text_buffer_get_iter_at_offset(buffer
, &startiter
, 0);
1536 gtk_text_buffer_get_iter_at_offset(buffer
, &enditer
,
1537 get_textview_buffer_charcount(gtktext
)-1);
1538 gtk_text_buffer_remove_tag_by_name(buffer
, "misspelled",
1539 &startiter
, &enditer
);
1542 static void toggle_check_while_typing_cb(GtkWidget
*w
, gpointer data
)
1544 GtkAspell
*gtkaspell
= (GtkAspell
*) data
;
1546 gtkaspell
->check_while_typing
= gtkaspell
->check_while_typing
== FALSE
;
1548 if (!gtkaspell
->check_while_typing
)
1549 gtkaspell_uncheck_all(gtkaspell
);
1550 if (gtkaspell
->menu_changed_cb
)
1551 gtkaspell
->menu_changed_cb(gtkaspell
->menu_changed_data
);
1554 static GSList
*create_empty_dictionary_list(void)
1556 GSList
*list
= NULL
;
1559 dict
= g_new0(Dictionary
, 1);
1560 dict
->fullname
= g_strdup(_("None"));
1561 dict
->dictname
= NULL
;
1563 return g_slist_append(list
, dict
);
1566 static void list_dict_cb(const char * const lang_tag
,
1567 const char * const provider_name
,
1568 const char * const provider_desc
,
1569 const char * const provider_file
,
1572 GSList
**list
= (GSList
**)data
;
1573 Dictionary
*dict
= g_new0(Dictionary
, 1);
1574 dict
->fullname
= g_strdup(lang_tag
);
1575 dict
->dictname
= g_strdup(lang_tag
);
1577 if (g_slist_find_custom(*list
, dict
,
1578 (GCompareFunc
) compare_dict
) == NULL
) {
1579 debug_print("Aspell: found dictionary %s %s\n", dict
->fullname
,
1581 *list
= g_slist_insert_sorted(*list
, dict
,
1582 (GCompareFunc
) compare_dict
);
1584 dictionary_delete(dict
);
1588 /* gtkaspell_get_dictionary_list() - returns list of dictionary names */
1589 static GSList
*gtkaspell_get_dictionary_list(gint refresh
)
1592 EnchantBroker
*broker
;
1594 if (!gtkaspellcheckers
)
1595 gtkaspell_checkers_init();
1597 if (gtkaspellcheckers
->dictionary_list
&& !refresh
)
1598 return gtkaspellcheckers
->dictionary_list
;
1600 gtkaspell_free_dictionary_list(
1601 gtkaspellcheckers
->dictionary_list
);
1604 broker
= enchant_broker_init();
1606 enchant_broker_list_dicts(broker
, list_dict_cb
, &list
);
1608 enchant_broker_free(broker
);
1612 debug_print("Aspell: error when searching for dictionaries: "
1613 "No dictionary found.\n");
1614 list
= create_empty_dictionary_list();
1617 gtkaspellcheckers
->dictionary_list
= list
;
1622 static void gtkaspell_free_dictionary_list(GSList
*list
)
1626 for (walk
= list
; walk
!= NULL
; walk
= g_slist_next(walk
))
1628 dict
= (Dictionary
*) walk
->data
;
1629 dictionary_delete(dict
);
1634 GtkTreeModel
*gtkaspell_dictionary_store_new_with_refresh(gboolean refresh
)
1636 GSList
*dict_list
, *tmp
;
1637 GtkListStore
*store
;
1641 dict_list
= gtkaspell_get_dictionary_list(refresh
);
1642 cm_return_val_if_fail(dict_list
, NULL
);
1644 store
= gtk_list_store_new(SET_GTKASPELL_SIZE
,
1649 for (tmp
= dict_list
; tmp
!= NULL
; tmp
= g_slist_next(tmp
)) {
1650 dict
= (Dictionary
*) tmp
->data
;
1652 gtk_list_store_append(store
, &iter
);
1653 gtk_list_store_set(store
, &iter
,
1654 SET_GTKASPELL_NAME
, dict
->dictname
,
1655 SET_GTKASPELL_FULLNAME
, dict
->fullname
,
1659 return GTK_TREE_MODEL(store
);
1662 GtkTreeModel
*gtkaspell_dictionary_store_new(void)
1664 return gtkaspell_dictionary_store_new_with_refresh
1668 GtkWidget
*gtkaspell_dictionary_combo_new(const gboolean refresh
)
1671 GtkCellRenderer
*renderer
;
1673 combo
= gtk_combo_box_new_with_model(
1674 gtkaspell_dictionary_store_new_with_refresh(refresh
));
1675 gtk_combo_box_set_active(GTK_COMBO_BOX(combo
), 0);
1676 gtk_widget_show(combo
);
1678 renderer
= gtk_cell_renderer_text_new();
1679 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo
), renderer
, TRUE
);
1680 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo
),renderer
,
1681 "text", SET_GTKASPELL_NAME
, NULL
);
1686 gchar
*gtkaspell_get_dictionary_menu_active_item(GtkComboBox
*combo
)
1689 GtkTreeModel
*model
;
1690 gchar
*dict_fullname
= NULL
;
1692 cm_return_val_if_fail(GTK_IS_COMBO_BOX(combo
), NULL
);
1693 cm_return_val_if_fail(gtk_combo_box_get_active_iter(combo
, &iter
), NULL
);
1695 model
= gtk_combo_box_get_model(combo
);
1699 gtk_tree_model_get(model
, &iter
,
1700 SET_GTKASPELL_FULLNAME
, &dict_fullname
,
1703 return dict_fullname
;
1706 gint
gtkaspell_set_dictionary_menu_active_item(GtkComboBox
*combo
,
1707 const gchar
*dictionary
)
1709 GtkTreeModel
*model
;
1711 gchar
*dict_name
= NULL
;
1713 cm_return_val_if_fail(combo
!= NULL
, 0);
1714 cm_return_val_if_fail(dictionary
!= NULL
, 0);
1715 cm_return_val_if_fail(GTK_IS_COMBO_BOX(combo
), 0);
1717 if((model
= gtk_combo_box_get_model(combo
)) == NULL
)
1719 if((gtk_tree_model_get_iter_first(model
, &iter
)) == FALSE
)
1723 gtk_tree_model_get(model
, &iter
,
1724 SET_GTKASPELL_FULLNAME
, &dict_name
,
1727 if ((dict_name
!= NULL
) && !g_strcmp0(dict_name
, dictionary
)) {
1728 gtk_combo_box_set_active_iter(combo
, &iter
);
1735 } while ((gtk_tree_model_iter_next(model
, &iter
)) == TRUE
);
1740 void gtkaspell_use_alternate_dict(GtkAspell
*gtkaspell
)
1744 tmp
= gtkaspell
->gtkaspeller
;
1745 gtkaspell
->gtkaspeller
= gtkaspell
->alternate_speller
;
1746 gtkaspell
->alternate_speller
= tmp
;
1749 static void destroy_menu(GtkWidget
*widget
,
1750 gpointer user_data
) {
1751 GtkAspell
*gtkaspell
= (GtkAspell
*)user_data
;
1753 if (gtkaspell
->accel_group
) {
1754 gtk_window_remove_accel_group(GTK_WINDOW(gtkaspell
->parent_window
),
1755 gtkaspell
->accel_group
);
1756 gtkaspell
->accel_group
= NULL
;
1760 static gboolean
aspell_key_pressed(GtkWidget
*widget
,
1762 GtkAspell
*gtkaspell
)
1764 if (event
&& (isascii(event
->keyval
) || event
->keyval
== GDK_KEY_Return
)) {
1765 gtk_accel_groups_activate(
1766 G_OBJECT(gtkaspell
->parent_window
),
1767 event
->keyval
, event
->state
);
1768 } else if (event
&& event
->keyval
== GDK_KEY_Escape
) {
1769 destroy_menu(NULL
, gtkaspell
);
1774 /* Create a paged submenu with choice of available dictionaries */
1775 static GtkWidget
*make_dictionary_list_submenu(GtkAspell
*gtkaspell
)
1777 GtkWidget
*menu
, *curmenu
, *moremenu
, *item
;
1783 if (gtkaspellcheckers
->dictionary_list
== NULL
)
1784 gtkaspell_get_dictionary_list(FALSE
);
1786 menu
= gtk_menu_new();
1789 for (tmp
= gtkaspellcheckers
->dictionary_list
; tmp
!= NULL
;
1790 tmp
= g_slist_next(tmp
)) {
1791 if (count
== MENUCOUNT
) {
1793 moremenu
= gtk_menu_new();
1794 item
= gtk_menu_item_new_with_label(_("More..."));
1795 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item
),
1798 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu
), item
);
1802 dict
= (Dictionary
*) tmp
->data
;
1803 item
= gtk_check_menu_item_new_with_label(dict
->fullname
);
1804 g_object_set_data(G_OBJECT(item
), "dict_name",
1806 if (g_strcmp0(dict
->fullname
,
1807 gtkaspell
->gtkaspeller
->dictionary
->fullname
))
1808 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item
), FALSE
);
1810 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item
), TRUE
);
1811 gtk_widget_set_sensitive(GTK_WIDGET(item
),
1814 g_signal_connect(G_OBJECT(item
), "activate",
1815 G_CALLBACK(change_dict_cb
),
1817 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu
), item
);
1822 gtk_widget_show_all(menu
);
1826 /* make_sug_menu() - Add menus to accept this word for this session
1827 * and to add it to personal dictionary
1829 static GSList
*make_sug_menu(GtkAspell
*gtkaspell
)
1831 GtkWidget
*item
, *submenu
;
1833 GtkAccelGroup
*accel
;
1834 GList
*l
= gtkaspell
->suggestions_list
;
1836 GSList
*list
= NULL
;
1841 accel
= gtk_accel_group_new();
1843 if (gtkaspell
->accel_group
) {
1844 gtk_window_remove_accel_group(GTK_WINDOW(gtkaspell
->parent_window
),
1845 gtkaspell
->accel_group
);
1846 gtkaspell
->accel_group
= NULL
;
1849 utf8buf
= g_strdup(l
->data
);
1850 caption
= g_strdup_printf(_("\"%s\" unknown in dictionary '%s'"),
1852 gtkaspell
->gtkaspeller
->dictionary
->dictname
);
1853 item
= gtk_menu_item_new_with_label(caption
);
1854 submenu
= make_dictionary_list_submenu(gtkaspell
);
1855 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item
), submenu
);
1857 gtk_widget_show(item
);
1858 list
= g_slist_append(list
, item
);
1861 item
= gtk_menu_item_new();
1862 gtk_widget_show(item
);
1863 list
= g_slist_append(list
, item
);
1865 item
= gtk_menu_item_new_with_label(_("Accept in this session"));
1866 gtk_widget_show(item
);
1867 list
= g_slist_append(list
, item
);
1868 g_signal_connect(G_OBJECT(item
), "activate",
1869 G_CALLBACK(add_word_to_session_cb
),
1871 gtk_widget_add_accelerator(item
, "activate", accel
, GDK_KEY_space
,
1873 GTK_ACCEL_LOCKED
| GTK_ACCEL_VISIBLE
);
1875 item
= gtk_menu_item_new_with_label(_("Add to personal dictionary"));
1876 gtk_widget_show(item
);
1877 list
= g_slist_append(list
, item
);
1878 g_signal_connect(G_OBJECT(item
), "activate",
1879 G_CALLBACK(add_word_to_personal_cb
),
1881 gtk_widget_add_accelerator(item
, "activate", accel
, GDK_KEY_Return
,
1883 GTK_ACCEL_LOCKED
| GTK_ACCEL_VISIBLE
);
1885 item
= gtk_menu_item_new_with_label(_("Replace with..."));
1886 gtk_widget_show(item
);
1887 list
= g_slist_append(list
, item
);
1888 g_signal_connect(G_OBJECT(item
), "activate",
1889 G_CALLBACK(replace_with_create_dialog_cb
),
1891 gtk_widget_add_accelerator(item
, "activate", accel
, GDK_KEY_R
, 0,
1892 GTK_ACCEL_LOCKED
| GTK_ACCEL_VISIBLE
);
1893 gtk_widget_add_accelerator(item
, "activate", accel
, GDK_KEY_R
,
1897 if (gtkaspell
->use_alternate
&& gtkaspell
->alternate_speller
) {
1898 caption
= g_strdup_printf(_("Check with %s"),
1899 gtkaspell
->alternate_speller
->dictionary
->dictname
);
1900 item
= gtk_menu_item_new_with_label(caption
);
1902 gtk_widget_show(item
);
1903 list
= g_slist_append(list
, item
);
1904 g_signal_connect(G_OBJECT(item
), "activate",
1905 G_CALLBACK(check_with_alternate_cb
),
1907 gtk_widget_add_accelerator(item
, "activate", accel
, GDK_KEY_X
, 0,
1908 GTK_ACCEL_LOCKED
| GTK_ACCEL_VISIBLE
);
1909 gtk_widget_add_accelerator(item
, "activate", accel
, GDK_KEY_X
,
1914 item
= gtk_menu_item_new();
1915 gtk_widget_show(item
);
1916 list
= g_slist_append(list
, item
);
1920 item
= gtk_menu_item_new_with_label(_("(no suggestions)"));
1921 gtk_widget_show(item
);
1922 list
= g_slist_append(list
, item
);
1924 GtkWidget
*curmenu
= NULL
;
1928 if (count
== MENUCOUNT
) {
1931 item
= gtk_menu_item_new_with_label(_("More..."));
1932 gtk_widget_show(item
);
1934 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu
), item
);
1936 list
= g_slist_append(list
, item
);
1938 curmenu
= gtk_menu_new();
1939 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item
),
1943 utf8buf
= g_strdup(l
->data
);
1945 item
= gtk_menu_item_new_with_label(utf8buf
);
1947 gtk_widget_show(item
);
1948 if (curmenu
== NULL
) {
1949 list
= g_slist_append(list
, item
);
1951 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu
), item
);
1953 g_signal_connect(G_OBJECT(item
), "activate",
1954 G_CALLBACK(replace_word_cb
),
1957 if (curmenu
== NULL
&& count
< MENUCOUNT
) {
1958 gtk_widget_add_accelerator(item
, "activate",
1960 GDK_KEY_A
+ count
, 0,
1963 gtk_widget_add_accelerator(item
, "activate",
1972 } while ((l
= l
->next
) != NULL
);
1975 gtk_window_add_accel_group
1976 (GTK_WINDOW(gtkaspell
->parent_window
),
1978 gtkaspell
->accel_group
= accel
;
1983 static GSList
*populate_submenu(GtkAspell
*gtkaspell
)
1985 GtkWidget
*item
, *submenu
, *both_dicts_item
;
1987 GtkAspeller
*gtkaspeller
= NULL
;
1988 GSList
*list
= NULL
;
1993 gtkaspeller
= gtkaspell
->gtkaspeller
;
1994 dictname
= g_strdup_printf(_("Dictionary: %s"),
1995 gtkaspeller
->dictionary
->dictname
);
1996 item
= gtk_menu_item_new_with_label(dictname
);
1998 submenu
= make_dictionary_list_submenu(gtkaspell
);
1999 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item
), submenu
);
2000 gtk_widget_show(item
);
2001 list
= g_slist_append(list
, item
);
2003 item
= gtk_separator_menu_item_new();
2004 gtk_widget_show(item
);
2005 list
= g_slist_append(list
, item
);
2007 if (gtkaspell
->use_alternate
&& gtkaspell
->alternate_speller
) {
2008 dictname
= g_strdup_printf(_("Use alternate (%s)"),
2009 gtkaspell
->alternate_speller
->dictionary
->dictname
);
2010 item
= gtk_menu_item_new_with_label(dictname
);
2012 g_signal_connect(G_OBJECT(item
), "activate",
2013 G_CALLBACK(switch_to_alternate_cb
),
2015 gtk_widget_show(item
);
2016 list
= g_slist_append(list
, item
);
2019 both_dicts_item
= gtk_check_menu_item_new_with_label(_("Use both dictionaries"));
2020 if (gtkaspell
->use_both_dicts
&& gtkaspell
->use_alternate
) {
2021 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(both_dicts_item
), TRUE
);
2023 gtk_widget_set_sensitive(both_dicts_item
, gtkaspell
->use_alternate
);
2025 g_signal_connect(G_OBJECT(both_dicts_item
), "activate",
2026 G_CALLBACK(set_use_both_cb
),
2028 gtk_widget_show(both_dicts_item
);
2029 list
= g_slist_append(list
, both_dicts_item
);
2031 item
= gtk_separator_menu_item_new();
2032 gtk_widget_show(item
);
2033 list
= g_slist_append(list
, item
);
2035 item
= gtk_check_menu_item_new_with_label(_("Check while typing"));
2036 if (gtkaspell
->check_while_typing
)
2037 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item
), TRUE
);
2039 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item
), FALSE
);
2040 g_signal_connect(G_OBJECT(item
), "activate",
2041 G_CALLBACK(toggle_check_while_typing_cb
),
2043 gtk_widget_show(item
);
2044 list
= g_slist_append(list
, item
);
2049 GSList
*gtkaspell_make_config_menu(GtkAspell
*gtkaspell
)
2051 return populate_submenu(gtkaspell
);
2054 static void set_menu_pos(GtkMenu
*menu
, gint
*x
, gint
*y
,
2055 gboolean
*push_in
, gpointer data
)
2057 GtkAspell
*gtkaspell
= (GtkAspell
*) data
;
2058 gint xx
= 0, yy
= 0;
2059 GtkTextView
*text
= GTK_TEXT_VIEW(gtkaspell
->gtktext
);
2060 GtkTextBuffer
*textbuf
;
2064 textbuf
= gtk_text_view_get_buffer(gtkaspell
->gtktext
);
2065 gtk_text_buffer_get_iter_at_mark(textbuf
, &iter
,
2066 gtk_text_buffer_get_insert(textbuf
));
2067 gtk_text_view_get_iter_location(gtkaspell
->gtktext
, &iter
, &rect
);
2068 gtk_text_view_buffer_to_window_coords(text
, GTK_TEXT_WINDOW_TEXT
,
2072 gdk_window_get_origin(gtk_widget_get_window(GTK_WIDGET(gtkaspell
->gtktext
)),
2075 gtk_widget_get_preferred_size(GTK_WIDGET(menu
), NULL
, NULL
);
2078 /* change the current dictionary of gtkaspell
2079 - if always_set_alt_dict is set, the alternate dict is unconditionally set to the former
2080 current dictionary (common use: from menu callbacks)
2081 - if always_set_alt_dict is NOT set, the alternate dict will be set to the former
2082 current dictionary only if there is no alternate dictionary already set
2083 (this is when we need to set the current dictionary then the alternate one
2084 when creating a compose window, from the account and folder settings)
2086 gboolean
gtkaspell_change_dict(GtkAspell
*gtkaspell
, const gchar
*dictionary
,
2087 gboolean always_set_alt_dict
)
2090 GtkAspeller
*gtkaspeller
;
2092 cm_return_val_if_fail(gtkaspell
, FALSE
);
2093 cm_return_val_if_fail(dictionary
, FALSE
);
2095 dict
= g_new0(Dictionary
, 1);
2097 if (strrchr(dictionary
, '/')) {
2098 dict
->fullname
= g_strdup(strrchr(dictionary
, '/')+1);
2099 dict
->dictname
= g_strdup(strrchr(dictionary
, '/')+1);
2101 dict
->fullname
= g_strdup(dictionary
);
2102 dict
->dictname
= g_strdup(dictionary
);
2105 if (dict
->fullname
&& strchr(dict
->fullname
, '-')) {
2106 *(strchr(dict
->fullname
, '-')) = '\0';
2107 *(strchr(dict
->dictname
, '-')) = '\0';
2110 if (!dict
->fullname
|| !(*dict
->fullname
)) {
2111 dictionary_delete(dict
);
2114 gtkaspeller
= gtkaspeller_new(dict
);
2117 alertpanel_warning(_("The spell checker could not change dictionary.\n%s"),
2118 gtkaspellcheckers
->error_message
);
2120 if (gtkaspell
->use_alternate
) {
2121 if (gtkaspell
->alternate_speller
) {
2122 if (always_set_alt_dict
) {
2123 gtkaspeller_delete(gtkaspell
->alternate_speller
);
2124 gtkaspell
->alternate_speller
= gtkaspell
->gtkaspeller
;
2126 gtkaspeller_delete(gtkaspell
->gtkaspeller
);
2128 /* should never be reached as the dicts are always set
2129 to a default value */
2130 gtkaspell
->alternate_speller
= gtkaspell
->gtkaspeller
;
2132 gtkaspeller_delete(gtkaspell
->gtkaspeller
);
2134 gtkaspell
->gtkaspeller
= gtkaspeller
;
2137 dictionary_delete(dict
);
2142 /* change the alternate dictionary of gtkaspell (doesn't affect the default dictionary) */
2143 gboolean
gtkaspell_change_alt_dict(GtkAspell
*gtkaspell
, const gchar
*alt_dictionary
)
2146 GtkAspeller
*gtkaspeller
;
2148 cm_return_val_if_fail(gtkaspell
, FALSE
);
2149 cm_return_val_if_fail(alt_dictionary
, FALSE
);
2151 dict
= g_new0(Dictionary
, 1);
2152 if (strrchr(alt_dictionary
, '/')) {
2153 dict
->fullname
= g_strdup(strrchr(alt_dictionary
, '/')+1);
2154 dict
->dictname
= g_strdup(strrchr(alt_dictionary
, '/')+1);
2156 dict
->fullname
= g_strdup(alt_dictionary
);
2157 dict
->dictname
= g_strdup(alt_dictionary
);
2160 if (dict
->fullname
&& strchr(dict
->fullname
, '-')) {
2161 *(strchr(dict
->fullname
, '-')) = '\0';
2162 *(strchr(dict
->dictname
, '-')) = '\0';
2165 if (!dict
->fullname
|| !(*dict
->fullname
)) {
2166 dictionary_delete(dict
);
2170 gtkaspeller
= gtkaspeller_new(dict
);
2173 alertpanel_warning(_("The spell checker could not change the alternate dictionary.\n%s"),
2174 gtkaspellcheckers
->error_message
);
2176 if (gtkaspell
->alternate_speller
)
2177 gtkaspeller_delete(gtkaspell
->alternate_speller
);
2178 gtkaspell
->alternate_speller
= gtkaspeller
;
2181 dictionary_delete(dict
);
2186 /* Menu call backs */
2188 /* change_dict_cb() - Menu callback : change dict */
2189 static void change_dict_cb(GtkWidget
*w
, GtkAspell
*gtkaspell
)
2193 fullname
= (gchar
*) g_object_get_data(G_OBJECT(w
), "dict_name");
2195 if (!g_strcmp0(fullname
, _("None")))
2198 gtkaspell_change_dict(gtkaspell
, fullname
, TRUE
);
2199 gtkaspell_dict_changed(gtkaspell
);
2201 if (gtkaspell
->menu_changed_cb
)
2202 gtkaspell
->menu_changed_cb(gtkaspell
->menu_changed_data
);
2205 static void switch_to_alternate_cb(GtkWidget
*w
,
2208 GtkAspell
*gtkaspell
= (GtkAspell
*) data
;
2209 gtkaspell_use_alternate_dict(gtkaspell
);
2210 gtkaspell_dict_changed(gtkaspell
);
2212 if (gtkaspell
->menu_changed_cb
)
2213 gtkaspell
->menu_changed_cb(gtkaspell
->menu_changed_data
);
2216 /* Misc. helper functions */
2218 static void set_point_continue(GtkAspell
*gtkaspell
)
2220 gtkaspell
->ctx
.set_position(gtkaspell
->ctx
.data
, gtkaspell
->orig_pos
);
2222 if (gtkaspell
->continue_check
)
2223 gtkaspell
->continue_check((gpointer
*) gtkaspell
->ctx
.data
);
2226 static void allocate_color(GtkAspell
*gtkaspell
, GdkRGBA rgba
)
2228 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(gtkaspell
->gtktext
);
2229 static const gchar
*col
= NULL
;
2231 gtkaspell
->highlight
= rgba
;
2232 col
= gtkut_gdk_rgba_to_string(&rgba
);
2234 if (strcmp(col
,"#000000") == 0)
2235 gtk_text_buffer_create_tag(buffer
, "misspelled",
2236 "underline", PANGO_UNDERLINE_ERROR
, NULL
);
2238 gtk_text_buffer_create_tag(buffer
, "misspelled",
2239 "foreground-rgba", &(gtkaspell
->highlight
), NULL
);
2242 static void change_color(GtkAspell
* gtkaspell
,
2243 gint start
, gint end
,
2247 GtkTextView
*gtktext
;
2248 GtkTextBuffer
*buffer
;
2249 GtkTextIter startiter
, enditer
;
2254 gtktext
= gtkaspell
->gtktext
;
2256 buffer
= gtk_text_view_get_buffer(gtktext
);
2257 gtk_text_buffer_get_iter_at_offset(buffer
, &startiter
, start
);
2258 gtk_text_buffer_get_iter_at_offset(buffer
, &enditer
, end
);
2260 gtk_text_buffer_apply_tag_by_name(buffer
, "misspelled",
2261 &startiter
, &enditer
);
2263 gtk_text_iter_forward_char(&enditer
);
2264 gtk_text_buffer_remove_tag_by_name(buffer
, "misspelled",
2265 &startiter
, &enditer
);
2269 /* compare_dict () - compare 2 dict names */
2270 static gint
compare_dict(Dictionary
*a
, Dictionary
*b
)
2272 guint aparts
= 0, bparts
= 0;
2275 for (i
=0; i
< strlen(a
->dictname
); i
++)
2276 if (a
->dictname
[i
] == '-')
2278 for (i
=0; i
< strlen(b
->dictname
); i
++)
2279 if (b
->dictname
[i
] == '-')
2282 if (aparts
!= bparts
)
2283 return (aparts
< bparts
) ? -1 : +1;
2286 compare
= g_strcmp0(a
->dictname
, b
->dictname
);
2288 compare
= g_strcmp0(a
->fullname
, b
->fullname
);
2293 static void dictionary_delete(Dictionary
*dict
)
2295 g_free(dict
->fullname
);
2296 g_free(dict
->dictname
);
2300 static Dictionary
*dictionary_dup(const Dictionary
*dict
)
2304 dict2
= g_new(Dictionary
, 1);
2306 dict2
->fullname
= g_strdup(dict
->fullname
);
2307 dict2
->dictname
= g_strdup(dict
->dictname
);
2312 void gtkaspell_free_suggestions_list(GtkAspell
*gtkaspell
)
2316 for (list
= gtkaspell
->suggestions_list
; list
!= NULL
;
2320 g_list_free(gtkaspell
->suggestions_list
);
2322 gtkaspell
->max_sug
= -1;
2323 gtkaspell
->suggestions_list
= NULL
;
2326 static void reset_theword_data(GtkAspell
*gtkaspell
)
2328 gtkaspell
->start_pos
= 0;
2329 gtkaspell
->end_pos
= 0;
2330 gtkaspell
->theword
[0] = 0;
2331 gtkaspell
->max_sug
= -1;
2333 gtkaspell_free_suggestions_list(gtkaspell
);
2336 static void free_checkers(gpointer elt
, gpointer data
)
2338 GtkAspeller
*gtkaspeller
= elt
;
2340 cm_return_if_fail(gtkaspeller
);
2342 gtkaspeller_real_delete(gtkaspeller
);
2345 gchar
*gtkaspell_get_default_dictionary(GtkAspell
*gtkaspell
)
2347 if (gtkaspell
&& gtkaspell
->gtkaspeller
&&
2348 gtkaspell
->gtkaspeller
->dictionary
)
2349 return gtkaspell
->gtkaspeller
->dictionary
->dictname
;