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>
51 #include <glib/gi18n.h>
55 #include <gdk/gdkkeysyms.h>
58 #include "alertpanel.h"
59 #include "gtkaspell.h"
60 #include "gtk/gtkutils.h"
61 #include "gtk/combobox.h"
63 #define ASPELL_FASTMODE 1
64 #define ASPELL_NORMALMODE 2
65 #define ASPELL_BADSPELLERMODE 3
67 /* size of the text buffer used in various word-processing routines. */
70 /* number of suggestions to display on each menu. */
74 SET_GTKASPELL_NAME
= 0,
75 SET_GTKASPELL_FULLNAME
= 1,
79 typedef struct _GtkAspellCheckers
{
81 GSList
*dictionary_list
;
85 /******************************************************************************/
87 static GtkAspellCheckers
*gtkaspellcheckers
;
89 /* Error message storage */
90 static void gtkaspell_checkers_error_message (gchar
*message
);
93 static gboolean
key_press_cb (GtkWidget
*text_view
,
95 GtkAspell
*gtkaspell
);
96 static void entry_insert_cb (GtkTextBuffer
*textbuf
,
100 GtkAspell
*gtkaspell
);
101 static void entry_delete_cb (GtkTextBuffer
*textbuf
,
102 GtkTextIter
*startiter
,
103 GtkTextIter
*enditer
,
104 GtkAspell
*gtkaspell
);
105 /*static gint button_press_intercept_cb (GtkTextView *gtktext,
107 GtkAspell *gtkaspell);
109 static void button_press_intercept_cb(GtkTextView
*gtktext
,
110 GtkMenu
*menu
, GtkAspell
*gtkaspell
);
112 /* Checker creation */
113 static GtkAspeller
* gtkaspeller_new (Dictionary
*dict
);
114 static GtkAspeller
* gtkaspeller_real_new (Dictionary
*dict
);
115 static GtkAspeller
* gtkaspeller_delete (GtkAspeller
*gtkaspeller
);
116 static GtkAspeller
* gtkaspeller_real_delete (GtkAspeller
*gtkaspeller
);
118 /* Checker configuration */
119 static EnchantDict
*set_dictionary (EnchantBroker
*broker
,
121 static void set_use_both_cb (GtkMenuItem
*w
,
122 GtkAspell
*gtkaspell
);
124 /* Checker actions */
125 static gboolean
check_at (GtkAspell
*gtkaspell
,
127 static gboolean
check_at_cb (gpointer data
);
128 static GList
* misspelled_suggest (GtkAspell
*gtkaspell
,
130 static gboolean
find_misspelled_cb (gpointer data
,
132 static void add_word_to_session_cb (GtkWidget
*w
,
134 static void add_word_to_personal_cb (GtkWidget
*w
,
136 static void replace_with_create_dialog_cb (GtkWidget
*w
,
138 static void replace_with_supplied_word_cb (GtkWidget
*w
,
139 GtkAspell
*gtkaspell
);
140 static void replace_word_cb (GtkWidget
*w
,
142 static void replace_real_word (GtkAspell
*gtkaspell
,
143 const gchar
*newword
);
144 static void replace_real_word_cb (gpointer data
,
145 const gchar
*newword
);
146 static void check_with_alternate_cb (GtkWidget
*w
,
148 static void toggle_check_while_typing_cb (GtkWidget
*w
,
152 static GSList
* make_sug_menu (GtkAspell
*gtkaspell
);
153 static GSList
* populate_submenu (GtkAspell
*gtkaspell
);
154 GSList
* gtkaspell_make_config_menu (GtkAspell
*gtkaspell
);
155 static void set_menu_pos (GtkMenu
*menu
,
160 /* Other menu callbacks */
161 static gboolean
aspell_key_pressed (GtkWidget
*widget
,
163 GtkAspell
*gtkaspell
);
164 static void change_dict_cb (GtkWidget
*w
,
165 GtkAspell
*gtkaspell
);
166 static void switch_to_alternate_cb (GtkWidget
*w
,
169 /* Misc. helper functions */
170 static void set_point_continue (GtkAspell
*gtkaspell
);
171 static void continue_check (gpointer
*gtkaspell
);
172 static gboolean
iswordsep (gunichar c
);
173 static gunichar
get_text_index_whar (GtkAspell
*gtkaspell
,
175 static gboolean
get_word_from_pos (GtkAspell
*gtkaspell
,
181 static void allocate_color (GtkAspell
*gtkaspell
,
183 static void change_color (GtkAspell
*gtkaspell
,
188 static gint
compare_dict (Dictionary
*a
,
190 static void dictionary_delete (Dictionary
*dict
);
191 static Dictionary
* dictionary_dup (const Dictionary
*dict
);
192 static void reset_theword_data (GtkAspell
*gtkaspell
);
193 static void free_checkers (gpointer elt
,
196 static void destroy_menu(GtkWidget
*widget
, gpointer user_data
);
198 /******************************************************************************/
199 static gint
get_textview_buffer_charcount(GtkTextView
*view
);
201 static void gtkaspell_free_dictionary_list (GSList
*list
);
202 static GSList
* gtkaspell_get_dictionary_list (gint refresh
);
204 static void gtkaspell_uncheck_all (GtkAspell
*gtkaspell
);
206 static gint
get_textview_buffer_charcount(GtkTextView
*view
)
208 GtkTextBuffer
*buffer
;
210 cm_return_val_if_fail(view
, 0);
212 buffer
= gtk_text_view_get_buffer(view
);
213 cm_return_val_if_fail(buffer
, 0);
215 return gtk_text_buffer_get_char_count(buffer
);
217 static gint
get_textview_buffer_offset(GtkTextView
*view
)
219 GtkTextBuffer
* buffer
;
223 cm_return_val_if_fail(view
, 0);
225 buffer
= gtk_text_view_get_buffer(view
);
226 cm_return_val_if_fail(buffer
, 0);
228 mark
= gtk_text_buffer_get_insert(buffer
);
229 cm_return_val_if_fail(mark
, 0);
231 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
233 return gtk_text_iter_get_offset(&iter
);
235 static void set_textview_buffer_offset(GtkTextView
*view
, gint offset
)
237 GtkTextBuffer
*buffer
;
240 cm_return_if_fail(view
);
242 buffer
= gtk_text_view_get_buffer(view
);
243 cm_return_if_fail(buffer
);
245 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, offset
);
246 gtk_text_buffer_place_cursor(buffer
, &iter
);
248 /******************************************************************************/
250 void gtkaspell_checkers_init(void)
252 gtkaspellcheckers
= g_new(GtkAspellCheckers
, 1);
253 gtkaspellcheckers
->checkers
= NULL
;
254 gtkaspellcheckers
->dictionary_list
= NULL
;
255 gtkaspellcheckers
->error_message
= NULL
;
258 void gtkaspell_checkers_quit(void)
263 if (gtkaspellcheckers
== NULL
)
266 if ((checkers
= gtkaspellcheckers
->checkers
)) {
267 debug_print("Aspell: number of running checkers to delete %d\n",
268 g_slist_length(checkers
));
270 g_slist_foreach(checkers
, free_checkers
, NULL
);
271 g_slist_free(checkers
);
272 gtkaspellcheckers
->checkers
= NULL
;
275 if ((dict_list
= gtkaspellcheckers
->dictionary_list
)) {
276 debug_print("Aspell: number of dictionaries to delete %d\n",
277 g_slist_length(dict_list
));
279 gtkaspell_free_dictionary_list(dict_list
);
280 gtkaspellcheckers
->dictionary_list
= NULL
;
283 g_free(gtkaspellcheckers
->error_message
);
284 gtkaspellcheckers
->error_message
= NULL
;
288 static void gtkaspell_checkers_error_message (gchar
*message
)
291 if (gtkaspellcheckers
->error_message
) {
292 tmp
= g_strdup_printf("%s\n%s",
293 gtkaspellcheckers
->error_message
,
296 g_free(gtkaspellcheckers
->error_message
);
297 gtkaspellcheckers
->error_message
= tmp
;
299 gtkaspellcheckers
->error_message
= message
;
304 const char *gtkaspell_checkers_strerror(void)
306 cm_return_val_if_fail(gtkaspellcheckers
, "");
307 return gtkaspellcheckers
->error_message
;
310 void gtkaspell_checkers_reset_error(void)
312 cm_return_if_fail(gtkaspellcheckers
);
314 g_free(gtkaspellcheckers
->error_message
);
316 gtkaspellcheckers
->error_message
= NULL
;
319 GtkAspell
*gtkaspell_new(const gchar
*dictionary
,
320 const gchar
*alt_dictionary
,
321 const gchar
*encoding
, /* unused */
322 GdkRGBA misspelled_color
,
323 gboolean check_while_typing
,
324 gboolean recheck_when_changing_dict
,
325 gboolean use_alternate
,
326 gboolean use_both_dicts
,
327 GtkTextView
*gtktext
,
328 GtkWindow
*parent_win
,
329 void (dict_changed_cb
)(void *data
),
330 void (*spell_menu_cb
)(void *data
),
334 GtkAspell
*gtkaspell
;
335 GtkAspeller
*gtkaspeller
;
336 GtkTextBuffer
*buffer
;
338 cm_return_val_if_fail(gtktext
, NULL
);
339 if (!dictionary
|| !*dictionary
) {
340 gtkaspell_checkers_error_message(
341 g_strdup(_("No dictionary selected.")));
345 buffer
= gtk_text_view_get_buffer(gtktext
);
347 dict
= g_new0(Dictionary
, 1);
348 if (strrchr(dictionary
, '/')) {
349 dict
->fullname
= g_strdup(strrchr(dictionary
, '/')+1);
350 dict
->dictname
= g_strdup(strrchr(dictionary
, '/')+1);
352 dict
->fullname
= g_strdup(dictionary
);
353 dict
->dictname
= g_strdup(dictionary
);
356 if (strchr(dict
->fullname
, '-')) {
357 *(strchr(dict
->fullname
, '-')) = '\0';
358 *(strchr(dict
->dictname
, '-')) = '\0';
360 gtkaspeller
= gtkaspeller_new(dict
);
361 dictionary_delete(dict
);
364 gtkaspell_checkers_error_message(
365 g_strdup_printf(_("Couldn't initialize %s speller."), dictionary
));
369 gtkaspell
= g_new0(GtkAspell
, 1);
371 gtkaspell
->gtkaspeller
= gtkaspeller
;
373 if (use_alternate
&& alt_dictionary
&& *alt_dictionary
) {
374 Dictionary
*alt_dict
;
375 GtkAspeller
*alt_gtkaspeller
;
377 alt_dict
= g_new0(Dictionary
, 1);
378 if (strrchr(alt_dictionary
, '/')) {
379 alt_dict
->fullname
= g_strdup(strrchr(alt_dictionary
, '/')+1);
380 alt_dict
->dictname
= g_strdup(strrchr(alt_dictionary
, '/')+1);
382 alt_dict
->fullname
= g_strdup(alt_dictionary
);
383 alt_dict
->dictname
= g_strdup(alt_dictionary
);
385 if (strchr(alt_dict
->fullname
, '-')) {
386 *(strchr(alt_dict
->fullname
, '-')) = '\0';
387 *(strchr(alt_dict
->dictname
, '-')) = '\0';
390 alt_gtkaspeller
= gtkaspeller_new(alt_dict
);
391 dictionary_delete(alt_dict
);
393 if (!alt_gtkaspeller
) {
394 gtkaspell_checkers_error_message(
395 g_strdup_printf(_("Couldn't initialize %s speller."), dictionary
));
396 gtkaspeller_delete(gtkaspeller
);
401 gtkaspell
->alternate_speller
= alt_gtkaspeller
;
403 gtkaspell
->alternate_speller
= NULL
;
406 gtkaspell
->theword
[0] = 0x00;
407 gtkaspell
->start_pos
= 0;
408 gtkaspell
->end_pos
= 0;
409 gtkaspell
->orig_pos
= -1;
410 gtkaspell
->end_check_pos
= -1;
411 gtkaspell
->misspelled
= -1;
412 gtkaspell
->check_while_typing
= check_while_typing
;
413 gtkaspell
->recheck_when_changing_dict
= recheck_when_changing_dict
;
414 gtkaspell
->continue_check
= NULL
;
415 gtkaspell
->replace_entry
= NULL
;
416 gtkaspell
->gtktext
= gtktext
;
417 gtkaspell
->max_sug
= -1;
418 gtkaspell
->suggestions_list
= NULL
;
419 gtkaspell
->use_alternate
= use_alternate
;
420 gtkaspell
->use_both_dicts
= use_both_dicts
;
421 gtkaspell
->parent_window
= GTK_WIDGET(parent_win
);
422 gtkaspell
->dict_changed_cb
= dict_changed_cb
;
423 gtkaspell
->menu_changed_cb
= spell_menu_cb
;
424 gtkaspell
->menu_changed_data
= data
;
426 allocate_color(gtkaspell
, misspelled_color
);
428 g_signal_connect(G_OBJECT(gtktext
), "key_press_event",
429 G_CALLBACK(key_press_cb
), gtkaspell
);
430 g_signal_connect_after(G_OBJECT(buffer
), "insert-text",
431 G_CALLBACK(entry_insert_cb
), gtkaspell
);
432 g_signal_connect_after(G_OBJECT(buffer
), "delete-range",
433 G_CALLBACK(entry_delete_cb
), gtkaspell
);
434 /*g_signal_connect(G_OBJECT(gtktext), "button-press-event",
435 G_CALLBACK(button_press_intercept_cb),
437 g_signal_connect(G_OBJECT(gtktext
), "populate-popup",
438 G_CALLBACK(button_press_intercept_cb
), gtkaspell
);
440 debug_print("Aspell: created gtkaspell %p\n", gtkaspell
);
445 void gtkaspell_delete(GtkAspell
*gtkaspell
)
447 GtkTextView
*gtktext
= gtkaspell
->gtktext
;
449 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext
),
450 G_CALLBACK(key_press_cb
),
452 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext
),
453 G_CALLBACK(entry_insert_cb
),
455 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext
),
456 G_CALLBACK(entry_delete_cb
),
458 g_signal_handlers_disconnect_by_func(G_OBJECT(gtktext
),
459 G_CALLBACK(button_press_intercept_cb
),
462 gtkaspell_uncheck_all(gtkaspell
);
464 gtkaspeller_delete(gtkaspell
->gtkaspeller
);
466 if (gtkaspell
->alternate_speller
)
467 gtkaspeller_delete(gtkaspell
->alternate_speller
);
469 if (gtkaspell
->suggestions_list
)
470 gtkaspell_free_suggestions_list(gtkaspell
);
472 debug_print("Aspell: deleting gtkaspell %p\n", gtkaspell
);
479 void gtkaspell_dict_changed(GtkAspell
*gtkaspell
)
481 if(!gtkaspell
|| !gtkaspell
->dict_changed_cb
||
482 !gtkaspell
->menu_changed_data
)
485 gtkaspell
->dict_changed_cb(gtkaspell
->menu_changed_data
);
488 static gboolean
key_press_cb (GtkWidget
*text_view
,
490 GtkAspell
*gtkaspell
)
494 cm_return_val_if_fail(gtkaspell
->gtkaspeller
->speller
, FALSE
);
496 if (!gtkaspell
->check_while_typing
)
499 switch (event
->keyval
) {
505 case GDK_KEY_Page_Up
:
506 case GDK_KEY_Page_Down
:
509 pos
= get_textview_buffer_offset(GTK_TEXT_VIEW(text_view
));
511 check_at(gtkaspell
, pos
- 1);
513 check_at(gtkaspell
, pos
);
522 static void entry_insert_cb(GtkTextBuffer
*textbuf
,
526 GtkAspell
*gtkaspell
)
530 cm_return_if_fail(gtkaspell
->gtkaspeller
->speller
);
532 if (!gtkaspell
->check_while_typing
)
535 pos
= gtk_text_iter_get_offset(iter
);
537 if (iswordsep(g_utf8_get_char(newtext
))) {
538 /* did we just end a word? */
540 check_at(gtkaspell
, pos
- 2);
542 /* did we just split a word? */
543 if (pos
< gtk_text_buffer_get_char_count(textbuf
))
544 check_at(gtkaspell
, pos
+ 1);
546 /* check as they type, *except* if they're typing at the end (the most
549 if (pos
< gtk_text_buffer_get_char_count(textbuf
) &&
550 !iswordsep(get_text_index_whar(gtkaspell
, pos
))) {
551 check_at(gtkaspell
, pos
- 1);
556 static void entry_delete_cb(GtkTextBuffer
*textbuf
,
557 GtkTextIter
*startiter
,
558 GtkTextIter
*enditer
,
559 GtkAspell
*gtkaspell
)
564 cm_return_if_fail(gtkaspell
->gtkaspeller
->speller
);
566 if (!gtkaspell
->check_while_typing
)
569 start
= gtk_text_iter_get_offset(startiter
);
570 origpos
= get_textview_buffer_offset(gtkaspell
->gtktext
);
572 check_at(gtkaspell
, start
- 1);
573 check_at(gtkaspell
, start
);
576 set_textview_buffer_offset(gtkaspell
->gtktext
, origpos
);
577 /* this is to *UNDO* the selection, in case they were holding shift
578 * while hitting backspace. */
579 /* needed with textview ??? */
580 /* gtk_editable_select_region(GTK_EDITABLE(gtktext), origpos, origpos); */
583 void gtkaspell_make_context_menu(GtkMenu
*menu
, GtkAspell
*gtkaspell
)
585 GtkMenuItem
*menuitem
;
586 GSList
*spell_menu
= NULL
;
588 gboolean suggest
= FALSE
;
590 if (gtkaspell
->misspelled
&&
591 misspelled_suggest(gtkaspell
, gtkaspell
->theword
)) {
592 spell_menu
= make_sug_menu(gtkaspell
);
596 spell_menu
= gtkaspell_make_config_menu(gtkaspell
);
598 menuitem
= GTK_MENU_ITEM(gtk_separator_menu_item_new());
599 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), GTK_WIDGET(menuitem
));
600 gtk_widget_show(GTK_WIDGET(menuitem
));
602 spell_menu
= g_slist_reverse(spell_menu
);
603 for (items
= spell_menu
;
604 items
; items
= items
->next
) {
605 menuitem
= GTK_MENU_ITEM(items
->data
);
606 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), GTK_WIDGET(menuitem
));
607 gtk_widget_show(GTK_WIDGET(menuitem
));
609 g_slist_free(spell_menu
);
611 g_signal_connect(G_OBJECT(menu
), "deactivate",
612 G_CALLBACK(destroy_menu
),
615 g_signal_connect(G_OBJECT(menu
),
617 G_CALLBACK(aspell_key_pressed
),
621 static void set_position_cb(gpointer data
, gint pos
)
623 GtkAspell
*gtkaspell
= (GtkAspell
*) data
;
624 set_textview_buffer_offset(gtkaspell
->gtktext
, pos
);
627 void gtkaspell_context_set(GtkAspell
*gtkaspell
)
629 gtkaspell
->ctx
.set_position
= set_position_cb
;
630 gtkaspell
->ctx
.set_menu_pos
= set_menu_pos
;
631 gtkaspell
->ctx
.find_misspelled
= find_misspelled_cb
;
632 gtkaspell
->ctx
.check_word
= check_at_cb
;
633 gtkaspell
->ctx
.replace_word
= replace_real_word_cb
;
634 gtkaspell
->ctx
.data
= (gpointer
) gtkaspell
;
637 static void button_press_intercept_cb(GtkTextView
*gtktext
,
638 GtkMenu
*menu
, GtkAspell
*gtkaspell
)
640 gtktext
= gtkaspell
->gtktext
;
641 gtkaspell
->orig_pos
= get_textview_buffer_offset(gtktext
);
642 gtkaspell
->misspelled
= check_at(gtkaspell
, gtkaspell
->orig_pos
);
644 gtkaspell_context_set(gtkaspell
);
645 gtkaspell_make_context_menu(menu
, gtkaspell
);
647 /* Checker creation */
648 static GtkAspeller
*gtkaspeller_new(Dictionary
*dictionary
)
650 GtkAspeller
*gtkaspeller
= NULL
;
654 cm_return_val_if_fail(gtkaspellcheckers
, NULL
);
656 cm_return_val_if_fail(dictionary
, NULL
);
658 if (dictionary
->dictname
== NULL
)
659 gtkaspell_checkers_error_message(
660 g_strdup(_("No dictionary selected.")));
662 cm_return_val_if_fail(dictionary
->fullname
, NULL
);
664 dict
= dictionary_dup(dictionary
);
666 tmp
= g_new0(GtkAspeller
, 1);
667 tmp
->dictionary
= dict
;
671 if ((gtkaspeller
= gtkaspeller_real_new(dict
)) != NULL
) {
672 gtkaspellcheckers
->checkers
= g_slist_append(
673 gtkaspellcheckers
->checkers
,
676 debug_print("Aspell: Created a new gtkaspeller %p\n",
679 dictionary_delete(dict
);
681 debug_print("Aspell: Could not create spell checker.\n");
684 debug_print("Aspell: number of existing checkers %d\n",
685 g_slist_length(gtkaspellcheckers
->checkers
));
690 static GtkAspeller
*gtkaspeller_real_new(Dictionary
*dict
)
692 GtkAspeller
*gtkaspeller
;
693 EnchantBroker
*broker
;
694 EnchantDict
*speller
;
696 cm_return_val_if_fail(gtkaspellcheckers
, NULL
);
697 cm_return_val_if_fail(dict
, NULL
);
699 gtkaspeller
= g_new(GtkAspeller
, 1);
701 gtkaspeller
->dictionary
= dict
;
703 broker
= enchant_broker_init();
706 gtkaspell_checkers_error_message(
707 g_strdup(_("Couldn't initialize Enchant broker.")));
711 if ((speller
= set_dictionary(broker
, dict
)) == NULL
) {
712 gtkaspell_checkers_error_message(
713 g_strdup_printf(_("Couldn't initialize %s dictionary:"), dict
->fullname
));
714 gtkaspell_checkers_error_message(
715 g_strdup(enchant_broker_get_error(broker
)));
719 gtkaspeller
->speller
= speller
;
720 gtkaspeller
->broker
= broker
;
725 static GtkAspeller
*gtkaspeller_delete(GtkAspeller
*gtkaspeller
)
727 cm_return_val_if_fail(gtkaspellcheckers
, NULL
);
729 gtkaspellcheckers
->checkers
=
730 g_slist_remove(gtkaspellcheckers
->checkers
,
733 debug_print("Aspell: Deleting gtkaspeller %p.\n",
736 gtkaspeller_real_delete(gtkaspeller
);
738 debug_print("Aspell: number of existing checkers %d\n",
739 g_slist_length(gtkaspellcheckers
->checkers
));
744 static GtkAspeller
*gtkaspeller_real_delete(GtkAspeller
*gtkaspeller
)
746 cm_return_val_if_fail(gtkaspeller
, NULL
);
747 cm_return_val_if_fail(gtkaspeller
->speller
, NULL
);
749 enchant_broker_free_dict(gtkaspeller
->broker
, gtkaspeller
->speller
);
750 enchant_broker_free(gtkaspeller
->broker
);
752 dictionary_delete(gtkaspeller
->dictionary
);
754 debug_print("Aspell: gtkaspeller %p deleted.\n",
762 /*****************************************************************************/
763 /* Checker configuration */
765 static EnchantDict
*set_dictionary(EnchantBroker
*broker
, Dictionary
*dict
)
767 cm_return_val_if_fail(broker
, FALSE
);
768 cm_return_val_if_fail(dict
, FALSE
);
770 return enchant_broker_request_dict(broker
, dict
->dictname
);
773 static void set_use_both_cb(GtkMenuItem
*w
, GtkAspell
*gtkaspell
)
775 gtkaspell
->use_both_dicts
= gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w
));
776 gtkaspell_dict_changed(gtkaspell
);
778 if (gtkaspell
->menu_changed_cb
)
779 gtkaspell
->menu_changed_cb(gtkaspell
->menu_changed_data
);
782 /* misspelled_suggest() - Create a suggestion list for word */
783 static GList
*misspelled_suggest(GtkAspell
*gtkaspell
, gchar
*word
)
788 cm_return_val_if_fail(word
, NULL
);
793 gtkaspell_free_suggestions_list(gtkaspell
);
795 suggestions
= enchant_dict_suggest(gtkaspell
->gtkaspeller
->speller
, word
, strlen(word
), &num_sug
);
796 list
= g_list_append(list
, g_strdup(word
));
797 if (suggestions
== NULL
|| num_sug
== 0) {
798 gtkaspell
->max_sug
= -1;
799 gtkaspell
->suggestions_list
= list
;
802 for (i
= 0; i
< num_sug
; i
++)
803 list
= g_list_append(list
, g_strdup((gchar
*)suggestions
[i
]));
805 gtkaspell
->max_sug
= num_sug
- 1;
806 gtkaspell
->suggestions_list
= list
;
807 enchant_dict_free_string_list(gtkaspell
->gtkaspeller
->speller
, suggestions
);
811 /* misspelled_test() - Just test if word is correctly spelled */
812 int gtkaspell_misspelled_test(GtkAspell
*gtkaspell
, char *word
)
815 cm_return_val_if_fail(word
, 0);
820 result
= enchant_dict_check(gtkaspell
->gtkaspeller
->speller
, word
, strlen(word
));
822 if (result
&& gtkaspell
->use_both_dicts
&& gtkaspell
->alternate_speller
) {
823 gtkaspell_use_alternate_dict(gtkaspell
);
824 result
= enchant_dict_check(gtkaspell
->gtkaspeller
->speller
, word
, strlen(word
));
825 gtkaspell_use_alternate_dict(gtkaspell
);
827 return (result
&& strcasecmp(word
, "sylpheed") &&
828 strcasecmp(word
, "claws-mail"));
832 static gboolean
iswordsep(gunichar c
)
834 return (g_unichar_isspace(c
) || g_unichar_ispunct(c
)) && c
!= (gunichar
)'\'';
837 static gunichar
get_text_index_whar(GtkAspell
*gtkaspell
, int pos
)
839 GtkTextView
*view
= gtkaspell
->gtktext
;
840 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(view
);
841 GtkTextIter start
, end
;
845 gtk_text_buffer_get_iter_at_offset(buffer
, &start
, pos
);
846 gtk_text_buffer_get_iter_at_offset(buffer
, &end
, pos
+1);
848 utf8chars
= gtk_text_iter_get_text(&start
, &end
);
849 a
= g_utf8_get_char(utf8chars
);
855 /* get_word_from_pos () - return the word pointed to. */
856 /* Handles correctly the quotes. */
857 static gboolean
get_word_from_pos(GtkAspell
*gtkaspell
, gint pos
,
858 char* buf
, gint buflen
,
859 gint
*pstart
, gint
*pend
)
862 /* TODO : when correcting a word into quotes, change the color of */
863 /* the quotes too, as may be they were highlighted before. To do */
864 /* this, we can use two others pointers that points to the whole */
865 /* word including quotes. */
871 GtkTextView
*gtktext
;
873 gtktext
= gtkaspell
->gtktext
;
874 if (iswordsep(get_text_index_whar(gtkaspell
, pos
)))
877 /* The apostrophe character is somtimes used for quotes
878 * So include it in the word only if it is not surrounded
879 * by other characters.
882 for (start
= pos
; start
>= 0; --start
) {
883 c
= get_text_index_whar(gtkaspell
, start
);
884 if (c
== (gunichar
)'\'') {
886 if (g_unichar_isspace(get_text_index_whar(gtkaspell
,
888 || g_unichar_ispunct(get_text_index_whar(gtkaspell
,
890 || g_unichar_isdigit(get_text_index_whar(gtkaspell
,
892 /* start_quote = TRUE; */
897 /* start_quote = TRUE; */
901 else if (g_unichar_isspace(c
) || g_unichar_ispunct(c
) || g_unichar_isdigit(c
))
907 for (end
= pos
; end
< get_textview_buffer_charcount(gtktext
); end
++) {
908 c
= get_text_index_whar(gtkaspell
, end
);
909 if (c
== (gunichar
)'\'') {
910 if (end
< get_textview_buffer_charcount(gtktext
)) {
911 if (g_unichar_isspace(get_text_index_whar(gtkaspell
,
913 || g_unichar_ispunct(get_text_index_whar(gtkaspell
,
915 || g_unichar_isdigit(get_text_index_whar(gtkaspell
,
917 /* end_quote = TRUE; */
922 /* end_quote = TRUE; */
926 else if (g_unichar_isspace(c
) || g_unichar_ispunct(c
) || g_unichar_isdigit(c
))
936 if (end
- start
< buflen
) {
937 GtkTextIter iterstart
, iterend
;
939 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(gtktext
);
940 gtk_text_buffer_get_iter_at_offset(buffer
, &iterstart
, start
);
941 gtk_text_buffer_get_iter_at_offset(buffer
, &iterend
, end
);
942 tmp
= gtk_text_buffer_get_text(buffer
, &iterstart
, &iterend
, FALSE
);
943 strncpy(buf
, tmp
, buflen
-1);
953 static gboolean
check_at(GtkAspell
*gtkaspell
, gint from_pos
)
956 char buf
[GTKASPELLWORDSIZE
];
958 cm_return_val_if_fail(from_pos
>= 0, FALSE
);
960 if (!get_word_from_pos(gtkaspell
, from_pos
, buf
, sizeof(buf
),
964 if (gtkaspell_misspelled_test(gtkaspell
, buf
)) {
965 strncpy(gtkaspell
->theword
, (gchar
*)buf
, GTKASPELLWORDSIZE
- 1);
966 gtkaspell
->theword
[GTKASPELLWORDSIZE
- 1] = 0;
967 gtkaspell
->start_pos
= start
;
968 gtkaspell
->end_pos
= end
;
969 gtkaspell_free_suggestions_list(gtkaspell
);
971 change_color(gtkaspell
, start
, end
, (gchar
*)buf
, TRUE
);
974 change_color(gtkaspell
, start
, end
, (gchar
*)buf
, FALSE
);
979 static gboolean
check_at_cb(gpointer data
)
981 GtkAspell
*gtkaspell
= (GtkAspell
*)data
;
982 return check_at(gtkaspell
, gtkaspell
->start_pos
);
985 static gboolean
find_misspelled_cb(gpointer data
, gboolean forward
)
987 GtkAspell
*gtkaspell
= (GtkAspell
*)data
;
995 maxpos
= gtkaspell
->end_check_pos
;
1003 pos
= get_textview_buffer_offset(gtkaspell
->gtktext
);
1004 gtkaspell
->orig_pos
= pos
;
1005 while (iswordsep(get_text_index_whar(gtkaspell
, pos
)) &&
1006 pos
> minpos
&& pos
<= maxpos
)
1008 while (!(misspelled
= check_at(gtkaspell
, pos
)) &&
1009 pos
> minpos
&& pos
<= maxpos
) {
1011 while (!iswordsep(get_text_index_whar(gtkaspell
, pos
)) &&
1012 pos
> minpos
&& pos
<= maxpos
)
1015 while (iswordsep(get_text_index_whar(gtkaspell
, pos
)) &&
1016 pos
> minpos
&& pos
<= maxpos
)
1023 gboolean
gtkaspell_check_next_prev(GtkAspell
*gtkaspell
, gboolean forward
)
1025 gboolean misspelled
= gtkaspell
->ctx
.find_misspelled(gtkaspell
->ctx
.data
,
1030 misspelled_suggest(gtkaspell
, gtkaspell
->theword
);
1033 gtkaspell
->orig_pos
= gtkaspell
->end_pos
;
1035 gtkaspell
->ctx
.set_position(gtkaspell
->ctx
.data
, gtkaspell
->end_pos
);
1037 /* only execute when in textview context */
1038 if (gtkaspell
== (GtkAspell
*)gtkaspell
->ctx
.data
) {
1039 /* scroll line to window center */
1040 gtk_text_view_scroll_to_mark(gtkaspell
->gtktext
,
1041 gtk_text_buffer_get_insert(
1042 gtk_text_view_get_buffer(gtkaspell
->gtktext
)),
1043 0.0, TRUE
, 0.0, 0.5);
1044 /* let textview recalculate coordinates (set_menu_pos) */
1045 while (gtk_events_pending ())
1046 gtk_main_iteration ();
1049 list
= make_sug_menu(gtkaspell
);
1050 menu
= gtk_menu_new();
1051 for (cur
= list
; cur
; cur
= cur
->next
)
1052 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), GTK_WIDGET(cur
->data
));
1054 gtk_menu_popup_at_pointer(GTK_MENU(menu
), NULL
);
1055 g_signal_connect(G_OBJECT(menu
), "deactivate",
1056 G_CALLBACK(destroy_menu
),
1058 g_signal_connect(G_OBJECT(menu
),
1060 G_CALLBACK(aspell_key_pressed
),
1065 reset_theword_data(gtkaspell
);
1067 alertpanel_notice(_("No misspelled word found."));
1068 gtkaspell
->ctx
.set_position(gtkaspell
->ctx
.data
,
1069 gtkaspell
->orig_pos
);
1075 void gtkaspell_check_backwards(GtkAspell
*gtkaspell
)
1077 gtkaspell
->continue_check
= NULL
;
1078 gtkaspell
->end_check_pos
=
1079 get_textview_buffer_charcount(gtkaspell
->gtktext
);
1080 gtkaspell_context_set(gtkaspell
);
1081 gtkaspell_check_next_prev(gtkaspell
, FALSE
);
1084 void gtkaspell_check_forwards_go(GtkAspell
*gtkaspell
)
1087 gtkaspell
->continue_check
= NULL
;
1088 gtkaspell
->end_check_pos
=
1089 get_textview_buffer_charcount(gtkaspell
->gtktext
);
1090 gtkaspell_context_set(gtkaspell
);
1091 gtkaspell_check_next_prev(gtkaspell
, TRUE
);
1094 void gtkaspell_check_all(GtkAspell
*gtkaspell
)
1096 GtkTextView
*gtktext
;
1098 GtkTextBuffer
*buffer
;
1099 GtkTextIter startiter
, enditer
;
1101 cm_return_if_fail(gtkaspell
);
1102 cm_return_if_fail(gtkaspell
->gtktext
);
1104 gtktext
= gtkaspell
->gtktext
;
1105 buffer
= gtk_text_view_get_buffer(gtktext
);
1106 gtk_text_buffer_get_selection_bounds(buffer
, &startiter
, &enditer
);
1107 start
= gtk_text_iter_get_offset(&startiter
);
1108 end
= gtk_text_iter_get_offset(&enditer
);
1112 end
= gtk_text_buffer_get_char_count(buffer
);
1113 } else if (start
> end
) {
1121 set_textview_buffer_offset(gtktext
, start
);
1123 gtkaspell
->continue_check
= continue_check
;
1124 gtkaspell
->end_check_pos
= end
;
1126 gtkaspell_context_set(gtkaspell
);
1127 gtkaspell
->misspelled
= gtkaspell_check_next_prev(gtkaspell
, TRUE
);
1130 static void continue_check(gpointer
*data
)
1132 GtkAspell
*gtkaspell
= (GtkAspell
*) data
;
1133 gint pos
= get_textview_buffer_offset(gtkaspell
->gtktext
);
1134 if (pos
< gtkaspell
->end_check_pos
&& gtkaspell
->misspelled
)
1135 gtkaspell
->misspelled
= gtkaspell_check_next_prev(gtkaspell
, TRUE
);
1137 gtkaspell
->continue_check
= NULL
;
1140 void gtkaspell_highlight_all(GtkAspell
*gtkaspell
)
1145 GtkTextView
*gtktext
;
1147 cm_return_if_fail(gtkaspell
->gtkaspeller
->speller
);
1149 gtktext
= gtkaspell
->gtktext
;
1151 len
= get_textview_buffer_charcount(gtktext
);
1153 origpos
= get_textview_buffer_offset(gtktext
);
1157 iswordsep(get_text_index_whar(gtkaspell
, pos
)))
1160 !iswordsep(get_text_index_whar(gtkaspell
, pos
)))
1163 check_at(gtkaspell
, pos
- 1);
1165 set_textview_buffer_offset(gtktext
, origpos
);
1168 static void replace_with_supplied_word_cb(GtkWidget
*w
, GtkAspell
*gtkaspell
)
1171 GdkEvent
*e
= (GdkEvent
*) gtk_get_current_event();
1173 newword
= gtk_editable_get_chars(GTK_EDITABLE(gtkaspell
->replace_entry
),
1176 if (strcmp(newword
, gtkaspell
->theword
)) {
1177 gtkaspell
->ctx
.replace_word(gtkaspell
->ctx
.data
, newword
);
1179 if ((e
->type
== GDK_KEY_PRESS
&&
1180 ((GdkEventKey
*) e
)->state
& GDK_CONTROL_MASK
)) {
1181 enchant_dict_store_replacement(gtkaspell
->gtkaspeller
->speller
,
1182 gtkaspell
->theword
, strlen(gtkaspell
->theword
),
1183 newword
, strlen(newword
));
1185 gtkaspell
->replace_entry
= NULL
;
1190 if (w
&& GTK_IS_DIALOG(w
)) {
1191 gtk_widget_destroy(w
);
1194 set_point_continue(gtkaspell
);
1198 static void replace_word_cb(GtkWidget
*w
, gpointer data
)
1200 const char *newword
;
1201 GtkAspell
*gtkaspell
= (GtkAspell
*) data
;
1202 GdkEvent
*e
= (GdkEvent
*) gtk_get_current_event();
1204 newword
= gtk_label_get_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN((w
)))));
1206 gtkaspell
->ctx
.replace_word(gtkaspell
->ctx
.data
, newword
);
1208 if ((e
->type
== GDK_KEY_PRESS
&&
1209 ((GdkEventKey
*) e
)->state
& GDK_CONTROL_MASK
) ||
1210 (e
->type
== GDK_BUTTON_RELEASE
&&
1211 ((GdkEventButton
*) e
)->state
& GDK_CONTROL_MASK
)) {
1212 enchant_dict_store_replacement(gtkaspell
->gtkaspeller
->speller
,
1213 gtkaspell
->theword
, strlen(gtkaspell
->theword
),
1214 newword
, strlen(newword
));
1217 gtk_menu_shell_deactivate(GTK_MENU_SHELL(gtk_widget_get_parent(w
)));
1219 set_point_continue(gtkaspell
);
1222 void gtkaspell_block_check(GtkAspell
*gtkaspell
)
1224 GtkTextView
*gtktext
;
1226 if (gtkaspell
== NULL
)
1229 gtktext
= gtkaspell
->gtktext
;
1230 g_signal_handlers_block_by_func(G_OBJECT(gtktext
),
1231 G_CALLBACK(key_press_cb
),
1233 g_signal_handlers_block_by_func(G_OBJECT(gtktext
),
1234 G_CALLBACK(entry_insert_cb
),
1236 g_signal_handlers_block_by_func(G_OBJECT(gtktext
),
1237 G_CALLBACK(entry_delete_cb
),
1241 void gtkaspell_unblock_check(GtkAspell
*gtkaspell
)
1243 GtkTextView
*gtktext
;
1245 if (gtkaspell
== NULL
)
1248 gtktext
= gtkaspell
->gtktext
;
1249 g_signal_handlers_unblock_by_func(G_OBJECT(gtktext
),
1250 G_CALLBACK(key_press_cb
),
1252 g_signal_handlers_unblock_by_func(G_OBJECT(gtktext
),
1253 G_CALLBACK(entry_insert_cb
),
1255 g_signal_handlers_unblock_by_func(G_OBJECT(gtktext
),
1256 G_CALLBACK(entry_delete_cb
),
1260 static void replace_real_word(GtkAspell
*gtkaspell
, const gchar
*newword
)
1265 GtkTextView
*gtktext
;
1266 GtkTextBuffer
*textbuf
;
1267 GtkTextIter startiter
, enditer
;
1269 if (!newword
) return;
1271 gtktext
= gtkaspell
->gtktext
;
1272 textbuf
= gtk_text_view_get_buffer(gtktext
);
1274 origpos
= gtkaspell
->orig_pos
;
1276 oldlen
= gtkaspell
->end_pos
- gtkaspell
->start_pos
;
1278 newlen
= strlen(newword
); /* FIXME: multybyte characters? */
1280 gtkaspell_block_check(gtkaspell
);
1282 gtk_text_buffer_get_iter_at_offset(textbuf
, &startiter
,
1283 gtkaspell
->start_pos
);
1284 gtk_text_buffer_get_iter_at_offset(textbuf
, &enditer
,
1285 gtkaspell
->end_pos
);
1286 g_signal_emit_by_name(G_OBJECT(textbuf
), "delete-range",
1287 &startiter
, &enditer
, gtkaspell
);
1288 g_signal_emit_by_name(G_OBJECT(textbuf
), "insert-text",
1289 &startiter
, newword
, newlen
, gtkaspell
);
1291 gtkaspell_unblock_check(gtkaspell
);
1293 /* Put the point and the position where we clicked with the mouse
1294 * It seems to be a hack, as I must thaw,freeze,thaw the widget
1295 * to let it update correctly the word insertion and then the
1296 * point & position position. If not, SEGV after the first replacement
1297 * If the new word ends before point, put the point at its end.
1300 if (origpos
- gtkaspell
->start_pos
< oldlen
&&
1301 origpos
- gtkaspell
->start_pos
>= 0) {
1302 /* Original point was in the word.
1303 * Let it there unless point is going to be outside of the word
1305 if (origpos
- gtkaspell
->start_pos
>= newlen
) {
1306 pos
= gtkaspell
->start_pos
+ newlen
;
1309 else if (origpos
>= gtkaspell
->end_pos
) {
1310 /* move the position according to the change of length */
1311 pos
= origpos
+ newlen
- oldlen
;
1314 gtkaspell
->end_pos
= gtkaspell
->start_pos
+ strlen(newword
); /* FIXME: multibyte characters? */
1316 if (get_textview_buffer_charcount(gtktext
) < pos
)
1317 pos
= get_textview_buffer_charcount(gtktext
);
1318 gtkaspell
->orig_pos
= pos
;
1320 set_textview_buffer_offset(gtktext
, gtkaspell
->orig_pos
);
1323 static void replace_real_word_cb(gpointer data
, const gchar
*newword
)
1325 replace_real_word((GtkAspell
*)data
, newword
);
1328 /* Accept this word for this session */
1329 static void add_word_to_session_cb(GtkWidget
*w
, gpointer data
)
1331 GtkAspell
*gtkaspell
= (GtkAspell
*) data
;
1333 enchant_dict_add_to_session(gtkaspell
->gtkaspeller
->speller
, gtkaspell
->theword
, strlen(gtkaspell
->theword
));
1335 gtkaspell
->ctx
.check_word(gtkaspell
->ctx
.data
);
1336 gtkaspell_dict_changed(gtkaspell
);
1338 gtk_menu_shell_deactivate(GTK_MENU_SHELL(gtk_widget_get_parent(w
)));
1340 set_point_continue(gtkaspell
);
1343 /* add_word_to_personal_cb() - add word to personal dict. */
1344 static void add_word_to_personal_cb(GtkWidget
*w
, gpointer data
)
1346 GtkAspell
*gtkaspell
= (GtkAspell
*) data
;
1348 enchant_dict_add(gtkaspell
->gtkaspeller
->speller
, gtkaspell
->theword
, strlen(gtkaspell
->theword
));
1350 gtkaspell
->ctx
.check_word(gtkaspell
->ctx
.data
);
1351 gtkaspell_dict_changed(gtkaspell
);
1353 gtk_menu_shell_deactivate(GTK_MENU_SHELL(gtk_widget_get_parent(w
)));
1354 set_point_continue(gtkaspell
);
1357 static void check_with_alternate_cb(GtkWidget
*w
, gpointer data
)
1359 GtkAspell
*gtkaspell
= (GtkAspell
*)data
;
1362 gtk_menu_shell_deactivate(GTK_MENU_SHELL(gtk_widget_get_parent(w
)));
1364 gtkaspell_use_alternate_dict(gtkaspell
);
1365 misspelled
= gtkaspell
->ctx
.check_word(gtkaspell
->ctx
.data
);
1367 if (!gtkaspell
->continue_check
) {
1369 gtkaspell
->misspelled
= misspelled
;
1371 if (gtkaspell
->misspelled
) {
1374 misspelled_suggest(gtkaspell
, gtkaspell
->theword
);
1376 gtkaspell
->ctx
.set_position(gtkaspell
->ctx
.data
,
1377 gtkaspell
->end_pos
);
1379 list
= make_sug_menu(gtkaspell
);
1380 menu
= gtk_menu_new();
1381 for (cur
= list
; cur
; cur
= cur
->next
)
1382 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), GTK_WIDGET(cur
->data
));
1384 gtk_menu_popup_at_pointer(GTK_MENU(menu
), NULL
);
1385 g_signal_connect(G_OBJECT(menu
), "deactivate",
1386 G_CALLBACK(destroy_menu
),
1388 g_signal_connect(G_OBJECT(menu
),
1390 G_CALLBACK(aspell_key_pressed
),
1395 gtkaspell
->orig_pos
= gtkaspell
->start_pos
;
1397 set_point_continue(gtkaspell
);
1400 static gboolean
replace_key_pressed(GtkWidget
*widget
,
1402 GtkAspell
*gtkaspell
)
1404 if (event
&& event
->keyval
== GDK_KEY_Escape
) {
1405 gtk_widget_destroy(widget
);
1407 } else if (event
&& (event
->keyval
== GDK_KEY_KP_Enter
||
1408 event
->keyval
== GDK_KEY_Return
)) {
1409 replace_with_supplied_word_cb(widget
, gtkaspell
);
1415 static void replace_with_create_dialog_cb(GtkWidget
*w
, gpointer data
)
1417 static PangoFontDescription
*font_desc
;
1423 GtkWidget
*ok_button
;
1424 GtkWidget
*cancel_button
;
1426 GtkWidget
*parent_window
;
1427 GtkWidget
*content_area
;
1428 gchar
*utf8buf
, *thelabel
, *format
;
1430 GtkAspell
*gtkaspell
= (GtkAspell
*) data
;
1432 cm_return_if_fail(w
!= NULL
);
1433 parent_window
= gtk_widget_get_parent(w
);
1434 cm_return_if_fail(parent_window
!= NULL
);
1435 gdk_window_get_origin(gtk_widget_get_window(parent_window
), &xx
, &yy
);
1437 gtk_menu_shell_deactivate(GTK_MENU_SHELL(gtk_widget_get_parent(w
)));
1439 dialog
= gtk_dialog_new();
1440 content_area
= gtk_dialog_get_content_area(GTK_DIALOG(dialog
));
1442 gtk_window_set_resizable(GTK_WINDOW(dialog
), FALSE
);
1443 gtk_window_set_title(GTK_WINDOW(dialog
),_("Replace unknown word"));
1444 gtk_window_move(GTK_WINDOW(dialog
), xx
, yy
);
1446 g_signal_connect_swapped(G_OBJECT(dialog
), "destroy",
1447 G_CALLBACK(gtk_widget_destroy
),
1450 gtk_box_set_spacing (GTK_BOX (content_area
), 14);
1451 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 12);
1452 gtk_container_set_border_width (GTK_CONTAINER (hbox
), 5);
1453 gtk_widget_show (hbox
);
1454 gtk_box_pack_start (GTK_BOX (content_area
), hbox
, FALSE
, FALSE
, 0);
1456 utf8buf
= g_strdup(gtkaspell
->theword
);
1458 format
= g_strconcat("<span weight=\"bold\" size=\"larger\">",
1459 _("Replace \"%s\" with: "), "</span>", NULL
);
1460 thelabel
= g_strdup_printf(format
, utf8buf
);
1463 icon
= gtk_image_new_from_icon_name("dialog-question",
1464 GTK_ICON_SIZE_DIALOG
);
1465 gtk_widget_set_halign(icon
, GTK_ALIGN_CENTER
);
1466 gtk_widget_set_valign(icon
, GTK_ALIGN_START
);
1467 gtk_box_pack_start (GTK_BOX (hbox
), icon
, FALSE
, FALSE
, 0);
1469 vbox
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, 12);
1470 gtk_box_pack_start (GTK_BOX (hbox
), vbox
, TRUE
, TRUE
, 0);
1471 gtk_widget_show (vbox
);
1473 label
= gtk_label_new(thelabel
);
1474 gtk_label_set_xalign(GTK_LABEL(label
), 0.0);
1475 gtk_label_set_justify(GTK_LABEL(label
), GTK_JUSTIFY_LEFT
);
1476 gtk_label_set_use_markup (GTK_LABEL (label
), TRUE
);
1477 gtk_box_pack_start(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
1478 gtk_label_set_line_wrap(GTK_LABEL(label
), TRUE
);
1482 size
= pango_font_description_get_size
1483 (gtk_widget_get_style(label
)->font_desc
);
1484 font_desc
= pango_font_description_new();
1485 pango_font_description_set_weight
1486 (font_desc
, PANGO_WEIGHT_BOLD
);
1487 pango_font_description_set_size
1488 (font_desc
, size
* PANGO_SCALE_LARGE
);
1491 gtk_widget_override_font(label
, font_desc
);
1494 entry
= gtk_entry_new();
1495 gtkaspell
->replace_entry
= entry
;
1496 gtk_entry_set_text(GTK_ENTRY(entry
), utf8buf
);
1497 gtk_editable_select_region(GTK_EDITABLE(entry
), 0, -1);
1498 g_signal_connect(G_OBJECT(dialog
),
1500 G_CALLBACK(replace_key_pressed
), gtkaspell
);
1501 gtk_box_pack_start(GTK_BOX(vbox
), entry
, FALSE
, FALSE
, 0);
1504 label
= gtk_label_new(_("Holding down Control key while pressing "
1505 "Enter\nwill learn from mistake.\n"));
1506 gtk_label_set_xalign(GTK_LABEL(label
), 0.0);
1507 gtk_label_set_justify(GTK_LABEL(label
), GTK_JUSTIFY_LEFT
);
1508 gtk_box_pack_start(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
1509 gtk_widget_show(label
);
1511 cancel_button
= gtk_dialog_add_button(GTK_DIALOG(dialog
), _("_Cancel"),
1513 ok_button
= gtk_dialog_add_button(GTK_DIALOG(dialog
),_("_OK"),
1515 g_signal_connect(G_OBJECT(ok_button
), "clicked",
1516 G_CALLBACK(replace_with_supplied_word_cb
),
1518 g_signal_connect_swapped(G_OBJECT(ok_button
), "clicked",
1519 G_CALLBACK(gtk_widget_destroy
),
1522 g_signal_connect_swapped(G_OBJECT(cancel_button
), "clicked",
1523 G_CALLBACK(gtk_widget_destroy
),
1526 gtk_widget_grab_focus(entry
);
1528 gtk_window_set_modal(GTK_WINDOW(dialog
), TRUE
);
1530 gtk_widget_show_all(dialog
);
1533 static void gtkaspell_uncheck_all(GtkAspell
* gtkaspell
)
1535 GtkTextView
*gtktext
;
1536 GtkTextBuffer
*buffer
;
1537 GtkTextIter startiter
, enditer
;
1539 gtktext
= gtkaspell
->gtktext
;
1541 buffer
= gtk_text_view_get_buffer(gtktext
);
1542 gtk_text_buffer_get_iter_at_offset(buffer
, &startiter
, 0);
1543 gtk_text_buffer_get_iter_at_offset(buffer
, &enditer
,
1544 get_textview_buffer_charcount(gtktext
)-1);
1545 gtk_text_buffer_remove_tag_by_name(buffer
, "misspelled",
1546 &startiter
, &enditer
);
1549 static void toggle_check_while_typing_cb(GtkWidget
*w
, gpointer data
)
1551 GtkAspell
*gtkaspell
= (GtkAspell
*) data
;
1553 gtkaspell
->check_while_typing
= gtkaspell
->check_while_typing
== FALSE
;
1555 if (!gtkaspell
->check_while_typing
)
1556 gtkaspell_uncheck_all(gtkaspell
);
1557 if (gtkaspell
->menu_changed_cb
)
1558 gtkaspell
->menu_changed_cb(gtkaspell
->menu_changed_data
);
1561 static GSList
*create_empty_dictionary_list(void)
1563 GSList
*list
= NULL
;
1566 dict
= g_new0(Dictionary
, 1);
1567 dict
->fullname
= g_strdup(_("None"));
1568 dict
->dictname
= NULL
;
1570 return g_slist_append(list
, dict
);
1573 static void list_dict_cb(const char * const lang_tag
,
1574 const char * const provider_name
,
1575 const char * const provider_desc
,
1576 const char * const provider_file
,
1579 GSList
**list
= (GSList
**)data
;
1580 Dictionary
*dict
= g_new0(Dictionary
, 1);
1581 dict
->fullname
= g_strdup(lang_tag
);
1582 dict
->dictname
= g_strdup(lang_tag
);
1584 if (g_slist_find_custom(*list
, dict
,
1585 (GCompareFunc
) compare_dict
) == NULL
) {
1586 debug_print("Aspell: found dictionary %s %s\n", dict
->fullname
,
1588 *list
= g_slist_insert_sorted(*list
, dict
,
1589 (GCompareFunc
) compare_dict
);
1591 dictionary_delete(dict
);
1595 /* gtkaspell_get_dictionary_list() - returns list of dictionary names */
1596 static GSList
*gtkaspell_get_dictionary_list(gint refresh
)
1599 EnchantBroker
*broker
;
1601 if (!gtkaspellcheckers
)
1602 gtkaspell_checkers_init();
1604 if (gtkaspellcheckers
->dictionary_list
&& !refresh
)
1605 return gtkaspellcheckers
->dictionary_list
;
1607 gtkaspell_free_dictionary_list(
1608 gtkaspellcheckers
->dictionary_list
);
1611 broker
= enchant_broker_init();
1613 enchant_broker_list_dicts(broker
, list_dict_cb
, &list
);
1615 enchant_broker_free(broker
);
1619 debug_print("Aspell: error when searching for dictionaries: "
1620 "No dictionary found.\n");
1621 list
= create_empty_dictionary_list();
1624 gtkaspellcheckers
->dictionary_list
= list
;
1629 static void gtkaspell_free_dictionary_list(GSList
*list
)
1633 for (walk
= list
; walk
!= NULL
; walk
= g_slist_next(walk
))
1635 dict
= (Dictionary
*) walk
->data
;
1636 dictionary_delete(dict
);
1641 GtkTreeModel
*gtkaspell_dictionary_store_new_with_refresh(gboolean refresh
)
1643 GSList
*dict_list
, *tmp
;
1644 GtkListStore
*store
;
1648 dict_list
= gtkaspell_get_dictionary_list(refresh
);
1649 cm_return_val_if_fail(dict_list
, NULL
);
1651 store
= gtk_list_store_new(SET_GTKASPELL_SIZE
,
1656 for (tmp
= dict_list
; tmp
!= NULL
; tmp
= g_slist_next(tmp
)) {
1657 dict
= (Dictionary
*) tmp
->data
;
1659 gtk_list_store_append(store
, &iter
);
1660 gtk_list_store_set(store
, &iter
,
1661 SET_GTKASPELL_NAME
, dict
->dictname
,
1662 SET_GTKASPELL_FULLNAME
, dict
->fullname
,
1666 return GTK_TREE_MODEL(store
);
1669 GtkTreeModel
*gtkaspell_dictionary_store_new(void)
1671 return gtkaspell_dictionary_store_new_with_refresh
1675 GtkWidget
*gtkaspell_dictionary_combo_new(const gboolean refresh
)
1678 GtkCellRenderer
*renderer
;
1680 combo
= gtk_combo_box_new_with_model(
1681 gtkaspell_dictionary_store_new_with_refresh(refresh
));
1682 gtk_combo_box_set_active(GTK_COMBO_BOX(combo
), 0);
1683 gtk_widget_show(combo
);
1685 renderer
= gtk_cell_renderer_text_new();
1686 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo
), renderer
, TRUE
);
1687 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo
),renderer
,
1688 "text", SET_GTKASPELL_NAME
, NULL
);
1693 gchar
*gtkaspell_get_dictionary_menu_active_item(GtkComboBox
*combo
)
1696 GtkTreeModel
*model
;
1697 gchar
*dict_fullname
= NULL
;
1699 cm_return_val_if_fail(GTK_IS_COMBO_BOX(combo
), NULL
);
1700 cm_return_val_if_fail(gtk_combo_box_get_active_iter(combo
, &iter
), NULL
);
1702 model
= gtk_combo_box_get_model(combo
);
1706 gtk_tree_model_get(model
, &iter
,
1707 SET_GTKASPELL_FULLNAME
, &dict_fullname
,
1710 return dict_fullname
;
1713 gint
gtkaspell_set_dictionary_menu_active_item(GtkComboBox
*combo
,
1714 const gchar
*dictionary
)
1716 GtkTreeModel
*model
;
1718 gchar
*dict_name
= NULL
;
1720 cm_return_val_if_fail(combo
!= NULL
, 0);
1721 cm_return_val_if_fail(dictionary
!= NULL
, 0);
1722 cm_return_val_if_fail(GTK_IS_COMBO_BOX(combo
), 0);
1724 if((model
= gtk_combo_box_get_model(combo
)) == NULL
)
1726 if((gtk_tree_model_get_iter_first(model
, &iter
)) == FALSE
)
1730 gtk_tree_model_get(model
, &iter
,
1731 SET_GTKASPELL_FULLNAME
, &dict_name
,
1734 if ((dict_name
!= NULL
) && !g_strcmp0(dict_name
, dictionary
)) {
1735 gtk_combo_box_set_active_iter(combo
, &iter
);
1742 } while ((gtk_tree_model_iter_next(model
, &iter
)) == TRUE
);
1747 void gtkaspell_use_alternate_dict(GtkAspell
*gtkaspell
)
1751 tmp
= gtkaspell
->gtkaspeller
;
1752 gtkaspell
->gtkaspeller
= gtkaspell
->alternate_speller
;
1753 gtkaspell
->alternate_speller
= tmp
;
1756 static void destroy_menu(GtkWidget
*widget
,
1757 gpointer user_data
) {
1758 GtkAspell
*gtkaspell
= (GtkAspell
*)user_data
;
1760 if (gtkaspell
->accel_group
) {
1761 gtk_window_remove_accel_group(GTK_WINDOW(gtkaspell
->parent_window
),
1762 gtkaspell
->accel_group
);
1763 gtkaspell
->accel_group
= NULL
;
1767 static gboolean
aspell_key_pressed(GtkWidget
*widget
,
1769 GtkAspell
*gtkaspell
)
1771 if (event
&& (isascii(event
->keyval
) || event
->keyval
== GDK_KEY_Return
)) {
1772 gtk_accel_groups_activate(
1773 G_OBJECT(gtkaspell
->parent_window
),
1774 event
->keyval
, event
->state
);
1775 } else if (event
&& event
->keyval
== GDK_KEY_Escape
) {
1776 destroy_menu(NULL
, gtkaspell
);
1781 /* Create a paged submenu with choice of available dictionaries */
1782 static GtkWidget
*make_dictionary_list_submenu(GtkAspell
*gtkaspell
)
1784 GtkWidget
*menu
, *curmenu
, *moremenu
, *item
;
1790 if (gtkaspellcheckers
->dictionary_list
== NULL
)
1791 gtkaspell_get_dictionary_list(FALSE
);
1793 menu
= gtk_menu_new();
1796 for (tmp
= gtkaspellcheckers
->dictionary_list
; tmp
!= NULL
;
1797 tmp
= g_slist_next(tmp
)) {
1798 if (count
== MENUCOUNT
) {
1800 moremenu
= gtk_menu_new();
1801 item
= gtk_menu_item_new_with_label(_("More..."));
1802 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item
),
1805 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu
), item
);
1809 dict
= (Dictionary
*) tmp
->data
;
1810 item
= gtk_check_menu_item_new_with_label(dict
->fullname
);
1811 g_object_set_data(G_OBJECT(item
), "dict_name",
1813 if (g_strcmp0(dict
->fullname
,
1814 gtkaspell
->gtkaspeller
->dictionary
->fullname
))
1815 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item
), FALSE
);
1817 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item
), TRUE
);
1818 gtk_widget_set_sensitive(GTK_WIDGET(item
),
1821 g_signal_connect(G_OBJECT(item
), "activate",
1822 G_CALLBACK(change_dict_cb
),
1824 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu
), item
);
1829 gtk_widget_show_all(menu
);
1833 /* make_sug_menu() - Add menus to accept this word for this session
1834 * and to add it to personal dictionary
1836 static GSList
*make_sug_menu(GtkAspell
*gtkaspell
)
1838 GtkWidget
*item
, *submenu
;
1840 GtkAccelGroup
*accel
;
1841 GList
*l
= gtkaspell
->suggestions_list
;
1843 GSList
*list
= NULL
;
1848 accel
= gtk_accel_group_new();
1850 if (gtkaspell
->accel_group
) {
1851 gtk_window_remove_accel_group(GTK_WINDOW(gtkaspell
->parent_window
),
1852 gtkaspell
->accel_group
);
1853 gtkaspell
->accel_group
= NULL
;
1856 utf8buf
= g_strdup(l
->data
);
1857 caption
= g_strdup_printf(_("\"%s\" unknown in dictionary '%s'"),
1859 gtkaspell
->gtkaspeller
->dictionary
->dictname
);
1860 item
= gtk_menu_item_new_with_label(caption
);
1861 submenu
= make_dictionary_list_submenu(gtkaspell
);
1862 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item
), submenu
);
1864 gtk_widget_show(item
);
1865 list
= g_slist_append(list
, item
);
1868 item
= gtk_menu_item_new();
1869 gtk_widget_show(item
);
1870 list
= g_slist_append(list
, item
);
1872 item
= gtk_menu_item_new_with_label(_("Accept in this session"));
1873 gtk_widget_show(item
);
1874 list
= g_slist_append(list
, item
);
1875 g_signal_connect(G_OBJECT(item
), "activate",
1876 G_CALLBACK(add_word_to_session_cb
),
1878 gtk_widget_add_accelerator(item
, "activate", accel
, GDK_KEY_space
,
1880 GTK_ACCEL_LOCKED
| GTK_ACCEL_VISIBLE
);
1882 item
= gtk_menu_item_new_with_label(_("Add to personal dictionary"));
1883 gtk_widget_show(item
);
1884 list
= g_slist_append(list
, item
);
1885 g_signal_connect(G_OBJECT(item
), "activate",
1886 G_CALLBACK(add_word_to_personal_cb
),
1888 gtk_widget_add_accelerator(item
, "activate", accel
, GDK_KEY_Return
,
1890 GTK_ACCEL_LOCKED
| GTK_ACCEL_VISIBLE
);
1892 item
= gtk_menu_item_new_with_label(_("Replace with..."));
1893 gtk_widget_show(item
);
1894 list
= g_slist_append(list
, item
);
1895 g_signal_connect(G_OBJECT(item
), "activate",
1896 G_CALLBACK(replace_with_create_dialog_cb
),
1898 gtk_widget_add_accelerator(item
, "activate", accel
, GDK_KEY_R
, 0,
1899 GTK_ACCEL_LOCKED
| GTK_ACCEL_VISIBLE
);
1900 gtk_widget_add_accelerator(item
, "activate", accel
, GDK_KEY_R
,
1904 if (gtkaspell
->use_alternate
&& gtkaspell
->alternate_speller
) {
1905 caption
= g_strdup_printf(_("Check with %s"),
1906 gtkaspell
->alternate_speller
->dictionary
->dictname
);
1907 item
= gtk_menu_item_new_with_label(caption
);
1909 gtk_widget_show(item
);
1910 list
= g_slist_append(list
, item
);
1911 g_signal_connect(G_OBJECT(item
), "activate",
1912 G_CALLBACK(check_with_alternate_cb
),
1914 gtk_widget_add_accelerator(item
, "activate", accel
, GDK_KEY_X
, 0,
1915 GTK_ACCEL_LOCKED
| GTK_ACCEL_VISIBLE
);
1916 gtk_widget_add_accelerator(item
, "activate", accel
, GDK_KEY_X
,
1921 item
= gtk_menu_item_new();
1922 gtk_widget_show(item
);
1923 list
= g_slist_append(list
, item
);
1927 item
= gtk_menu_item_new_with_label(_("(no suggestions)"));
1928 gtk_widget_show(item
);
1929 list
= g_slist_append(list
, item
);
1931 GtkWidget
*curmenu
= NULL
;
1935 if (count
== MENUCOUNT
) {
1938 item
= gtk_menu_item_new_with_label(_("More..."));
1939 gtk_widget_show(item
);
1941 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu
), item
);
1943 list
= g_slist_append(list
, item
);
1945 curmenu
= gtk_menu_new();
1946 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item
),
1950 utf8buf
= g_strdup(l
->data
);
1952 item
= gtk_menu_item_new_with_label(utf8buf
);
1954 gtk_widget_show(item
);
1955 if (curmenu
== NULL
) {
1956 list
= g_slist_append(list
, item
);
1958 gtk_menu_shell_append(GTK_MENU_SHELL(curmenu
), item
);
1960 g_signal_connect(G_OBJECT(item
), "activate",
1961 G_CALLBACK(replace_word_cb
),
1964 if (curmenu
== NULL
&& count
< MENUCOUNT
) {
1965 gtk_widget_add_accelerator(item
, "activate",
1967 GDK_KEY_A
+ count
, 0,
1970 gtk_widget_add_accelerator(item
, "activate",
1979 } while ((l
= l
->next
) != NULL
);
1982 gtk_window_add_accel_group
1983 (GTK_WINDOW(gtkaspell
->parent_window
),
1985 gtkaspell
->accel_group
= accel
;
1990 static GSList
*populate_submenu(GtkAspell
*gtkaspell
)
1992 GtkWidget
*item
, *submenu
, *both_dicts_item
;
1994 GtkAspeller
*gtkaspeller
= NULL
;
1995 GSList
*list
= NULL
;
2000 gtkaspeller
= gtkaspell
->gtkaspeller
;
2001 dictname
= g_strdup_printf(_("Dictionary: %s"),
2002 gtkaspeller
->dictionary
->dictname
);
2003 item
= gtk_menu_item_new_with_label(dictname
);
2005 submenu
= make_dictionary_list_submenu(gtkaspell
);
2006 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item
), submenu
);
2007 gtk_widget_show(item
);
2008 list
= g_slist_append(list
, item
);
2010 item
= gtk_separator_menu_item_new();
2011 gtk_widget_show(item
);
2012 list
= g_slist_append(list
, item
);
2014 if (gtkaspell
->use_alternate
&& gtkaspell
->alternate_speller
) {
2015 dictname
= g_strdup_printf(_("Use alternate (%s)"),
2016 gtkaspell
->alternate_speller
->dictionary
->dictname
);
2017 item
= gtk_menu_item_new_with_label(dictname
);
2019 g_signal_connect(G_OBJECT(item
), "activate",
2020 G_CALLBACK(switch_to_alternate_cb
),
2022 gtk_widget_show(item
);
2023 list
= g_slist_append(list
, item
);
2026 both_dicts_item
= gtk_check_menu_item_new_with_label(_("Use both dictionaries"));
2027 if (gtkaspell
->use_both_dicts
&& gtkaspell
->use_alternate
) {
2028 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(both_dicts_item
), TRUE
);
2030 gtk_widget_set_sensitive(both_dicts_item
, gtkaspell
->use_alternate
);
2032 g_signal_connect(G_OBJECT(both_dicts_item
), "activate",
2033 G_CALLBACK(set_use_both_cb
),
2035 gtk_widget_show(both_dicts_item
);
2036 list
= g_slist_append(list
, both_dicts_item
);
2038 item
= gtk_separator_menu_item_new();
2039 gtk_widget_show(item
);
2040 list
= g_slist_append(list
, item
);
2042 item
= gtk_check_menu_item_new_with_label(_("Check while typing"));
2043 if (gtkaspell
->check_while_typing
)
2044 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item
), TRUE
);
2046 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item
), FALSE
);
2047 g_signal_connect(G_OBJECT(item
), "activate",
2048 G_CALLBACK(toggle_check_while_typing_cb
),
2050 gtk_widget_show(item
);
2051 list
= g_slist_append(list
, item
);
2056 GSList
*gtkaspell_make_config_menu(GtkAspell
*gtkaspell
)
2058 return populate_submenu(gtkaspell
);
2061 static void set_menu_pos(GtkMenu
*menu
, gint
*x
, gint
*y
,
2062 gboolean
*push_in
, gpointer data
)
2064 GtkAspell
*gtkaspell
= (GtkAspell
*) data
;
2065 gint xx
= 0, yy
= 0;
2066 GtkTextView
*text
= GTK_TEXT_VIEW(gtkaspell
->gtktext
);
2067 GtkTextBuffer
*textbuf
;
2071 textbuf
= gtk_text_view_get_buffer(gtkaspell
->gtktext
);
2072 gtk_text_buffer_get_iter_at_mark(textbuf
, &iter
,
2073 gtk_text_buffer_get_insert(textbuf
));
2074 gtk_text_view_get_iter_location(gtkaspell
->gtktext
, &iter
, &rect
);
2075 gtk_text_view_buffer_to_window_coords(text
, GTK_TEXT_WINDOW_TEXT
,
2079 gdk_window_get_origin(gtk_widget_get_window(GTK_WIDGET(gtkaspell
->gtktext
)),
2082 gtk_widget_get_preferred_size(GTK_WIDGET(menu
), NULL
, NULL
);
2085 /* change the current dictionary of gtkaspell
2086 - if always_set_alt_dict is set, the alternate dict is unconditionally set to the former
2087 current dictionary (common use: from menu callbacks)
2088 - if always_set_alt_dict is NOT set, the alternate dict will be set to the former
2089 current dictionary only if there is no alternate dictionary already set
2090 (this is when we need to set the current dictionary then the alternate one
2091 when creating a compose window, from the account and folder settings)
2093 gboolean
gtkaspell_change_dict(GtkAspell
*gtkaspell
, const gchar
*dictionary
,
2094 gboolean always_set_alt_dict
)
2097 GtkAspeller
*gtkaspeller
;
2099 cm_return_val_if_fail(gtkaspell
, FALSE
);
2100 cm_return_val_if_fail(dictionary
, FALSE
);
2102 dict
= g_new0(Dictionary
, 1);
2104 if (strrchr(dictionary
, '/')) {
2105 dict
->fullname
= g_strdup(strrchr(dictionary
, '/')+1);
2106 dict
->dictname
= g_strdup(strrchr(dictionary
, '/')+1);
2108 dict
->fullname
= g_strdup(dictionary
);
2109 dict
->dictname
= g_strdup(dictionary
);
2112 if (dict
->fullname
&& strchr(dict
->fullname
, '-')) {
2113 *(strchr(dict
->fullname
, '-')) = '\0';
2114 *(strchr(dict
->dictname
, '-')) = '\0';
2117 if (!dict
->fullname
|| !(*dict
->fullname
)) {
2118 dictionary_delete(dict
);
2121 gtkaspeller
= gtkaspeller_new(dict
);
2124 alertpanel_warning(_("The spell checker could not change dictionary.\n%s"),
2125 gtkaspellcheckers
->error_message
);
2127 if (gtkaspell
->use_alternate
) {
2128 if (gtkaspell
->alternate_speller
) {
2129 if (always_set_alt_dict
) {
2130 gtkaspeller_delete(gtkaspell
->alternate_speller
);
2131 gtkaspell
->alternate_speller
= gtkaspell
->gtkaspeller
;
2133 gtkaspeller_delete(gtkaspell
->gtkaspeller
);
2135 /* should never be reached as the dicts are always set
2136 to a default value */
2137 gtkaspell
->alternate_speller
= gtkaspell
->gtkaspeller
;
2139 gtkaspeller_delete(gtkaspell
->gtkaspeller
);
2141 gtkaspell
->gtkaspeller
= gtkaspeller
;
2144 dictionary_delete(dict
);
2149 /* change the alternate dictionary of gtkaspell (doesn't affect the default dictionary) */
2150 gboolean
gtkaspell_change_alt_dict(GtkAspell
*gtkaspell
, const gchar
*alt_dictionary
)
2153 GtkAspeller
*gtkaspeller
;
2155 cm_return_val_if_fail(gtkaspell
, FALSE
);
2156 cm_return_val_if_fail(alt_dictionary
, FALSE
);
2158 dict
= g_new0(Dictionary
, 1);
2159 if (strrchr(alt_dictionary
, '/')) {
2160 dict
->fullname
= g_strdup(strrchr(alt_dictionary
, '/')+1);
2161 dict
->dictname
= g_strdup(strrchr(alt_dictionary
, '/')+1);
2163 dict
->fullname
= g_strdup(alt_dictionary
);
2164 dict
->dictname
= g_strdup(alt_dictionary
);
2167 if (dict
->fullname
&& strchr(dict
->fullname
, '-')) {
2168 *(strchr(dict
->fullname
, '-')) = '\0';
2169 *(strchr(dict
->dictname
, '-')) = '\0';
2172 if (!dict
->fullname
|| !(*dict
->fullname
)) {
2173 dictionary_delete(dict
);
2177 gtkaspeller
= gtkaspeller_new(dict
);
2180 alertpanel_warning(_("The spell checker could not change the alternate dictionary.\n%s"),
2181 gtkaspellcheckers
->error_message
);
2183 if (gtkaspell
->alternate_speller
)
2184 gtkaspeller_delete(gtkaspell
->alternate_speller
);
2185 gtkaspell
->alternate_speller
= gtkaspeller
;
2188 dictionary_delete(dict
);
2193 /* Menu call backs */
2195 /* change_dict_cb() - Menu callback : change dict */
2196 static void change_dict_cb(GtkWidget
*w
, GtkAspell
*gtkaspell
)
2200 fullname
= (gchar
*) g_object_get_data(G_OBJECT(w
), "dict_name");
2202 if (!g_strcmp0(fullname
, _("None")))
2205 gtkaspell_change_dict(gtkaspell
, fullname
, TRUE
);
2206 gtkaspell_dict_changed(gtkaspell
);
2208 if (gtkaspell
->menu_changed_cb
)
2209 gtkaspell
->menu_changed_cb(gtkaspell
->menu_changed_data
);
2212 static void switch_to_alternate_cb(GtkWidget
*w
,
2215 GtkAspell
*gtkaspell
= (GtkAspell
*) data
;
2216 gtkaspell_use_alternate_dict(gtkaspell
);
2217 gtkaspell_dict_changed(gtkaspell
);
2219 if (gtkaspell
->menu_changed_cb
)
2220 gtkaspell
->menu_changed_cb(gtkaspell
->menu_changed_data
);
2223 /* Misc. helper functions */
2225 static void set_point_continue(GtkAspell
*gtkaspell
)
2227 gtkaspell
->ctx
.set_position(gtkaspell
->ctx
.data
, gtkaspell
->orig_pos
);
2229 if (gtkaspell
->continue_check
)
2230 gtkaspell
->continue_check((gpointer
*) gtkaspell
->ctx
.data
);
2233 static void allocate_color(GtkAspell
*gtkaspell
, GdkRGBA rgba
)
2235 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(gtkaspell
->gtktext
);
2236 static const gchar
*col
= NULL
;
2238 gtkaspell
->highlight
= rgba
;
2239 col
= gtkut_gdk_rgba_to_string(&rgba
);
2241 if (strcmp(col
,"#000000") == 0)
2242 gtk_text_buffer_create_tag(buffer
, "misspelled",
2243 "underline", PANGO_UNDERLINE_ERROR
, NULL
);
2245 gtk_text_buffer_create_tag(buffer
, "misspelled",
2246 "foreground-rgba", &(gtkaspell
->highlight
), NULL
);
2249 static void change_color(GtkAspell
* gtkaspell
,
2250 gint start
, gint end
,
2254 GtkTextView
*gtktext
;
2255 GtkTextBuffer
*buffer
;
2256 GtkTextIter startiter
, enditer
;
2261 gtktext
= gtkaspell
->gtktext
;
2263 buffer
= gtk_text_view_get_buffer(gtktext
);
2264 gtk_text_buffer_get_iter_at_offset(buffer
, &startiter
, start
);
2265 gtk_text_buffer_get_iter_at_offset(buffer
, &enditer
, end
);
2267 gtk_text_buffer_apply_tag_by_name(buffer
, "misspelled",
2268 &startiter
, &enditer
);
2270 gtk_text_iter_forward_char(&enditer
);
2271 gtk_text_buffer_remove_tag_by_name(buffer
, "misspelled",
2272 &startiter
, &enditer
);
2276 /* compare_dict () - compare 2 dict names */
2277 static gint
compare_dict(Dictionary
*a
, Dictionary
*b
)
2279 guint aparts
= 0, bparts
= 0;
2282 for (i
=0; i
< strlen(a
->dictname
); i
++)
2283 if (a
->dictname
[i
] == '-')
2285 for (i
=0; i
< strlen(b
->dictname
); i
++)
2286 if (b
->dictname
[i
] == '-')
2289 if (aparts
!= bparts
)
2290 return (aparts
< bparts
) ? -1 : +1;
2293 compare
= g_strcmp0(a
->dictname
, b
->dictname
);
2295 compare
= g_strcmp0(a
->fullname
, b
->fullname
);
2300 static void dictionary_delete(Dictionary
*dict
)
2302 g_free(dict
->fullname
);
2303 g_free(dict
->dictname
);
2307 static Dictionary
*dictionary_dup(const Dictionary
*dict
)
2311 dict2
= g_new(Dictionary
, 1);
2313 dict2
->fullname
= g_strdup(dict
->fullname
);
2314 dict2
->dictname
= g_strdup(dict
->dictname
);
2319 void gtkaspell_free_suggestions_list(GtkAspell
*gtkaspell
)
2323 for (list
= gtkaspell
->suggestions_list
; list
!= NULL
;
2327 g_list_free(gtkaspell
->suggestions_list
);
2329 gtkaspell
->max_sug
= -1;
2330 gtkaspell
->suggestions_list
= NULL
;
2333 static void reset_theword_data(GtkAspell
*gtkaspell
)
2335 gtkaspell
->start_pos
= 0;
2336 gtkaspell
->end_pos
= 0;
2337 gtkaspell
->theword
[0] = 0;
2338 gtkaspell
->max_sug
= -1;
2340 gtkaspell_free_suggestions_list(gtkaspell
);
2343 static void free_checkers(gpointer elt
, gpointer data
)
2345 GtkAspeller
*gtkaspeller
= elt
;
2347 cm_return_if_fail(gtkaspeller
);
2349 gtkaspeller_real_delete(gtkaspeller
);
2352 gchar
*gtkaspell_get_default_dictionary(GtkAspell
*gtkaspell
)
2354 if (gtkaspell
&& gtkaspell
->gtkaspeller
&&
2355 gtkaspell
->gtkaspeller
->dictionary
)
2356 return gtkaspell
->gtkaspeller
->dictionary
->dictname
;