1 /* gtkaspell - a spell-checking addon for GtkText
2 * Copyright (c) 2000 Evan Martin (original code for ispell).
3 * Copyright (c) 2002 Melvin Hadasht.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; If not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * Stuphead: (C) 2000,2001 Grigroy Bakunov, Sergey Pinaev
21 * Adapted for Sylpheed (Claws) (c) 2001-2002 by Hiroyuki Yamamoto &
22 * The Sylpheed Claws Team.
23 * Adapted for pspell (c) 2001-2002 Melvin Hadasht
24 * Adapted for GNU/aspell (c) 2002 Melvin Hadasht
36 #include <sys/types.h>
51 #include <gtk/gtkoptionmenu.h>
52 #include <gtk/gtkmenu.h>
53 #include <gtk/gtkmenuitem.h>
54 #include <gdk/gdkkeysyms.h>
62 #include "gtkaspell.h"
63 #define ASPELL_FASTMODE 1
64 #define ASPELL_NORMALMODE 2
65 #define ASPELL_BADSPELLERMODE 3
67 #define GTKASPELLWORDSIZE 1024
69 /* size of the text buffer used in various word-processing routines. */
72 /* number of suggestions to display on each menu. */
75 /* 'config' must be defined as a 'AspellConfig *' */
76 #define RETURN_FALSE_IF_CONFIG_ERROR() \
78 if (aspell_config_error_number(config) != 0) { \
79 gtkaspellcheckers->error_message = g_strdup(aspell_config_error_message(config)); \
84 #define CONFIG_REPLACE_RETURN_FALSE_IF_FAIL(option, value) { \
85 aspell_config_replace(config, option, value); \
86 RETURN_FALSE_IF_CONFIG_ERROR(); \
89 typedef struct _GtkAspellCheckers
{
91 GSList
*dictionary_list
;
95 typedef struct _Dictionary
{
101 typedef struct _GtkAspeller
{
102 Dictionary
*dictionary
;
104 AspellConfig
*config
;
105 AspellSpeller
*checker
;
108 typedef void (*ContCheckFunc
) (gpointer
*gtkaspell
);
112 GtkAspeller
*gtkaspeller
;
113 GtkAspeller
*alternate_speller
;
114 gchar
*dictionary_path
;
115 gchar theword
[GTKASPELLWORDSIZE
];
121 gboolean check_while_typing
;
122 gboolean use_alternate
;
124 ContCheckFunc continue_check
;
126 GtkWidget
*config_menu
;
127 GtkWidget
*popup_config_menu
;
129 GtkWidget
*replace_entry
;
131 gint default_sug_mode
;
133 GList
*suggestions_list
;
139 typedef AspellConfig GtkAspellConfig
;
141 /******************************************************************************/
143 static GtkAspellCheckers
*gtkaspellcheckers
;
145 /* Error message storage */
146 static void gtkaspell_checkers_error_message (gchar
*message
);
149 static void entry_insert_cb (GtkSText
*gtktext
,
153 GtkAspell
*gtkaspell
);
154 static void entry_delete_cb (GtkSText
*gtktext
,
157 GtkAspell
*gtkaspell
);
158 static gint
button_press_intercept_cb (GtkSText
*gtktext
,
160 GtkAspell
*gtkaspell
);
162 /* Checker creation */
163 static GtkAspeller
* gtkaspeller_new (Dictionary
*dict
);
164 static GtkAspeller
* gtkaspeller_real_new (Dictionary
*dict
);
165 static GtkAspeller
* gtkaspeller_delete (GtkAspeller
*gtkaspeller
);
166 static GtkAspeller
* gtkaspeller_real_delete (GtkAspeller
*gtkaspeller
);
168 /* Checker configuration */
169 static gint
set_dictionary (AspellConfig
*config
,
171 static void set_sug_mode_cb (GtkMenuItem
*w
,
172 GtkAspell
*gtkaspell
);
173 static void set_real_sug_mode (GtkAspell
*gtkaspell
,
174 const char *themode
);
176 /* Checker actions */
177 static gboolean
check_at (GtkAspell
*gtkaspell
,
179 static gboolean
check_next_prev (GtkAspell
*gtkaspell
,
181 static GList
* misspelled_suggest (GtkAspell
*gtkaspell
,
183 static void add_word_to_session_cb (GtkWidget
*w
,
185 static void add_word_to_personal_cb (GtkWidget
*w
,
187 static void replace_with_create_dialog_cb (GtkWidget
*w
,
189 static void replace_with_supplied_word_cb (GtkWidget
*w
,
190 GtkAspell
*gtkaspell
);
191 static void replace_word_cb (GtkWidget
*w
,
193 static void replace_real_word (GtkAspell
*gtkaspell
,
195 static void check_with_alternate_cb (GtkWidget
*w
,
197 static void use_alternate_dict (GtkAspell
*gtkaspell
);
198 static void toggle_check_while_typing_cb (GtkWidget
*w
,
202 static void popup_menu (GtkAspell
*gtkaspell
,
204 static GtkMenu
* make_sug_menu (GtkAspell
*gtkaspell
);
205 static void populate_submenu (GtkAspell
*gtkaspell
,
207 static GtkMenu
* make_config_menu (GtkAspell
*gtkaspell
);
208 static void set_menu_pos (GtkMenu
*menu
,
212 /* Other menu callbacks */
213 static gboolean
cancel_menu_cb (GtkMenuShell
*w
,
215 static void change_dict_cb (GtkWidget
*w
,
216 GtkAspell
*gtkaspell
);
217 static void switch_to_alternate_cb (GtkWidget
*w
,
220 /* Misc. helper functions */
221 static void set_point_continue (GtkAspell
*gtkaspell
);
222 static void continue_check (gpointer
*gtkaspell
);
223 static gboolean
iswordsep (unsigned char c
);
224 static guchar
get_text_index_whar (GtkAspell
*gtkaspell
,
226 static gboolean
get_word_from_pos (GtkAspell
*gtkaspell
,
232 static void allocate_color (GtkAspell
*gtkaspell
,
234 static void change_color (GtkAspell
*gtkaspell
,
239 static guchar
* convert_to_aspell_encoding (const guchar
*encoding
);
240 static gint
compare_dict (Dictionary
*a
,
242 static void dictionary_delete (Dictionary
*dict
);
243 static Dictionary
* dictionary_dup (const Dictionary
*dict
);
244 static void free_suggestions_list (GtkAspell
*gtkaspell
);
245 static void reset_theword_data (GtkAspell
*gtkaspell
);
246 static void free_checkers (gpointer elt
,
248 static gint
find_gtkaspeller (gconstpointer aa
,
250 static void gtkaspell_alert_dialog (gchar
*message
);
251 /* gtkspellconfig - only one config per session */
252 GtkAspellConfig
* gtkaspellconfig
;
254 /******************************************************************************/
256 void gtkaspell_checkers_init(void)
258 gtkaspellcheckers
= g_new(GtkAspellCheckers
, 1);
259 gtkaspellcheckers
->checkers
= NULL
;
260 gtkaspellcheckers
->dictionary_list
= NULL
;
261 gtkaspellcheckers
->error_message
= NULL
;
264 void gtkaspell_checkers_quit(void)
269 if (gtkaspellcheckers
== NULL
)
272 if ((checkers
= gtkaspellcheckers
->checkers
)) {
273 debug_print("Aspell: number of running checkers to delete %d\n",
274 g_slist_length(checkers
));
276 g_slist_foreach(checkers
, free_checkers
, NULL
);
277 g_slist_free(checkers
);
280 if ((dict_list
= gtkaspellcheckers
->dictionary_list
)) {
281 debug_print("Aspell: number of dictionaries to delete %d\n",
282 g_slist_length(dict_list
));
284 gtkaspell_free_dictionary_list(dict_list
);
285 gtkaspellcheckers
->dictionary_list
= NULL
;
288 g_free(gtkaspellcheckers
->error_message
);
293 static void gtkaspell_checkers_error_message (gchar
*message
)
296 if (gtkaspellcheckers
->error_message
) {
297 tmp
= g_strdup_printf("%s\n%s",
298 gtkaspellcheckers
->error_message
, message
);
300 g_free(gtkaspellcheckers
->error_message
);
301 gtkaspellcheckers
->error_message
= tmp
;
303 gtkaspellcheckers
->error_message
= message
;
306 const char *gtkaspell_checkers_strerror(void)
308 g_return_val_if_fail(gtkaspellcheckers
, "");
309 return gtkaspellcheckers
->error_message
;
312 void gtkaspell_checkers_reset_error(void)
314 g_return_if_fail(gtkaspellcheckers
);
316 g_free(gtkaspellcheckers
->error_message
);
318 gtkaspellcheckers
->error_message
= NULL
;
321 GtkAspell
*gtkaspell_new(const gchar
*dictionary_path
,
322 const gchar
*dictionary
,
323 const gchar
*encoding
,
324 gint misspelled_color
,
325 gboolean check_while_typing
,
326 gboolean use_alternate
,
330 GtkAspell
*gtkaspell
;
331 GtkAspeller
*gtkaspeller
;
333 g_return_val_if_fail(gtktext
, NULL
);
335 dict
= g_new0(Dictionary
, 1);
336 dict
->fullname
= g_strdup(dictionary
);
337 dict
->encoding
= g_strdup(encoding
);
339 gtkaspeller
= gtkaspeller_new(dict
);
340 dictionary_delete(dict
);
345 gtkaspell
= g_new0(GtkAspell
, 1);
347 gtkaspell
->dictionary_path
= g_strdup(dictionary_path
);
349 gtkaspell
->gtkaspeller
= gtkaspeller
;
350 gtkaspell
->alternate_speller
= NULL
;
351 gtkaspell
->theword
[0] = 0x00;
352 gtkaspell
->start_pos
= 0;
353 gtkaspell
->end_pos
= 0;
354 gtkaspell
->orig_pos
= -1;
355 gtkaspell
->end_check_pos
= -1;
356 gtkaspell
->misspelled
= -1;
357 gtkaspell
->check_while_typing
= check_while_typing
;
358 gtkaspell
->continue_check
= NULL
;
359 gtkaspell
->config_menu
= NULL
;
360 gtkaspell
->popup_config_menu
= NULL
;
361 gtkaspell
->sug_menu
= NULL
;
362 gtkaspell
->replace_entry
= NULL
;
363 gtkaspell
->gtktext
= gtktext
;
364 gtkaspell
->default_sug_mode
= ASPELL_FASTMODE
;
365 gtkaspell
->max_sug
= -1;
366 gtkaspell
->suggestions_list
= NULL
;
367 gtkaspell
->use_alternate
= use_alternate
;
369 allocate_color(gtkaspell
, misspelled_color
);
371 gtk_signal_connect_after(GTK_OBJECT(gtktext
), "insert-text",
372 GTK_SIGNAL_FUNC(entry_insert_cb
), gtkaspell
);
373 gtk_signal_connect_after(GTK_OBJECT(gtktext
), "delete-text",
374 GTK_SIGNAL_FUNC(entry_delete_cb
), gtkaspell
);
375 gtk_signal_connect(GTK_OBJECT(gtktext
), "button-press-event",
376 GTK_SIGNAL_FUNC(button_press_intercept_cb
), gtkaspell
);
378 debug_print("Aspell: created gtkaspell %0x\n", (guint
) gtkaspell
);
383 void gtkaspell_delete(GtkAspell
* gtkaspell
)
385 GtkSText
*gtktext
= gtkaspell
->gtktext
;
387 gtk_signal_disconnect_by_func(GTK_OBJECT(gtktext
),
388 GTK_SIGNAL_FUNC(entry_insert_cb
),
390 gtk_signal_disconnect_by_func(GTK_OBJECT(gtktext
),
391 GTK_SIGNAL_FUNC(entry_delete_cb
),
393 gtk_signal_disconnect_by_func(GTK_OBJECT(gtktext
),
394 GTK_SIGNAL_FUNC(button_press_intercept_cb
),
397 gtkaspell_uncheck_all(gtkaspell
);
399 gtkaspeller_delete(gtkaspell
->gtkaspeller
);
401 if (gtkaspell
->use_alternate
&& gtkaspell
->alternate_speller
)
402 gtkaspeller_delete(gtkaspell
->alternate_speller
);
404 if (gtkaspell
->sug_menu
)
405 gtk_widget_destroy(gtkaspell
->sug_menu
);
407 if (gtkaspell
->popup_config_menu
)
408 gtk_widget_destroy(gtkaspell
->popup_config_menu
);
410 if (gtkaspell
->config_menu
)
411 gtk_widget_destroy(gtkaspell
->config_menu
);
413 if (gtkaspell
->suggestions_list
)
414 free_suggestions_list(gtkaspell
);
416 g_free((gchar
*)gtkaspell
->dictionary_path
);
418 debug_print("Aspell: deleting gtkaspell %0x\n", (guint
) gtkaspell
);
425 static void entry_insert_cb(GtkSText
*gtktext
,
429 GtkAspell
*gtkaspell
)
433 g_return_if_fail(gtkaspell
->gtkaspeller
->checker
);
435 if (!gtkaspell
->check_while_typing
)
438 /* We must insert ourselves the character so the
439 * color of the inserted character is the default color.
440 * Never mess with set_insertion when frozen.
443 gtk_stext_freeze(gtktext
);
444 if (MB_CUR_MAX
> 1) {
446 Xstrndup_a(str
, newtext
, len
, return);
447 wlen
= mbstowcs(NULL
, str
, 0);
453 gtk_stext_backward_delete(GTK_STEXT(gtktext
), wlen
);
454 gtk_stext_insert(GTK_STEXT(gtktext
), NULL
, NULL
, NULL
, newtext
, len
);
455 *ppos
= gtk_stext_get_point(GTK_STEXT(gtktext
));
457 if (iswordsep(newtext
[0])) {
458 /* did we just end a word? */
460 check_at(gtkaspell
, *ppos
- 2);
462 /* did we just split a word? */
463 if (*ppos
< gtk_stext_get_length(gtktext
))
464 check_at(gtkaspell
, *ppos
+ 1);
466 /* check as they type, *except* if they're typing at the end (the most
469 if (*ppos
< gtk_stext_get_length(gtktext
) &&
470 !iswordsep(get_text_index_whar(gtkaspell
, *ppos
))) {
471 check_at(gtkaspell
, *ppos
- 1);
475 gtk_stext_thaw(gtktext
);
476 gtk_editable_set_position(GTK_EDITABLE(gtktext
), *ppos
);
479 static void entry_delete_cb(GtkSText
*gtktext
,
482 GtkAspell
*gtkaspell
)
486 g_return_if_fail(gtkaspell
->gtkaspeller
->checker
);
488 if (!gtkaspell
->check_while_typing
)
491 origpos
= gtk_editable_get_position(GTK_EDITABLE(gtktext
));
493 check_at(gtkaspell
, start
- 1);
494 check_at(gtkaspell
, start
);
497 gtk_editable_set_position(GTK_EDITABLE(gtktext
), origpos
);
498 gtk_stext_set_point(gtktext
, origpos
);
499 /* this is to *UNDO* the selection, in case they were holding shift
500 * while hitting backspace. */
501 gtk_editable_select_region(GTK_EDITABLE(gtktext
), origpos
, origpos
);
504 /* ok, this is pretty wacky:
505 * we need to let the right-mouse-click go through, so it moves the cursor,
506 * but we *can't* let it go through, because GtkText interprets rightclicks as
507 * weird selection modifiers.
509 * so what do we do? forge rightclicks as leftclicks, then popup the menu.
512 static gint
button_press_intercept_cb(GtkSText
*gtktext
, GdkEvent
*e
, GtkAspell
*gtkaspell
)
517 g_return_val_if_fail(gtkaspell
->gtkaspeller
->checker
, FALSE
);
519 if (e
->type
!= GDK_BUTTON_PRESS
)
521 eb
= (GdkEventButton
*) e
;
526 /* forge the leftclick */
529 gtk_signal_handler_block_by_func(GTK_OBJECT(gtktext
),
530 GTK_SIGNAL_FUNC(button_press_intercept_cb
),
532 gtk_signal_emit_by_name(GTK_OBJECT(gtktext
), "button-press-event",
534 gtk_signal_handler_unblock_by_func(GTK_OBJECT(gtktext
),
535 GTK_SIGNAL_FUNC(button_press_intercept_cb
),
537 gtk_signal_emit_stop_by_name(GTK_OBJECT(gtktext
), "button-press-event");
539 /* now do the menu wackiness */
540 popup_menu(gtkaspell
, eb
);
541 gtk_grab_remove(GTK_WIDGET(gtktext
));
545 /* Checker creation */
546 static GtkAspeller
*gtkaspeller_new(Dictionary
*dictionary
)
549 GtkAspeller
*gtkaspeller
= NULL
;
553 g_return_val_if_fail(gtkaspellcheckers
, NULL
);
555 g_return_val_if_fail(dictionary
, NULL
);
557 if (dictionary
->fullname
== NULL
)
558 gtkaspell_checkers_error_message(g_strdup(_("No dictionary selected.")));
560 g_return_val_if_fail(dictionary
->fullname
, NULL
);
562 if (dictionary
->dictname
== NULL
) {
565 tmp
= strrchr(dictionary
->fullname
, G_DIR_SEPARATOR
);
568 dictionary
->dictname
= dictionary
->fullname
;
570 dictionary
->dictname
= tmp
+ 1;
573 dict
= dictionary_dup(dictionary
);
575 tmp
= g_new0(GtkAspeller
, 1);
576 tmp
->dictionary
= dict
;
578 exist
= g_slist_find_custom(gtkaspellcheckers
->checkers
, tmp
,
583 if ((gtkaspeller
= gtkaspeller_real_new(dict
)) != NULL
) {
584 gtkaspellcheckers
->checkers
= g_slist_append(
585 gtkaspellcheckers
->checkers
,
588 debug_print("Aspell: Created a new gtkaspeller %0x\n",
591 dictionary_delete(dict
);
593 debug_print("Aspell: Could not create spell checker.\n");
596 debug_print("Aspell: number of existing checkers %d\n",
597 g_slist_length(gtkaspellcheckers
->checkers
));
602 static GtkAspeller
*gtkaspeller_real_new(Dictionary
*dict
)
604 GtkAspeller
*gtkaspeller
;
605 AspellConfig
*config
;
606 AspellCanHaveError
*ret
;
608 g_return_val_if_fail(gtkaspellcheckers
, NULL
);
609 g_return_val_if_fail(dict
, NULL
);
611 gtkaspeller
= g_new(GtkAspeller
, 1);
613 gtkaspeller
->dictionary
= dict
;
614 gtkaspeller
->sug_mode
= ASPELL_FASTMODE
;
616 config
= new_aspell_config();
618 if (!set_dictionary(config
, dict
))
621 ret
= new_aspell_speller(config
);
622 delete_aspell_config(config
);
624 if (aspell_error_number(ret
) != 0) {
625 gtkaspellcheckers
->error_message
= g_strdup(aspell_error_message(ret
));
627 delete_aspell_can_have_error(ret
);
632 gtkaspeller
->checker
= to_aspell_speller(ret
);
633 gtkaspeller
->config
= aspell_speller_config(gtkaspeller
->checker
);
638 static GtkAspeller
*gtkaspeller_delete(GtkAspeller
*gtkaspeller
)
640 g_return_val_if_fail(gtkaspellcheckers
, NULL
);
642 gtkaspellcheckers
->checkers
=
643 g_slist_remove(gtkaspellcheckers
->checkers
,
646 debug_print("Aspell: Deleting gtkaspeller %0x.\n",
649 gtkaspeller_real_delete(gtkaspeller
);
651 debug_print("Aspell: number of existing checkers %d\n",
652 g_slist_length(gtkaspellcheckers
->checkers
));
657 static GtkAspeller
*gtkaspeller_real_delete(GtkAspeller
*gtkaspeller
)
659 g_return_val_if_fail(gtkaspeller
, NULL
);
660 g_return_val_if_fail(gtkaspeller
->checker
, NULL
);
662 aspell_speller_save_all_word_lists(gtkaspeller
->checker
);
664 delete_aspell_speller(gtkaspeller
->checker
);
666 dictionary_delete(gtkaspeller
->dictionary
);
668 debug_print("Aspell: gtkaspeller %0x deleted.\n",
676 /*****************************************************************************/
677 /* Checker configuration */
679 static gboolean
set_dictionary(AspellConfig
*config
, Dictionary
*dict
)
681 gchar
*language
= NULL
;
682 gchar
*jargon
= NULL
;
686 g_return_val_if_fail(config
, FALSE
);
687 g_return_val_if_fail(dict
, FALSE
);
689 strncpy(buf
, dict
->fullname
, BUFSIZE
-1);
690 buf
[BUFSIZE
-1] = 0x00;
692 buf
[dict
->dictname
- dict
->fullname
] = 0x00;
694 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("dict-dir", buf
);
695 debug_print("Aspell: looking for dictionaries in path %s.\n", buf
);
697 strncpy(buf
, dict
->dictname
, BUFSIZE
-1);
700 if ((size
= strrchr(buf
, '-')) && isdigit((int) size
[1]))
705 if ((jargon
= strchr(language
, '-')) != NULL
)
708 if (size
!= NULL
&& jargon
== size
)
711 debug_print("Aspell: language: %s, jargon: %s, size: %s\n",
712 language
, jargon
, size
);
715 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("lang", language
);
717 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("jargon", jargon
);
719 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("size", size
);
720 if (dict
->encoding
) {
723 aspell_enc
= convert_to_aspell_encoding (dict
->encoding
);
724 aspell_config_replace(config
, "encoding", (const char *) aspell_enc
);
727 RETURN_FALSE_IF_CONFIG_ERROR();
733 guchar
*gtkaspell_get_dict(GtkAspell
*gtkaspell
)
736 g_return_val_if_fail(gtkaspell
->gtkaspeller
->config
, NULL
);
737 g_return_val_if_fail(gtkaspell
->gtkaspeller
->dictionary
, NULL
);
739 return g_strdup(gtkaspell
->gtkaspeller
->dictionary
->dictname
);
742 guchar
*gtkaspell_get_path(GtkAspell
*gtkaspell
)
747 g_return_val_if_fail(gtkaspell
->gtkaspeller
->config
, NULL
);
748 g_return_val_if_fail(gtkaspell
->gtkaspeller
->dictionary
, NULL
);
750 dict
= gtkaspell
->gtkaspeller
->dictionary
;
751 path
= g_strndup(dict
->fullname
, dict
->dictname
- dict
->fullname
);
756 /* set_sug_mode_cb() - Menu callback: Set the suggestion mode */
757 static void set_sug_mode_cb(GtkMenuItem
*w
, GtkAspell
*gtkaspell
)
761 gtk_label_get(GTK_LABEL(GTK_BIN(w
)->child
), (gchar
**) &themode
);
763 set_real_sug_mode(gtkaspell
, themode
);
765 if (gtkaspell
->config_menu
)
766 populate_submenu(gtkaspell
, gtkaspell
->config_menu
);
769 static void set_real_sug_mode(GtkAspell
*gtkaspell
, const char *themode
)
772 gint mode
= ASPELL_FASTMODE
;
773 g_return_if_fail(gtkaspell
);
774 g_return_if_fail(gtkaspell
->gtkaspeller
);
775 g_return_if_fail(themode
);
777 if (!strcmp(themode
,_("Normal Mode")))
778 mode
= ASPELL_NORMALMODE
;
779 else if (!strcmp( themode
,_("Bad Spellers Mode")))
780 mode
= ASPELL_BADSPELLERMODE
;
782 result
= gtkaspell_set_sug_mode(gtkaspell
, mode
);
785 debug_print("Aspell: error while changing suggestion mode:%s\n",
786 gtkaspellcheckers
->error_message
);
787 gtkaspell_checkers_reset_error();
791 /* gtkaspell_set_sug_mode() - Set the suggestion mode */
792 gboolean
gtkaspell_set_sug_mode(GtkAspell
*gtkaspell
, gint themode
)
794 AspellConfig
*config
;
796 g_return_val_if_fail(gtkaspell
, FALSE
);
797 g_return_val_if_fail(gtkaspell
->gtkaspeller
, FALSE
);
798 g_return_val_if_fail(gtkaspell
->gtkaspeller
->config
, FALSE
);
800 debug_print("Aspell: setting sug mode of gtkaspeller %0x to %d\n",
801 (guint
) gtkaspell
->gtkaspeller
, themode
);
803 config
= gtkaspell
->gtkaspeller
->config
;
806 case ASPELL_FASTMODE
:
807 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("sug-mode", "fast");
809 case ASPELL_NORMALMODE
:
810 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("sug-mode", "normal");
812 case ASPELL_BADSPELLERMODE
:
813 CONFIG_REPLACE_RETURN_FALSE_IF_FAIL("sug-mode",
817 gtkaspellcheckers
->error_message
=
818 g_strdup(_("Unknown suggestion mode."));
822 gtkaspell
->gtkaspeller
->sug_mode
= themode
;
823 gtkaspell
->default_sug_mode
= themode
;
828 /* misspelled_suggest() - Create a suggestion list for word */
829 static GList
*misspelled_suggest(GtkAspell
*gtkaspell
, guchar
*word
)
831 const guchar
*newword
;
833 const AspellWordList
*suggestions
;
834 AspellStringEnumeration
*elements
;
836 g_return_val_if_fail(word
, NULL
);
838 if (!aspell_speller_check(gtkaspell
->gtkaspeller
->checker
, word
, -1)) {
839 free_suggestions_list(gtkaspell
);
841 suggestions
= aspell_speller_suggest(gtkaspell
->gtkaspeller
->checker
,
842 (const char *)word
, -1);
843 elements
= aspell_word_list_elements(suggestions
);
844 list
= g_list_append(list
, g_strdup(word
));
846 while ((newword
= aspell_string_enumeration_next(elements
)) != NULL
)
847 list
= g_list_append(list
, g_strdup(newword
));
849 gtkaspell
->max_sug
= g_list_length(list
) - 1;
850 gtkaspell
->suggestions_list
= list
;
855 free_suggestions_list(gtkaspell
);
860 /* misspelled_test() - Just test if word is correctly spelled */
861 static int misspelled_test(GtkAspell
*gtkaspell
, unsigned char *word
)
863 return aspell_speller_check(gtkaspell
->gtkaspeller
->checker
, word
, -1) ? 0 : 1;
867 static gboolean
iswordsep(unsigned char c
)
869 return !isalpha(c
) && c
!= '\'';
872 static guchar
get_text_index_whar(GtkAspell
*gtkaspell
, int pos
)
877 text
= gtk_editable_get_chars(GTK_EDITABLE(gtkaspell
->gtktext
), pos
,
889 /* get_word_from_pos () - return the word pointed to. */
890 /* Handles correctly the quotes. */
891 static gboolean
get_word_from_pos(GtkAspell
*gtkaspell
, gint pos
,
892 unsigned char* buf
, gint buflen
,
893 gint
*pstart
, gint
*pend
)
896 /* TODO : when correcting a word into quotes, change the color of */
897 /* the quotes too, as may be they were highlighted before. To do */
898 /* this, we can use two others pointers that points to the whole */
899 /* word including quotes. */
907 gtktext
= gtkaspell
->gtktext
;
908 if (iswordsep(get_text_index_whar(gtkaspell
, pos
)))
911 /* The apostrophe character is somtimes used for quotes
912 * So include it in the word only if it is not surrounded
913 * by other characters.
916 for (start
= pos
; start
>= 0; --start
) {
917 c
= get_text_index_whar(gtkaspell
, start
);
920 if (!isalpha(get_text_index_whar(gtkaspell
,
922 /* start_quote = TRUE; */
927 /* start_quote = TRUE; */
931 else if (!isalpha(c
))
937 for (end
= pos
; end
< gtk_stext_get_length(gtktext
); end
++) {
938 c
= get_text_index_whar(gtkaspell
, end
);
940 if (end
< gtk_stext_get_length(gtktext
)) {
941 if (!isalpha(get_text_index_whar(gtkaspell
,
943 /* end_quote = TRUE; */
948 /* end_quote = TRUE; */
962 if (end
- start
< buflen
) {
963 for (pos
= start
; pos
< end
; pos
++)
965 get_text_index_whar(gtkaspell
, pos
);
966 buf
[pos
- start
] = 0;
974 static gboolean
check_at(GtkAspell
*gtkaspell
, gint from_pos
)
977 unsigned char buf
[GTKASPELLWORDSIZE
];
980 g_return_val_if_fail(from_pos
>= 0, FALSE
);
982 gtktext
= gtkaspell
->gtktext
;
984 if (!get_word_from_pos(gtkaspell
, from_pos
, buf
, sizeof(buf
),
988 if (misspelled_test(gtkaspell
, buf
)) {
989 strncpy(gtkaspell
->theword
, buf
, GTKASPELLWORDSIZE
- 1);
990 gtkaspell
->theword
[GTKASPELLWORDSIZE
- 1] = 0;
991 gtkaspell
->start_pos
= start
;
992 gtkaspell
->end_pos
= end
;
993 free_suggestions_list(gtkaspell
);
995 change_color(gtkaspell
, start
, end
, buf
, &(gtkaspell
->highlight
));
998 change_color(gtkaspell
, start
, end
, buf
, NULL
);
1003 static gboolean
check_next_prev(GtkAspell
*gtkaspell
, gboolean forward
)
1009 gboolean misspelled
;
1012 maxpos
= gtkaspell
->end_check_pos
;
1020 pos
= gtk_editable_get_position(GTK_EDITABLE(gtkaspell
->gtktext
));
1021 gtkaspell
->orig_pos
= pos
;
1022 while (iswordsep(get_text_index_whar(gtkaspell
, pos
)) &&
1023 pos
> minpos
&& pos
<= maxpos
)
1025 while (!(misspelled
= check_at(gtkaspell
, pos
)) &&
1026 pos
> minpos
&& pos
<= maxpos
) {
1028 while (!iswordsep(get_text_index_whar(gtkaspell
, pos
)) &&
1029 pos
> minpos
&& pos
<= maxpos
)
1032 while (iswordsep(get_text_index_whar(gtkaspell
, pos
)) &&
1033 pos
> minpos
&& pos
<= maxpos
)
1037 misspelled_suggest(gtkaspell
, gtkaspell
->theword
);
1040 gtkaspell
->orig_pos
= gtkaspell
->end_pos
;
1042 gtk_stext_set_point(GTK_STEXT(gtkaspell
->gtktext
),
1043 gtkaspell
->end_pos
);
1044 gtk_editable_set_position(GTK_EDITABLE(gtkaspell
->gtktext
),
1045 gtkaspell
->end_pos
);
1046 gtk_menu_popup(make_sug_menu(gtkaspell
), NULL
, NULL
,
1047 set_menu_pos
, gtkaspell
, 0, GDK_CURRENT_TIME
);
1049 reset_theword_data(gtkaspell
);
1051 gtkaspell_alert_dialog(_("No misspelled word found."));
1052 gtk_stext_set_point(GTK_STEXT(gtkaspell
->gtktext
),
1053 gtkaspell
->orig_pos
);
1054 gtk_editable_set_position(GTK_EDITABLE(gtkaspell
->gtktext
),
1055 gtkaspell
->orig_pos
);
1062 void gtkaspell_check_backwards(GtkAspell
*gtkaspell
)
1064 gtkaspell
->continue_check
= NULL
;
1065 gtkaspell
->end_check_pos
=
1066 gtk_stext_get_length(GTK_STEXT(gtkaspell
->gtktext
));
1067 check_next_prev(gtkaspell
, FALSE
);
1070 void gtkaspell_check_forwards_go(GtkAspell
*gtkaspell
)
1073 gtkaspell
->continue_check
= NULL
;
1074 gtkaspell
->end_check_pos
1075 = gtk_stext_get_length(GTK_STEXT(gtkaspell
->gtktext
));
1076 check_next_prev(gtkaspell
, TRUE
);
1079 void gtkaspell_check_all(GtkAspell
*gtkaspell
)
1084 g_return_if_fail(gtkaspell
);
1085 g_return_if_fail(gtkaspell
->gtktext
);
1087 gtktext
= (GtkWidget
*) gtkaspell
->gtktext
;
1090 end
= gtk_stext_get_length(GTK_STEXT(gtktext
));
1092 if (GTK_EDITABLE(gtktext
)->has_selection
) {
1093 start
= GTK_EDITABLE(gtktext
)->selection_start_pos
;
1094 end
= GTK_EDITABLE(gtktext
)->selection_end_pos
;
1106 gtk_editable_set_position(GTK_EDITABLE(gtktext
), start
);
1107 gtk_stext_set_point(GTK_STEXT(gtktext
), start
);
1109 gtkaspell
->continue_check
= continue_check
;
1110 gtkaspell
->end_check_pos
= end
;
1112 gtkaspell
->misspelled
= check_next_prev(gtkaspell
, TRUE
);
1116 static void continue_check(gpointer
*data
)
1118 GtkAspell
*gtkaspell
= (GtkAspell
*) data
;
1119 gint pos
= gtk_editable_get_position(GTK_EDITABLE(gtkaspell
->gtktext
));
1120 if (pos
< gtkaspell
->end_check_pos
&& gtkaspell
->misspelled
)
1121 gtkaspell
->misspelled
= check_next_prev(gtkaspell
, TRUE
);
1123 gtkaspell
->continue_check
= NULL
;
1127 void gtkaspell_highlight_all(GtkAspell
*gtkaspell
)
1135 g_return_if_fail(gtkaspell
->gtkaspeller
->checker
);
1137 gtktext
= gtkaspell
->gtktext
;
1139 adj_value
= gtktext
->vadj
->value
;
1141 len
= gtk_stext_get_length(gtktext
);
1143 origpos
= gtk_editable_get_position(GTK_EDITABLE(gtktext
));
1147 iswordsep(get_text_index_whar(gtkaspell
, pos
)))
1150 !iswordsep(get_text_index_whar(gtkaspell
, pos
)))
1153 check_at(gtkaspell
, pos
- 1);
1155 gtk_editable_set_position(GTK_EDITABLE(gtktext
), origpos
);
1156 gtk_stext_set_point(GTK_STEXT(gtktext
), origpos
);
1157 gtk_adjustment_set_value(gtktext
->vadj
, adj_value
);
1160 static void replace_with_supplied_word_cb(GtkWidget
*w
, GtkAspell
*gtkaspell
)
1162 unsigned char *newword
;
1163 GdkEvent
*e
= (GdkEvent
*) gtk_get_current_event();
1165 newword
= gtk_editable_get_chars(GTK_EDITABLE(gtkaspell
->replace_entry
),
1168 if (strcmp(newword
, gtkaspell
->theword
)) {
1169 replace_real_word(gtkaspell
, newword
);
1171 if ((e
->type
== GDK_KEY_PRESS
&&
1172 ((GdkEventKey
*) e
)->state
& GDK_MOD1_MASK
)) {
1173 aspell_speller_store_replacement(gtkaspell
->gtkaspeller
->checker
,
1174 gtkaspell
->theword
, -1,
1177 gtkaspell
->replace_entry
= NULL
;
1182 set_point_continue(gtkaspell
);
1186 static void replace_word_cb(GtkWidget
*w
, gpointer data
)
1188 unsigned char *newword
;
1189 GtkAspell
*gtkaspell
= (GtkAspell
*) data
;
1190 GdkEvent
*e
= (GdkEvent
*) gtk_get_current_event();
1192 gtk_label_get(GTK_LABEL(GTK_BIN(w
)->child
), (gchar
**) &newword
);
1194 replace_real_word(gtkaspell
, newword
);
1196 if ((e
->type
== GDK_KEY_PRESS
&&
1197 ((GdkEventKey
*) e
)->state
& GDK_MOD1_MASK
) ||
1198 (e
->type
== GDK_BUTTON_RELEASE
&&
1199 ((GdkEventButton
*) e
)->state
& GDK_MOD1_MASK
)) {
1200 aspell_speller_store_replacement(gtkaspell
->gtkaspeller
->checker
,
1201 gtkaspell
->theword
, -1,
1205 gtk_menu_shell_deactivate(GTK_MENU_SHELL(w
->parent
));
1207 set_point_continue(gtkaspell
);
1210 static void replace_real_word(GtkAspell
*gtkaspell
, gchar
*newword
)
1212 int oldlen
, newlen
, wordlen
;
1215 gint start
= gtkaspell
->start_pos
;
1218 if (!newword
) return;
1220 gtktext
= gtkaspell
->gtktext
;
1222 gtk_stext_freeze(GTK_STEXT(gtktext
));
1223 origpos
= gtkaspell
->orig_pos
;
1225 oldlen
= gtkaspell
->end_pos
- gtkaspell
->start_pos
;
1226 wordlen
= strlen(gtkaspell
->theword
);
1228 newlen
= strlen(newword
); /* FIXME: multybyte characters? */
1230 gtk_signal_handler_block_by_func(GTK_OBJECT(gtktext
),
1231 GTK_SIGNAL_FUNC(entry_insert_cb
),
1233 gtk_signal_handler_block_by_func(GTK_OBJECT(gtktext
),
1234 GTK_SIGNAL_FUNC(entry_delete_cb
),
1237 gtk_signal_emit_by_name(GTK_OBJECT(gtktext
), "delete-text",
1238 gtkaspell
->start_pos
, gtkaspell
->end_pos
);
1239 gtk_signal_emit_by_name(GTK_OBJECT(gtktext
), "insert-text",
1240 newword
, newlen
, &start
);
1242 gtk_signal_handler_unblock_by_func(GTK_OBJECT(gtktext
),
1243 GTK_SIGNAL_FUNC(entry_insert_cb
),
1245 gtk_signal_handler_unblock_by_func(GTK_OBJECT(gtktext
),
1246 GTK_SIGNAL_FUNC(entry_delete_cb
),
1249 /* Put the point and the position where we clicked with the mouse
1250 * It seems to be a hack, as I must thaw,freeze,thaw the widget
1251 * to let it update correctly the word insertion and then the
1252 * point & position position. If not, SEGV after the first replacement
1253 * If the new word ends before point, put the point at its end.
1256 if (origpos
- gtkaspell
->start_pos
< oldlen
&&
1257 origpos
- gtkaspell
->start_pos
>= 0) {
1258 /* Original point was in the word.
1259 * Let it there unless point is going to be outside of the word
1261 if (origpos
- gtkaspell
->start_pos
>= newlen
) {
1262 pos
= gtkaspell
->start_pos
+ newlen
;
1265 else if (origpos
>= gtkaspell
->end_pos
) {
1266 /* move the position according to the change of length */
1267 pos
= origpos
+ newlen
- oldlen
;
1270 gtkaspell
->end_pos
= gtkaspell
->start_pos
+ strlen(newword
); /* FIXME: multibyte characters? */
1272 gtk_stext_thaw(GTK_STEXT(gtktext
));
1273 gtk_stext_freeze(GTK_STEXT(gtktext
));
1275 if (GTK_STEXT(gtktext
)->text_len
< pos
)
1276 pos
= gtk_stext_get_length(GTK_STEXT(gtktext
));
1278 gtkaspell
->orig_pos
= pos
;
1280 gtk_editable_set_position(GTK_EDITABLE(gtktext
), gtkaspell
->orig_pos
);
1281 gtk_stext_set_point(GTK_STEXT(gtktext
),
1282 gtk_editable_get_position(GTK_EDITABLE(gtktext
)));
1284 gtk_stext_thaw(GTK_STEXT(gtktext
));
1287 /* Accept this word for this session */
1288 static void add_word_to_session_cb(GtkWidget
*w
, gpointer data
)
1292 GtkAspell
*gtkaspell
= (GtkAspell
*) data
;
1293 gtktext
= gtkaspell
->gtktext
;
1295 gtk_stext_freeze(GTK_STEXT(gtktext
));
1297 pos
= gtk_editable_get_position(GTK_EDITABLE(gtktext
));
1299 aspell_speller_add_to_session(gtkaspell
->gtkaspeller
->checker
,
1301 strlen(gtkaspell
->theword
));
1303 check_at(gtkaspell
, gtkaspell
->start_pos
);
1305 gtk_stext_thaw(gtkaspell
->gtktext
);
1307 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w
)->parent
));
1309 set_point_continue(gtkaspell
);
1312 /* add_word_to_personal_cb() - add word to personal dict. */
1313 static void add_word_to_personal_cb(GtkWidget
*w
, gpointer data
)
1315 GtkAspell
*gtkaspell
= (GtkAspell
*) data
;
1316 GtkSText
*gtktext
= gtkaspell
->gtktext
;
1318 gtk_stext_freeze(GTK_STEXT(gtktext
));
1320 aspell_speller_add_to_personal(gtkaspell
->gtkaspeller
->checker
,
1322 strlen(gtkaspell
->theword
));
1324 check_at(gtkaspell
, gtkaspell
->start_pos
);
1326 gtk_stext_thaw(gtkaspell
->gtktext
);
1328 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w
)->parent
));
1329 set_point_continue(gtkaspell
);
1332 static void check_with_alternate_cb(GtkWidget
*w
, gpointer data
)
1334 GtkAspell
*gtkaspell
= (GtkAspell
*) data
;
1337 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w
)->parent
));
1339 use_alternate_dict(gtkaspell
);
1340 misspelled
= check_at(gtkaspell
, gtkaspell
->start_pos
);
1342 if (!gtkaspell
->continue_check
) {
1344 gtkaspell
->misspelled
= misspelled
;
1346 if (gtkaspell
->misspelled
) {
1348 misspelled_suggest(gtkaspell
, gtkaspell
->theword
);
1350 gtk_stext_set_point(GTK_STEXT(gtkaspell
->gtktext
),
1351 gtkaspell
->end_pos
);
1352 gtk_editable_set_position(GTK_EDITABLE(gtkaspell
->gtktext
),
1353 gtkaspell
->end_pos
);
1355 gtk_menu_popup(make_sug_menu(gtkaspell
), NULL
, NULL
,
1356 set_menu_pos
, gtkaspell
, 0,
1361 gtkaspell
->orig_pos
= gtkaspell
->start_pos
;
1363 set_point_continue(gtkaspell
);
1366 static void replace_with_create_dialog_cb(GtkWidget
*w
, gpointer data
)
1372 GtkWidget
*ok_button
;
1373 GtkWidget
*cancel_button
;
1376 GtkAspell
*gtkaspell
= (GtkAspell
*) data
;
1378 gdk_window_get_origin((GTK_WIDGET(w
)->parent
)->window
, &xx
, &yy
);
1380 gtk_menu_shell_deactivate(GTK_MENU_SHELL(GTK_WIDGET(w
)->parent
));
1382 dialog
= gtk_dialog_new();
1384 gtk_window_set_policy(GTK_WINDOW(dialog
), FALSE
, FALSE
, FALSE
);
1385 gtk_window_set_title(GTK_WINDOW(dialog
),_("Replace unknown word"));
1386 gtk_widget_set_uposition(dialog
, xx
, yy
);
1388 gtk_signal_connect_object(GTK_OBJECT(dialog
), "destroy",
1389 GTK_SIGNAL_FUNC(gtk_widget_destroy
),
1390 GTK_OBJECT(dialog
));
1392 hbox
= gtk_hbox_new(FALSE
, 0);
1393 gtk_container_set_border_width(GTK_CONTAINER(hbox
), 8);
1395 thelabel
= g_strdup_printf(_("Replace \"%s\" with: "),
1396 gtkaspell
->theword
);
1397 label
= gtk_label_new(thelabel
);
1399 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, FALSE
, 0);
1401 entry
= gtk_entry_new();
1402 gtkaspell
->replace_entry
= entry
;
1403 gtk_entry_set_text(GTK_ENTRY(entry
), gtkaspell
->theword
);
1404 gtk_editable_select_region(GTK_EDITABLE(entry
), 0, -1);
1405 gtk_signal_connect(GTK_OBJECT(entry
), "activate",
1406 GTK_SIGNAL_FUNC(replace_with_supplied_word_cb
),
1408 gtk_signal_connect_object(GTK_OBJECT(entry
), "activate",
1409 GTK_SIGNAL_FUNC(gtk_widget_destroy
),
1410 GTK_OBJECT(dialog
));
1411 gtk_box_pack_start(GTK_BOX(hbox
), entry
, TRUE
, TRUE
, 0);
1413 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog
)->vbox
), hbox
, TRUE
,
1415 label
= gtk_label_new(_("Holding down MOD1 key while pressing "
1416 "Enter\nwill learn from mistake.\n"));
1417 gtk_label_set_justify(GTK_LABEL(label
), GTK_JUSTIFY_LEFT
);
1418 gtk_misc_set_alignment(GTK_MISC(label
), 0.0, 0.0);
1419 gtk_misc_set_padding(GTK_MISC(label
), 8, 0);
1420 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog
)->vbox
), label
,
1423 hbox
= gtk_hbox_new(TRUE
, 0);
1425 ok_button
= gtk_button_new_with_label(_("OK"));
1426 gtk_box_pack_start(GTK_BOX(hbox
), ok_button
, TRUE
, TRUE
, 8);
1427 gtk_signal_connect(GTK_OBJECT(ok_button
), "clicked",
1428 GTK_SIGNAL_FUNC(replace_with_supplied_word_cb
),
1430 gtk_signal_connect_object(GTK_OBJECT(ok_button
), "clicked",
1431 GTK_SIGNAL_FUNC(gtk_widget_destroy
),
1432 GTK_OBJECT(dialog
));
1434 cancel_button
= gtk_button_new_with_label(_("Cancel"));
1435 gtk_box_pack_start(GTK_BOX(hbox
), cancel_button
, TRUE
, TRUE
, 8);
1436 gtk_signal_connect_object(GTK_OBJECT(cancel_button
), "clicked",
1437 GTK_SIGNAL_FUNC(gtk_widget_destroy
),
1438 GTK_OBJECT(dialog
));
1440 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog
)->action_area
), hbox
);
1442 gtk_widget_grab_focus(entry
);
1444 gtk_window_set_modal(GTK_WINDOW(dialog
), TRUE
);
1446 gtk_widget_show_all(dialog
);
1449 void gtkaspell_uncheck_all(GtkAspell
* gtkaspell
)
1456 gtktext
= gtkaspell
->gtktext
;
1458 adj_value
= gtktext
->vadj
->value
;
1460 gtk_stext_freeze(gtktext
);
1462 origpos
= gtk_editable_get_position(GTK_EDITABLE(gtktext
));
1464 text
= gtk_editable_get_chars(GTK_EDITABLE(gtktext
), 0, -1);
1466 gtk_stext_set_point(gtktext
, 0);
1467 gtk_stext_forward_delete(gtktext
, gtk_stext_get_length(gtktext
));
1468 gtk_stext_insert(gtktext
, NULL
, NULL
, NULL
, text
, strlen(text
));
1470 gtk_stext_thaw(gtktext
);
1472 gtk_editable_set_position(GTK_EDITABLE(gtktext
), origpos
);
1473 gtk_stext_set_point(gtktext
, origpos
);
1474 gtk_adjustment_set_value(gtktext
->vadj
, adj_value
);
1480 static void toggle_check_while_typing_cb(GtkWidget
*w
, gpointer data
)
1482 GtkAspell
*gtkaspell
= (GtkAspell
*) data
;
1484 gtkaspell
->check_while_typing
= gtkaspell
->check_while_typing
== FALSE
;
1486 if (!gtkaspell
->check_while_typing
)
1487 gtkaspell_uncheck_all(gtkaspell
);
1489 if (gtkaspell
->config_menu
)
1490 populate_submenu(gtkaspell
, gtkaspell
->config_menu
);
1493 static GSList
*create_empty_dictionary_list(void)
1495 GSList
*list
= NULL
;
1498 dict
= g_new0(Dictionary
, 1);
1499 dict
->fullname
= g_strdup(_("None"));
1500 dict
->dictname
= dict
->fullname
;
1501 dict
->encoding
= NULL
;
1503 return g_slist_append(list
, dict
);
1506 /* gtkaspell_get_dictionary_list() - returns list of dictionary names */
1507 GSList
*gtkaspell_get_dictionary_list(const gchar
*aspell_path
, gint refresh
)
1511 AspellConfig
*config
;
1512 AspellDictInfoList
*dlist
;
1513 AspellDictInfoEnumeration
*dels
;
1514 const AspellDictInfo
*entry
;
1516 if (!gtkaspellcheckers
)
1517 gtkaspell_checkers_init();
1519 if (gtkaspellcheckers
->dictionary_list
&& !refresh
)
1520 return gtkaspellcheckers
->dictionary_list
;
1522 gtkaspell_free_dictionary_list(gtkaspellcheckers
->dictionary_list
);
1525 config
= new_aspell_config();
1527 aspell_config_replace(config
, "rem-all-word-list-path", "");
1528 if (aspell_config_error_number(config
) != 0) {
1529 gtkaspellcheckers
->error_message
= g_strdup(
1530 aspell_config_error_message(config
));
1531 gtkaspellcheckers
->dictionary_list
=
1532 create_empty_dictionary_list();
1534 return gtkaspellcheckers
->dictionary_list
;
1537 aspell_config_replace(config
, "dict-dir", aspell_path
);
1538 if (aspell_config_error_number(config
) != 0) {
1539 gtkaspellcheckers
->error_message
= g_strdup(
1540 aspell_config_error_message(config
));
1541 gtkaspellcheckers
->dictionary_list
=
1542 create_empty_dictionary_list();
1544 return gtkaspellcheckers
->dictionary_list
;
1547 dlist
= get_aspell_dict_info_list(config
);
1548 delete_aspell_config(config
);
1550 debug_print("Aspell: checking for dictionaries in %s\n", aspell_path
);
1551 dels
= aspell_dict_info_list_elements(dlist
);
1552 while ( (entry
= aspell_dict_info_enumeration_next(dels
)) != 0)
1554 dict
= g_new0(Dictionary
, 1);
1555 dict
->fullname
= g_strdup_printf("%s%s", aspell_path
,
1557 dict
->dictname
= dict
->fullname
+ strlen(aspell_path
);
1558 dict
->encoding
= g_strdup(entry
->code
);
1559 debug_print("Aspell: found dictionary %s %s\n", dict
->fullname
,
1561 list
= g_slist_insert_sorted(list
, dict
,
1562 (GCompareFunc
) compare_dict
);
1565 delete_aspell_dict_info_enumeration(dels
);
1569 debug_print("Aspell: error when searching for dictionaries: "
1570 "No dictionary found.\n");
1571 list
= create_empty_dictionary_list();
1574 gtkaspellcheckers
->dictionary_list
= list
;
1579 void gtkaspell_free_dictionary_list(GSList
*list
)
1583 for (walk
= list
; walk
!= NULL
; walk
= g_slist_next(walk
))
1585 dict
= (Dictionary
*) walk
->data
;
1586 dictionary_delete(dict
);
1591 GtkWidget
*gtkaspell_dictionary_option_menu_new(const gchar
*aspell_path
)
1593 GSList
*dict_list
, *tmp
;
1598 dict_list
= gtkaspell_get_dictionary_list(aspell_path
, TRUE
);
1599 g_return_val_if_fail(dict_list
, NULL
);
1601 menu
= gtk_menu_new();
1603 for (tmp
= dict_list
; tmp
!= NULL
; tmp
= g_slist_next(tmp
)) {
1604 dict
= (Dictionary
*) tmp
->data
;
1605 item
= gtk_menu_item_new_with_label(dict
->dictname
);
1606 gtk_object_set_data(GTK_OBJECT(item
), "dict_name",
1609 gtk_menu_append(GTK_MENU(menu
), item
);
1610 gtk_widget_show(item
);
1613 gtk_widget_show(menu
);
1618 gchar
*gtkaspell_get_dictionary_menu_active_item(GtkWidget
*menu
)
1620 GtkWidget
*menuitem
;
1621 gchar
*dict_fullname
;
1624 g_return_val_if_fail(GTK_IS_MENU(menu
), NULL
);
1626 menuitem
= gtk_menu_get_active(GTK_MENU(menu
));
1627 dict_fullname
= (gchar
*) gtk_object_get_data(GTK_OBJECT(menuitem
),
1629 g_return_val_if_fail(dict_fullname
, NULL
);
1631 label
= g_strdup(dict_fullname
);
1637 gint
gtkaspell_set_dictionary_menu_active_item(GtkWidget
*menu
, const gchar
*dictionary
)
1642 g_return_val_if_fail(menu
!= NULL
, 0);
1643 g_return_val_if_fail(dictionary
!= NULL
, 0);
1644 g_return_val_if_fail(GTK_IS_OPTION_MENU(menu
), 0);
1647 for (cur
= GTK_MENU_SHELL(gtk_option_menu_get_menu(GTK_OPTION_MENU(menu
)))->children
;
1648 cur
!= NULL
; cur
= cur
->next
) {
1649 GtkWidget
*menuitem
;
1652 menuitem
= GTK_WIDGET(cur
->data
);
1653 dict_name
= gtk_object_get_data(GTK_OBJECT(menuitem
),
1655 if ((dict_name
!= NULL
) && !strcmp2(dict_name
, dictionary
)) {
1656 gtk_option_menu_set_history(GTK_OPTION_MENU(menu
), n
);
1666 GtkWidget
*gtkaspell_sugmode_option_menu_new(gint sugmode
)
1671 menu
= gtk_menu_new();
1672 gtk_widget_show(menu
);
1674 item
= gtk_menu_item_new_with_label(_("Fast Mode"));
1675 gtk_widget_show(item
);
1676 gtk_menu_append(GTK_MENU(menu
), item
);
1677 gtk_object_set_data(GTK_OBJECT(item
), "sugmode", GINT_TO_POINTER(ASPELL_FASTMODE
));
1679 item
= gtk_menu_item_new_with_label(_("Normal Mode"));
1680 gtk_widget_show(item
);
1681 gtk_menu_append(GTK_MENU(menu
), item
);
1682 gtk_object_set_data(GTK_OBJECT(item
), "sugmode", GINT_TO_POINTER(ASPELL_NORMALMODE
));
1684 item
= gtk_menu_item_new_with_label(_("Bad Spellers Mode"));
1685 gtk_widget_show(item
);
1686 gtk_menu_append(GTK_MENU(menu
), item
);
1687 gtk_object_set_data(GTK_OBJECT(item
), "sugmode", GINT_TO_POINTER(ASPELL_BADSPELLERMODE
));
1692 void gtkaspell_sugmode_option_menu_set(GtkOptionMenu
*optmenu
, gint sugmode
)
1694 g_return_if_fail(GTK_IS_OPTION_MENU(optmenu
));
1696 g_return_if_fail(sugmode
== ASPELL_FASTMODE
||
1697 sugmode
== ASPELL_NORMALMODE
||
1698 sugmode
== ASPELL_BADSPELLERMODE
);
1700 gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu
), sugmode
- 1);
1703 gint
gtkaspell_get_sugmode_from_option_menu(GtkOptionMenu
*optmenu
)
1708 g_return_val_if_fail(GTK_IS_OPTION_MENU(optmenu
), -1);
1710 item
= gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(optmenu
)));
1712 sugmode
= GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(item
),
1718 static void use_alternate_dict(GtkAspell
*gtkaspell
)
1722 tmp
= gtkaspell
->gtkaspeller
;
1723 gtkaspell
->gtkaspeller
= gtkaspell
->alternate_speller
;
1724 gtkaspell
->alternate_speller
= tmp
;
1726 if (gtkaspell
->config_menu
)
1727 populate_submenu(gtkaspell
, gtkaspell
->config_menu
);
1730 static void popup_menu(GtkAspell
*gtkaspell
, GdkEventButton
*eb
)
1734 gtktext
= gtkaspell
->gtktext
;
1735 gtkaspell
->orig_pos
= gtk_editable_get_position(GTK_EDITABLE(gtktext
));
1737 if (!(eb
->state
& GDK_SHIFT_MASK
)) {
1738 if (check_at(gtkaspell
, gtkaspell
->orig_pos
)) {
1740 gtk_editable_set_position(GTK_EDITABLE(gtktext
),
1741 gtkaspell
->orig_pos
);
1742 gtk_stext_set_point(gtktext
, gtkaspell
->orig_pos
);
1744 if (misspelled_suggest(gtkaspell
, gtkaspell
->theword
)) {
1745 gtk_menu_popup(make_sug_menu(gtkaspell
),
1746 NULL
, NULL
, NULL
, NULL
,
1747 eb
->button
, GDK_CURRENT_TIME
);
1752 gtk_editable_set_position(GTK_EDITABLE(gtktext
),
1753 gtkaspell
->orig_pos
);
1754 gtk_stext_set_point(gtktext
, gtkaspell
->orig_pos
);
1758 gtk_menu_popup(make_config_menu(gtkaspell
), NULL
, NULL
, NULL
, NULL
,
1759 eb
->button
, GDK_CURRENT_TIME
);
1762 /* make_sug_menu() - Add menus to accept this word for this session
1763 * and to add it to personal dictionary
1765 static GtkMenu
*make_sug_menu(GtkAspell
*gtkaspell
)
1767 GtkWidget
*menu
, *item
;
1768 unsigned char *caption
;
1770 GtkAccelGroup
*accel
;
1771 GList
*l
= gtkaspell
->suggestions_list
;
1773 gtktext
= gtkaspell
->gtktext
;
1775 accel
= gtk_accel_group_new();
1776 menu
= gtk_menu_new();
1778 if (gtkaspell
->sug_menu
)
1779 gtk_widget_destroy(gtkaspell
->sug_menu
);
1781 gtkaspell
->sug_menu
= menu
;
1783 gtk_signal_connect(GTK_OBJECT(menu
), "cancel",
1784 GTK_SIGNAL_FUNC(cancel_menu_cb
), gtkaspell
);
1786 caption
= g_strdup_printf(_("\"%s\" unknown in %s"),
1787 (unsigned char*) l
->data
,
1788 gtkaspell
->gtkaspeller
->dictionary
->dictname
);
1789 item
= gtk_menu_item_new_with_label(caption
);
1790 gtk_widget_show(item
);
1791 gtk_menu_append(GTK_MENU(menu
), item
);
1792 gtk_misc_set_alignment(GTK_MISC(GTK_BIN(item
)->child
), 0.5, 0.5);
1795 item
= gtk_menu_item_new();
1796 gtk_widget_show(item
);
1797 gtk_menu_append(GTK_MENU(menu
), item
);
1799 item
= gtk_menu_item_new_with_label(_("Accept in this session"));
1800 gtk_widget_show(item
);
1801 gtk_menu_append(GTK_MENU(menu
), item
);
1802 gtk_signal_connect(GTK_OBJECT(item
), "activate",
1803 GTK_SIGNAL_FUNC(add_word_to_session_cb
),
1805 gtk_widget_add_accelerator(item
, "activate", accel
, GDK_space
,
1807 GTK_ACCEL_LOCKED
| GTK_ACCEL_VISIBLE
);
1809 item
= gtk_menu_item_new_with_label(_("Add to personal dictionary"));
1810 gtk_widget_show(item
);
1811 gtk_menu_append(GTK_MENU(menu
), item
);
1812 gtk_signal_connect(GTK_OBJECT(item
), "activate",
1813 GTK_SIGNAL_FUNC(add_word_to_personal_cb
),
1815 gtk_widget_add_accelerator(item
, "activate", accel
, GDK_Return
,
1817 GTK_ACCEL_LOCKED
| GTK_ACCEL_VISIBLE
);
1819 item
= gtk_menu_item_new_with_label(_("Replace with..."));
1820 gtk_widget_show(item
);
1821 gtk_menu_append(GTK_MENU(menu
), item
);
1822 gtk_signal_connect(GTK_OBJECT(item
), "activate",
1823 GTK_SIGNAL_FUNC(replace_with_create_dialog_cb
),
1825 gtk_widget_add_accelerator(item
, "activate", accel
, GDK_R
, 0,
1826 GTK_ACCEL_LOCKED
| GTK_ACCEL_VISIBLE
);
1828 if (gtkaspell
->use_alternate
&& gtkaspell
->alternate_speller
) {
1829 caption
= g_strdup_printf(_("Check with %s"),
1830 gtkaspell
->alternate_speller
->dictionary
->dictname
);
1831 item
= gtk_menu_item_new_with_label(caption
);
1833 gtk_widget_show(item
);
1834 gtk_menu_append(GTK_MENU(menu
), item
);
1835 gtk_signal_connect(GTK_OBJECT(item
), "activate",
1836 GTK_SIGNAL_FUNC(check_with_alternate_cb
),
1838 gtk_widget_add_accelerator(item
, "activate", accel
, GDK_X
, 0,
1839 GTK_ACCEL_LOCKED
| GTK_ACCEL_VISIBLE
);
1842 item
= gtk_menu_item_new();
1843 gtk_widget_show(item
);
1844 gtk_menu_append(GTK_MENU(menu
), item
);
1848 item
= gtk_menu_item_new_with_label(_("(no suggestions)"));
1849 gtk_widget_show(item
);
1850 gtk_menu_append(GTK_MENU(menu
), item
);
1852 GtkWidget
*curmenu
= menu
;
1856 if (count
== MENUCOUNT
) {
1859 item
= gtk_menu_item_new_with_label(_("More..."));
1860 gtk_widget_show(item
);
1861 gtk_menu_append(GTK_MENU(curmenu
), item
);
1863 curmenu
= gtk_menu_new();
1864 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item
),
1868 item
= gtk_menu_item_new_with_label((unsigned char*)l
->data
);
1869 gtk_widget_show(item
);
1870 gtk_menu_append(GTK_MENU(curmenu
), item
);
1871 gtk_signal_connect(GTK_OBJECT(item
), "activate",
1872 GTK_SIGNAL_FUNC(replace_word_cb
),
1875 if (curmenu
== menu
&& count
< MENUCOUNT
) {
1876 gtk_widget_add_accelerator(item
, "activate",
1881 gtk_widget_add_accelerator(item
, "activate",
1890 } while ((l
= l
->next
) != NULL
);
1893 gtk_accel_group_attach(accel
, GTK_OBJECT(menu
));
1894 gtk_accel_group_unref(accel
);
1896 return GTK_MENU(menu
);
1899 static void populate_submenu(GtkAspell
*gtkaspell
, GtkWidget
*menu
)
1901 GtkWidget
*item
, *submenu
;
1903 GtkAspeller
*gtkaspeller
= gtkaspell
->gtkaspeller
;
1905 if (GTK_MENU_SHELL(menu
)->children
) {
1906 GList
*amenu
, *alist
;
1907 for (amenu
= (GTK_MENU_SHELL(menu
)->children
); amenu
; ) {
1908 alist
= amenu
->next
;
1909 gtk_widget_destroy(GTK_WIDGET(amenu
->data
));
1914 dictname
= g_strdup_printf(_("Dictionary: %s"),
1915 gtkaspeller
->dictionary
->dictname
);
1916 item
= gtk_menu_item_new_with_label(dictname
);
1917 gtk_misc_set_alignment(GTK_MISC(GTK_BIN(item
)->child
), 0.5, 0.5);
1919 gtk_widget_show(item
);
1920 gtk_menu_append(GTK_MENU(menu
), item
);
1922 item
= gtk_menu_item_new();
1923 gtk_widget_show(item
);
1924 gtk_menu_append(GTK_MENU(menu
), item
);
1926 if (gtkaspell
->use_alternate
&& gtkaspell
->alternate_speller
) {
1927 dictname
= g_strdup_printf(_("Use alternate (%s)"),
1928 gtkaspell
->alternate_speller
->dictionary
->dictname
);
1929 item
= gtk_menu_item_new_with_label(dictname
);
1931 gtk_signal_connect(GTK_OBJECT(item
), "activate",
1932 GTK_SIGNAL_FUNC(switch_to_alternate_cb
),
1934 gtk_widget_show(item
);
1935 gtk_menu_append(GTK_MENU(menu
), item
);
1938 item
= gtk_check_menu_item_new_with_label(_("Fast Mode"));
1939 if (gtkaspell
->gtkaspeller
->sug_mode
== ASPELL_FASTMODE
) {
1940 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item
),TRUE
);
1941 gtk_widget_set_sensitive(GTK_WIDGET(item
),FALSE
);
1943 gtk_signal_connect(GTK_OBJECT(item
), "activate",
1944 GTK_SIGNAL_FUNC(set_sug_mode_cb
),
1946 gtk_widget_show(item
);
1947 gtk_menu_append(GTK_MENU(menu
), item
);
1949 item
= gtk_check_menu_item_new_with_label(_("Normal Mode"));
1950 if (gtkaspell
->gtkaspeller
->sug_mode
== ASPELL_NORMALMODE
) {
1951 gtk_widget_set_sensitive(GTK_WIDGET(item
), FALSE
);
1952 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item
), TRUE
);
1954 gtk_signal_connect(GTK_OBJECT(item
), "activate",
1955 GTK_SIGNAL_FUNC(set_sug_mode_cb
),
1957 gtk_widget_show(item
);
1958 gtk_menu_append(GTK_MENU(menu
),item
);
1960 item
= gtk_check_menu_item_new_with_label(_("Bad Spellers Mode"));
1961 if (gtkaspell
->gtkaspeller
->sug_mode
== ASPELL_BADSPELLERMODE
) {
1962 gtk_widget_set_sensitive(GTK_WIDGET(item
), FALSE
);
1963 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item
), TRUE
);
1965 gtk_signal_connect(GTK_OBJECT(item
), "activate",
1966 GTK_SIGNAL_FUNC(set_sug_mode_cb
),
1968 gtk_widget_show(item
);
1969 gtk_menu_append(GTK_MENU(menu
), item
);
1971 item
= gtk_menu_item_new();
1972 gtk_widget_show(item
);
1973 gtk_menu_append(GTK_MENU(menu
), item
);
1975 item
= gtk_check_menu_item_new_with_label(_("Check while typing"));
1976 if (gtkaspell
->check_while_typing
)
1977 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item
), TRUE
);
1979 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item
), FALSE
);
1980 gtk_signal_connect(GTK_OBJECT(item
), "activate",
1981 GTK_SIGNAL_FUNC(toggle_check_while_typing_cb
),
1983 gtk_widget_show(item
);
1984 gtk_menu_append(GTK_MENU(menu
), item
);
1986 item
= gtk_menu_item_new();
1987 gtk_widget_show(item
);
1988 gtk_menu_append(GTK_MENU(menu
), item
);
1990 submenu
= gtk_menu_new();
1991 item
= gtk_menu_item_new_with_label(_("Change dictionary"));
1992 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item
),submenu
);
1993 gtk_widget_show(item
);
1994 gtk_menu_append(GTK_MENU(menu
), item
);
1997 if (gtkaspellcheckers
->dictionary_list
== NULL
)
1998 gtkaspell_get_dictionary_list(gtkaspell
->dictionary_path
, FALSE
);
2000 GtkWidget
* curmenu
= submenu
;
2004 tmp
= gtkaspellcheckers
->dictionary_list
;
2006 for (tmp
= gtkaspellcheckers
->dictionary_list
; tmp
!= NULL
;
2007 tmp
= g_slist_next(tmp
)) {
2008 if (count
== MENUCOUNT
) {
2011 newmenu
= gtk_menu_new();
2012 item
= gtk_menu_item_new_with_label(_("More..."));
2013 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item
),
2016 gtk_menu_append(GTK_MENU(curmenu
), item
);
2017 gtk_widget_show(item
);
2021 dict
= (Dictionary
*) tmp
->data
;
2022 item
= gtk_check_menu_item_new_with_label(dict
->dictname
);
2023 gtk_object_set_data(GTK_OBJECT(item
), "dict_name",
2025 if (strcmp2(dict
->fullname
,
2026 gtkaspell
->gtkaspeller
->dictionary
->fullname
))
2027 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item
), FALSE
);
2029 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item
), TRUE
);
2030 gtk_widget_set_sensitive(GTK_WIDGET(item
),
2033 gtk_signal_connect(GTK_OBJECT(item
), "activate",
2034 GTK_SIGNAL_FUNC(change_dict_cb
),
2036 gtk_widget_show(item
);
2037 gtk_menu_append(GTK_MENU(curmenu
), item
);
2044 static GtkMenu
*make_config_menu(GtkAspell
*gtkaspell
)
2046 if (!gtkaspell
->popup_config_menu
)
2047 gtkaspell
->popup_config_menu
= gtk_menu_new();
2049 debug_print("Aspell: creating/using popup_config_menu %0x\n",
2050 (guint
) gtkaspell
->popup_config_menu
);
2051 populate_submenu(gtkaspell
, gtkaspell
->popup_config_menu
);
2053 return GTK_MENU(gtkaspell
->popup_config_menu
);
2056 void gtkaspell_populate_submenu(GtkAspell
*gtkaspell
, GtkWidget
*menuitem
)
2060 menu
= GTK_WIDGET(GTK_MENU_ITEM(menuitem
)->submenu
);
2062 debug_print("Aspell: using config menu %0x\n",
2063 (guint
) gtkaspell
->popup_config_menu
);
2064 populate_submenu(gtkaspell
, menu
);
2066 gtkaspell
->config_menu
= menu
;
2070 static void set_menu_pos(GtkMenu
*menu
, gint
*x
, gint
*y
, gpointer data
)
2072 GtkAspell
*gtkaspell
= (GtkAspell
*) data
;
2073 gint xx
= 0, yy
= 0;
2076 GtkSText
*text
= GTK_STEXT(gtkaspell
->gtktext
);
2079 gdk_window_get_origin(GTK_WIDGET(gtkaspell
->gtktext
)->window
, &xx
, &yy
);
2081 sx
= gdk_screen_width();
2082 sy
= gdk_screen_height();
2084 gtk_widget_get_child_requisition(GTK_WIDGET(menu
), &r
);
2089 *x
= gtkaspell
->gtktext
->cursor_pos_x
+ xx
+
2090 gdk_char_width(GTK_WIDGET(text
)->style
->font
, ' ');
2091 *y
= gtkaspell
->gtktext
->cursor_pos_y
+ yy
;
2097 gdk_string_height((GTK_WIDGET(gtkaspell
->gtktext
))->style
->font
,
2098 gtkaspell
->theword
);
2102 /* Menu call backs */
2104 static gboolean
cancel_menu_cb(GtkMenuShell
*w
, gpointer data
)
2106 GtkAspell
*gtkaspell
= (GtkAspell
*) data
;
2108 gtkaspell
->continue_check
= NULL
;
2109 set_point_continue(gtkaspell
);
2115 gboolean
gtkaspell_change_dict(GtkAspell
*gtkaspell
, const gchar
*dictionary
)
2118 GtkAspeller
*gtkaspeller
;
2121 g_return_val_if_fail(gtkaspell
, FALSE
);
2122 g_return_val_if_fail(dictionary
, FALSE
);
2124 sug_mode
= gtkaspell
->default_sug_mode
;
2126 dict
= g_new0(Dictionary
, 1);
2127 dict
->fullname
= g_strdup(dictionary
);
2128 dict
->encoding
= g_strdup(gtkaspell
->gtkaspeller
->dictionary
->encoding
);
2130 if (gtkaspell
->use_alternate
&& gtkaspell
->alternate_speller
&&
2131 dict
== gtkaspell
->alternate_speller
->dictionary
) {
2132 use_alternate_dict(gtkaspell
);
2133 dictionary_delete(dict
);
2137 gtkaspeller
= gtkaspeller_new(dict
);
2141 message
= g_strdup_printf(_("The spell checker could not change dictionary.\n%s"),
2142 gtkaspellcheckers
->error_message
);
2144 gtkaspell_alert_dialog(message
);
2147 if (gtkaspell
->use_alternate
) {
2148 if (gtkaspell
->alternate_speller
)
2149 gtkaspeller_delete(gtkaspell
->alternate_speller
);
2150 gtkaspell
->alternate_speller
= gtkaspell
->gtkaspeller
;
2152 gtkaspeller_delete(gtkaspell
->gtkaspeller
);
2154 gtkaspell
->gtkaspeller
= gtkaspeller
;
2155 gtkaspell_set_sug_mode(gtkaspell
, sug_mode
);
2158 dictionary_delete(dict
);
2160 if (gtkaspell
->config_menu
)
2161 populate_submenu(gtkaspell
, gtkaspell
->config_menu
);
2166 /* change_dict_cb() - Menu callback : change dict */
2167 static void change_dict_cb(GtkWidget
*w
, GtkAspell
*gtkaspell
)
2171 fullname
= (gchar
*) gtk_object_get_data(GTK_OBJECT(w
), "dict_name");
2173 if (!strcmp2(fullname
, _("None")))
2176 gtkaspell_change_dict(gtkaspell
, fullname
);
2179 static void switch_to_alternate_cb(GtkWidget
*w
,
2182 GtkAspell
*gtkaspell
= (GtkAspell
*) data
;
2183 use_alternate_dict(gtkaspell
);
2186 /* Misc. helper functions */
2188 static void set_point_continue(GtkAspell
*gtkaspell
)
2192 gtktext
= gtkaspell
->gtktext
;
2194 gtk_stext_freeze(gtktext
);
2195 gtk_editable_set_position(GTK_EDITABLE(gtktext
),gtkaspell
->orig_pos
);
2196 gtk_stext_set_point(gtktext
, gtkaspell
->orig_pos
);
2197 gtk_stext_thaw(gtktext
);
2199 if (gtkaspell
->continue_check
)
2200 gtkaspell
->continue_check((gpointer
*) gtkaspell
);
2203 static void allocate_color(GtkAspell
*gtkaspell
, gint rgbvalue
)
2206 GdkColor
*color
= &(gtkaspell
->highlight
);
2208 gc
= gtk_widget_get_colormap(GTK_WIDGET(gtkaspell
->gtktext
));
2210 if (gtkaspell
->highlight
.pixel
)
2211 gdk_colormap_free_colors(gc
, &(gtkaspell
->highlight
), 1);
2213 /* Shameless copy from Sylpheed's gtkutils.c */
2215 color
->red
= (int) (((gdouble
)((rgbvalue
& 0xff0000) >> 16) / 255.0)
2217 color
->green
= (int) (((gdouble
)((rgbvalue
& 0x00ff00) >> 8) / 255.0)
2219 color
->blue
= (int) (((gdouble
) (rgbvalue
& 0x0000ff) / 255.0)
2222 gdk_colormap_alloc_color(gc
, &(gtkaspell
->highlight
), FALSE
, TRUE
);
2225 static void change_color(GtkAspell
* gtkaspell
,
2226 gint start
, gint end
,
2232 g_return_if_fail(start
< end
);
2234 gtktext
= gtkaspell
->gtktext
;
2236 gtk_stext_freeze(gtktext
);
2238 gtk_stext_set_point(gtktext
, start
);
2239 gtk_stext_forward_delete(gtktext
, end
- start
);
2240 gtk_stext_insert(gtktext
, NULL
, color
, NULL
, newtext
,
2243 gtk_stext_thaw(gtktext
);
2246 /* convert_to_aspell_encoding () - converts ISO-8859-* strings to iso8859-*
2247 * as needed by aspell. Returns an allocated string.
2250 static guchar
*convert_to_aspell_encoding (const guchar
*encoding
)
2252 guchar
* aspell_encoding
;
2254 if (strstr2(encoding
, "ISO-8859-")) {
2255 aspell_encoding
= g_strdup_printf("iso8859%s", encoding
+8);
2258 if (!strcmp2(encoding
, "US-ASCII"))
2259 aspell_encoding
= g_strdup("iso8859-1");
2261 aspell_encoding
= g_strdup(encoding
);
2264 return aspell_encoding
;
2267 /* compare_dict () - compare 2 dict names */
2268 static gint
compare_dict(Dictionary
*a
, Dictionary
*b
)
2270 guint aparts
= 0, bparts
= 0;
2273 for (i
=0; i
< strlen(a
->dictname
); i
++)
2274 if (a
->dictname
[i
] == '-')
2276 for (i
=0; i
< strlen(b
->dictname
); i
++)
2277 if (b
->dictname
[i
] == '-')
2280 if (aparts
!= bparts
)
2281 return (aparts
< bparts
) ? -1 : +1;
2284 compare
= strcmp2(a
->dictname
, b
->dictname
);
2286 compare
= strcmp2(a
->fullname
, b
->fullname
);
2291 static void dictionary_delete(Dictionary
*dict
)
2293 g_free(dict
->fullname
);
2294 g_free(dict
->encoding
);
2298 static Dictionary
*dictionary_dup(const Dictionary
*dict
)
2302 dict2
= g_new(Dictionary
, 1);
2304 dict2
->fullname
= g_strdup(dict
->fullname
);
2305 dict2
->dictname
= dict
->dictname
- dict
->fullname
+ dict2
->fullname
;
2306 dict2
->encoding
= g_strdup(dict
->encoding
);
2311 static void free_suggestions_list(GtkAspell
*gtkaspell
)
2315 for (list
= gtkaspell
->suggestions_list
; list
!= NULL
;
2321 gtkaspell
->max_sug
= -1;
2322 gtkaspell
->suggestions_list
= NULL
;
2325 static void reset_theword_data(GtkAspell
*gtkaspell
)
2327 gtkaspell
->start_pos
= 0;
2328 gtkaspell
->end_pos
= 0;
2329 gtkaspell
->theword
[0] = 0;
2330 gtkaspell
->max_sug
= -1;
2332 free_suggestions_list(gtkaspell
);
2335 static void free_checkers(gpointer elt
, gpointer data
)
2337 GtkAspeller
*gtkaspeller
= elt
;
2339 g_return_if_fail(gtkaspeller
);
2341 gtkaspeller_real_delete(gtkaspeller
);
2344 static gint
find_gtkaspeller(gconstpointer aa
, gconstpointer bb
)
2346 Dictionary
*a
= ((GtkAspeller
*) aa
)->dictionary
;
2347 Dictionary
*b
= ((GtkAspeller
*) bb
)->dictionary
;
2349 if (a
&& b
&& a
->fullname
&& b
->fullname
&&
2350 strcmp(a
->fullname
, b
->fullname
) == 0 &&
2351 a
->encoding
&& b
->encoding
)
2352 return strcmp(a
->encoding
, b
->encoding
);
2357 static void gtkaspell_alert_dialog(gchar
*message
)
2362 GtkWidget
*ok_button
;
2364 dialog
= gtk_dialog_new();
2365 gtk_window_set_policy(GTK_WINDOW(dialog
), FALSE
, FALSE
, FALSE
);
2366 gtk_window_set_position(GTK_WINDOW(dialog
),GTK_WIN_POS_MOUSE
);
2367 gtk_signal_connect_object(GTK_OBJECT(dialog
), "destroy",
2368 GTK_SIGNAL_FUNC(gtk_widget_destroy
),
2369 GTK_OBJECT(dialog
));
2371 label
= gtk_label_new(message
);
2372 gtk_misc_set_padding(GTK_MISC(label
), 8, 8);
2374 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog
)->vbox
), label
);
2376 hbox
= gtk_hbox_new(FALSE
, 0);
2378 ok_button
= gtk_button_new_with_label(_("OK"));
2379 GTK_WIDGET_SET_FLAGS(ok_button
, GTK_CAN_DEFAULT
);
2380 gtk_box_pack_start(GTK_BOX(hbox
), ok_button
, TRUE
, TRUE
, 8);
2382 gtk_signal_connect_object(GTK_OBJECT(ok_button
), "clicked",
2383 GTK_SIGNAL_FUNC(gtk_widget_destroy
),
2384 GTK_OBJECT(dialog
));
2385 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog
)->action_area
), hbox
);
2387 gtk_widget_grab_default(ok_button
);
2388 gtk_widget_grab_focus(ok_button
);
2389 gtk_window_set_modal(GTK_WINDOW(dialog
), TRUE
);
2391 gtk_widget_show_all(dialog
);