1 /*
2 * gui.c - this file is part of Spellcheck, a Geany plugin
3 *
4 * Copyright 2008-2011 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
5 * Copyright 2008-2010 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 * MA 02110-1301, USA.
21 *
22 * $Id$
23 */
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <geanyplugin.h>
30
31 #include <ctype.h>
32 #include <string.h>
33
34
35 #include "gui.h"
36 #include "scplugin.h"
37 #include "speller.h"
38
39
40
41 typedef struct
42 {
43 gint pos;
44 GeanyDocument *doc;
45 /* static storage for the misspelled word under the cursor when using the editing menu */
46 gchar *word;
47 } SpellClickInfo;
48 static SpellClickInfo clickinfo;
49
50 typedef struct
51 {
52 GeanyDocument *doc;
53 gint line_number;
54 gint line_count;
55 guint check_while_typing_idle_source_id;
56 } CheckLineData;
57 static CheckLineData check_line_data;
58
59 /* Flag to indicate that a callback function will be triggered by generating the appropriate event
60 * but the callback should be ignored. */
61 static gboolean sc_ignore_callback = FALSE;
62
63
64 static void perform_check(GeanyDocument *doc);
65
66
clear_spellcheck_error_markers(GeanyDocument * doc)67 static void clear_spellcheck_error_markers(GeanyDocument *doc)
68 {
69 editor_indicator_clear(doc->editor, GEANY_INDICATOR_ERROR);
70 }
71
72
print_typing_changed_message(void)73 static void print_typing_changed_message(void)
74 {
75 if (sc_info->check_while_typing)
76 ui_set_statusbar(FALSE, _("Spell checking while typing is now enabled"));
77 else
78 ui_set_statusbar(FALSE, _("Spell checking while typing is now disabled"));
79 }
80
81
toolbar_item_toggled_cb(GtkToggleToolButton * button,gpointer user_data)82 static void toolbar_item_toggled_cb(GtkToggleToolButton *button, gpointer user_data)
83 {
84 gboolean check_while_typing_changed, check_while_typing;
85
86 if (sc_ignore_callback)
87 return;
88
89 check_while_typing = gtk_toggle_tool_button_get_active(button);
90 check_while_typing_changed = check_while_typing != sc_info->check_while_typing;
91 sc_info->check_while_typing = check_while_typing;
92
93 print_typing_changed_message();
94
95 /* force a rescan of the document if 'check while typing' has been turned on and clean
96 * errors if it has been turned off */
97 if (check_while_typing_changed)
98 {
99 GeanyDocument *doc = document_get_current();
100 if (sc_info->check_while_typing)
101 perform_check(doc);
102 else
103 clear_spellcheck_error_markers(doc);
104 }
105 }
106
107
sc_gui_update_toolbar(void)108 void sc_gui_update_toolbar(void)
109 {
110 /* toolbar item is not requested, so remove the item if it exists */
111 if (! sc_info->show_toolbar_item)
112 {
113 if (sc_info->toolbar_button != NULL)
114 {
115 gtk_widget_hide(GTK_WIDGET(sc_info->toolbar_button));
116 }
117 }
118 else
119 {
120 if (sc_info->toolbar_button == NULL)
121 {
122 sc_info->toolbar_button = gtk_toggle_tool_button_new_from_stock(GTK_STOCK_SPELL_CHECK);
123
124 plugin_add_toolbar_item(geany_plugin, sc_info->toolbar_button);
125 ui_add_document_sensitive(GTK_WIDGET(sc_info->toolbar_button));
126
127 g_signal_connect(sc_info->toolbar_button, "toggled",
128 G_CALLBACK(toolbar_item_toggled_cb), NULL);
129 }
130 gtk_widget_show(GTK_WIDGET(sc_info->toolbar_button));
131
132 sc_ignore_callback = TRUE;
133 gtk_toggle_tool_button_set_active(
134 GTK_TOGGLE_TOOL_BUTTON(sc_info->toolbar_button), sc_info->check_while_typing);
135 sc_ignore_callback = FALSE;
136 }
137 }
138
139
menu_suggestion_item_activate_cb(GtkMenuItem * menuitem,gpointer gdata)140 static void menu_suggestion_item_activate_cb(GtkMenuItem *menuitem, gpointer gdata)
141 {
142 const gchar *sugg;
143 gint startword, endword;
144 ScintillaObject *sci = clickinfo.doc->editor->sci;
145
146 g_return_if_fail(clickinfo.doc != NULL && clickinfo.pos != -1);
147
148 startword = scintilla_send_message(sci, SCI_WORDSTARTPOSITION, clickinfo.pos, 0);
149 endword = scintilla_send_message(sci, SCI_WORDENDPOSITION, clickinfo.pos, 0);
150
151 if (startword != endword)
152 {
153 gchar *word;
154
155 sci_set_selection_start(sci, startword);
156 sci_set_selection_end(sci, endword);
157
158 /* retrieve the old text */
159 word = sci_get_selection_contents(sci);
160
161 /* retrieve the new text */
162 sugg = gtk_label_get_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(menuitem))));
163
164 /* replace the misspelled word with the chosen suggestion */
165 sci_replace_sel(sci, sugg);
166
167 /* store the replacement for future checks */
168 sc_speller_store_replacement(word, sugg);
169
170 /* remove indicator */
171 sci_indicator_clear(sci, startword, endword - startword);
172
173 g_free(word);
174 }
175 }
176
177
menu_addword_item_activate_cb(GtkMenuItem * menuitem,gpointer gdata)178 static void menu_addword_item_activate_cb(GtkMenuItem *menuitem, gpointer gdata)
179 {
180 gint startword, endword, i, doc_len;
181 ScintillaObject *sci;
182 gboolean ignore = GPOINTER_TO_INT(gdata);
183 gint click_word_len;
184
185 if (clickinfo.doc == NULL || clickinfo.word == NULL || clickinfo.pos == -1)
186 return;
187
188 /* if we ignore the word, we add it to the current session, to ignore it
189 * also for further checks*/
190 if (ignore)
191 sc_speller_add_word_to_session(clickinfo.word);
192 /* if we do not ignore the word, we add the word to the personal dictionary */
193 else
194 sc_speller_add_word(clickinfo.word);
195
196 /* Remove all indicators on the added/ignored word */
197 sci = clickinfo.doc->editor->sci;
198 click_word_len = (gint) strlen(clickinfo.word);
199 doc_len = sci_get_length(sci);
200 for (i = 0; i < doc_len; i++)
201 {
202 startword = scintilla_send_message(sci, SCI_INDICATORSTART, 0, i);
203 if (startword >= 0)
204 {
205 endword = scintilla_send_message(sci, SCI_INDICATOREND, 0, startword);
206 if (startword == endword)
207 continue;
208
209 if (click_word_len == endword - startword)
210 {
211 const gchar *ptr = (const gchar *) scintilla_send_message(sci,
212 SCI_GETRANGEPOINTER, startword, endword - startword);
213
214 if (strncmp(ptr, clickinfo.word, click_word_len) == 0)
215 sci_indicator_clear(sci, startword, endword - startword);
216 }
217
218 i = endword;
219 }
220 }
221 }
222
223
224 /* Create a @c GtkImageMenuItem with a stock image and a custom label.
225 * @param stock_id Stock image ID, e.g. @c GTK_STOCK_OPEN.
226 * @param label Menu item label.
227 * @return The new @c GtkImageMenuItem. */
image_menu_item_new(const gchar * stock_id,const gchar * label)228 static GtkWidget *image_menu_item_new(const gchar *stock_id, const gchar *label)
229 {
230 GtkWidget *item = gtk_image_menu_item_new_with_label(label);
231 GtkWidget *image = gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_MENU);
232
233 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
234 gtk_widget_show(image);
235 return item;
236 }
237
238
init_editor_submenu(void)239 static GtkWidget *init_editor_submenu(void)
240 {
241 if (sc_info->show_editor_menu_item_sub_menu)
242 {
243 if (sc_info->edit_menu_sub != NULL && GTK_IS_WIDGET(sc_info->edit_menu_sub))
244 gtk_widget_destroy(sc_info->edit_menu_sub);
245
246 sc_info->edit_menu_sub = gtk_menu_new();
247 gtk_menu_item_set_submenu(GTK_MENU_ITEM(sc_info->edit_menu), sc_info->edit_menu_sub);
248
249 gtk_widget_show(sc_info->edit_menu);
250 gtk_widget_show(sc_info->edit_menu_sep);
251 gtk_widget_show(sc_info->edit_menu_sub);
252
253 return sc_info->edit_menu_sub;
254 }
255 else
256 {
257 return geany->main_widgets->editor_menu;
258 }
259 }
260
261
perform_check(GeanyDocument * doc)262 static void perform_check(GeanyDocument *doc)
263 {
264 clear_spellcheck_error_markers(doc);
265
266 if (sc_info->use_msgwin)
267 {
268 msgwin_clear_tab(MSG_MESSAGE);
269 msgwin_switch_tab(MSG_MESSAGE, FALSE);
270 }
271
272 sc_speller_check_document(doc);
273 }
274
275
perform_spell_check_cb(GtkWidget * menu_item,GeanyDocument * doc)276 static void perform_spell_check_cb(GtkWidget *menu_item, GeanyDocument *doc)
277 {
278 perform_check(doc);
279 }
280
281
perform_check_delayed_cb(gpointer doc)282 static gboolean perform_check_delayed_cb(gpointer doc)
283 {
284 perform_check((GeanyDocument*)doc);
285 return FALSE;
286 }
287
288
sc_gui_document_open_cb(GObject * obj,GeanyDocument * doc,gpointer user_data)289 void sc_gui_document_open_cb(GObject *obj, GeanyDocument *doc, gpointer user_data)
290 {
291 if (sc_info->check_on_document_open && main_is_realized())
292 g_idle_add(perform_check_delayed_cb, doc);
293 }
294
295
menu_item_ref(GtkWidget * menu_item)296 static void menu_item_ref(GtkWidget *menu_item)
297 {
298 if (! sc_info->show_editor_menu_item_sub_menu)
299 sc_info->edit_menu_items = g_slist_append(sc_info->edit_menu_items, menu_item);
300 }
301
302
update_editor_menu_items(const gchar * search_word,const gchar ** suggs,gsize n_suggs)303 static void update_editor_menu_items(const gchar *search_word, const gchar **suggs, gsize n_suggs)
304 {
305 GtkWidget *menu_item, *menu, *sub_menu;
306 GSList *node = NULL;
307 gchar *label;
308 gsize i;
309
310 menu = init_editor_submenu();
311 sub_menu = menu;
312
313 /* display 5 suggestions on top level, 20 more in sub menu */
314 for (i = 0; i < MIN(n_suggs, 25); i++)
315 {
316 if (i >= 5 && menu == sub_menu)
317 {
318 /* create "More..." sub menu */
319 if (sc_info->show_editor_menu_item_sub_menu)
320 {
321 menu_item = gtk_separator_menu_item_new();
322 gtk_widget_show(menu_item);
323 gtk_menu_shell_append(GTK_MENU_SHELL(sub_menu), menu_item);
324 }
325
326 menu_item = gtk_menu_item_new_with_label(_("More..."));
327 gtk_widget_show(menu_item);
328 gtk_menu_shell_append(GTK_MENU_SHELL(sub_menu), menu_item);
329 menu_item_ref(menu_item);
330
331 sub_menu = gtk_menu_new();
332 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), sub_menu);
333 }
334 menu_item = gtk_menu_item_new_with_label(suggs[i]);
335 gtk_widget_show(menu_item);
336 gtk_container_add(GTK_CONTAINER(sub_menu), menu_item);
337 if (menu == sub_menu)
338 {
339 /* Remember menu items to delete only for the top-level, the nested menu items are
340 * destroyed recursively via the sub menu */
341 menu_item_ref(menu_item);
342 }
343 g_signal_connect(menu_item, "activate", G_CALLBACK(menu_suggestion_item_activate_cb), NULL);
344 }
345 if (suggs == NULL)
346 {
347 menu_item = gtk_menu_item_new_with_label(_("(No Suggestions)"));
348 gtk_widget_set_sensitive(menu_item, FALSE);
349 gtk_widget_show(menu_item);
350 gtk_container_add(GTK_CONTAINER(menu), menu_item);
351 menu_item_ref(menu_item);
352 }
353 if (sc_info->show_editor_menu_item_sub_menu)
354 {
355 menu_item = gtk_separator_menu_item_new();
356 gtk_widget_show(menu_item);
357 gtk_container_add(GTK_CONTAINER(menu), menu_item);
358 }
359
360 label = g_strdup_printf(_("Add \"%s\" to Dictionary"), search_word);
361 menu_item = image_menu_item_new(GTK_STOCK_ADD, label);
362 gtk_widget_show(menu_item);
363 gtk_container_add(GTK_CONTAINER(menu), menu_item);
364 menu_item_ref(menu_item);
365 g_signal_connect(menu_item, "activate",
366 G_CALLBACK(menu_addword_item_activate_cb), GINT_TO_POINTER(FALSE));
367
368 menu_item = image_menu_item_new(GTK_STOCK_REMOVE, _("Ignore All"));
369 gtk_widget_show(menu_item);
370 gtk_container_add(GTK_CONTAINER(menu), menu_item);
371 menu_item_ref(menu_item);
372 g_signal_connect(menu_item, "activate",
373 G_CALLBACK(menu_addword_item_activate_cb), GINT_TO_POINTER(TRUE));
374
375 g_free(label);
376
377 /* re-order menu items: above all menu items are append but for the top-level menu items
378 * we want them to appear at the top of the editor menu */
379 if (! sc_info->show_editor_menu_item_sub_menu)
380 {
381 gpointer child;
382 /* final separator */
383 menu_item = gtk_separator_menu_item_new();
384 gtk_widget_show(menu_item);
385 gtk_container_add(GTK_CONTAINER(menu), menu_item);
386 menu_item_ref(menu_item);
387 /* re-order */
388 i = 0;
389 foreach_slist(node, sc_info->edit_menu_items)
390 {
391 child = node->data;
392 gtk_menu_reorder_child(GTK_MENU(menu), GTK_WIDGET(child), i);
393 i++;
394 }
395 }
396 }
397
398
sc_gui_update_editor_menu_cb(GObject * obj,const gchar * word,gint pos,GeanyDocument * doc,gpointer user_data)399 void sc_gui_update_editor_menu_cb(GObject *obj, const gchar *word, gint pos,
400 GeanyDocument *doc, gpointer user_data)
401 {
402 gchar *search_word;
403
404 g_return_if_fail(doc != NULL && doc->is_valid);
405
406 /* hide the submenu in any case, we will reshow it again if we actually found something */
407 if (sc_info->edit_menu != NULL)
408 gtk_widget_hide(sc_info->edit_menu);
409 if (sc_info->edit_menu_sep != NULL)
410 gtk_widget_hide(sc_info->edit_menu_sep);
411 /* clean previously added items to the editor menu */
412 if (sc_info->edit_menu_items != NULL)
413 {
414 g_slist_free_full(sc_info->edit_menu_items, (GDestroyNotify) gtk_widget_destroy);
415 sc_info->edit_menu_items = NULL;
416 }
417
418 if (! sc_info->show_editor_menu_item)
419 return;
420
421 /* if we have a selection, prefer it over the current word */
422 if (sci_has_selection(doc->editor->sci))
423 search_word = sci_get_selection_contents(doc->editor->sci);
424 else
425 search_word = g_strdup(word);
426
427 /* ignore numbers or words starting with digits and non-text */
428 if (EMPTY(search_word) || isdigit(*search_word) || ! sc_speller_is_text(doc, pos))
429 {
430 g_free(search_word);
431 return;
432 }
433
434 /* ignore too long search words */
435 if (strlen(search_word) > 100)
436 {
437 GtkWidget *menu_item, *menu;
438
439 menu = init_editor_submenu();
440 menu_item = gtk_menu_item_new_with_label(
441 _("Search term is too long to provide\nspelling suggestions in the editor menu."));
442 gtk_widget_set_sensitive(menu_item, FALSE);
443 gtk_widget_show(menu_item);
444 gtk_container_add(GTK_CONTAINER(menu), menu_item);
445 menu_item_ref(menu_item);
446
447 menu_item = gtk_menu_item_new_with_label(_("Perform Spell Check"));
448 gtk_widget_show(menu_item);
449 gtk_container_add(GTK_CONTAINER(menu), menu_item);
450 menu_item_ref(menu_item);
451 g_signal_connect(menu_item, "activate", G_CALLBACK(perform_spell_check_cb), doc);
452
453 g_free(search_word);
454 return;
455 }
456
457 if (sc_speller_dict_check(search_word) != 0)
458 {
459 gsize n_suggs;
460 gchar **suggs;
461
462 suggs = sc_speller_dict_suggest(search_word, &n_suggs);
463
464 clickinfo.pos = pos;
465 clickinfo.doc = doc;
466 setptr(clickinfo.word, search_word);
467
468 update_editor_menu_items(search_word, (const gchar**) suggs, n_suggs);
469
470 if (suggs != NULL)
471 sc_speller_dict_free_string_list(suggs);
472 }
473 else
474 {
475 g_free(search_word); /* search_word is free'd via clickinfo.word otherwise */
476 }
477 }
478
479
indicator_clear_on_line(GeanyDocument * doc,gint line_number)480 static void indicator_clear_on_line(GeanyDocument *doc, gint line_number)
481 {
482 gint start_pos, length;
483
484 g_return_if_fail(doc != NULL);
485
486 start_pos = sci_get_position_from_line(doc->editor->sci, line_number);
487 length = sci_get_line_length(doc->editor->sci, line_number);
488
489 sci_indicator_set(doc->editor->sci, GEANY_INDICATOR_ERROR);
490 sci_indicator_clear(doc->editor->sci, start_pos, length);
491 }
492
493
check_lines(gpointer data)494 static gboolean check_lines(gpointer data)
495 {
496 GeanyDocument *doc = check_line_data.doc;
497
498 /* since we're in an timeout callback, the document may have been closed */
499 if (DOC_VALID (doc))
500 {
501 gint line_number = check_line_data.line_number;
502 gint line_count = check_line_data.line_count;
503 gint i;
504
505 for (i = 0; i < line_count; i++)
506 {
507 indicator_clear_on_line(doc, line_number);
508 if (sc_speller_process_line(doc, line_number) != 0)
509 {
510 if (sc_info->use_msgwin)
511 msgwin_switch_tab(MSG_MESSAGE, FALSE);
512 }
513 line_number++;
514 }
515 }
516 check_line_data.check_while_typing_idle_source_id = 0;
517 return FALSE;
518 }
519
520
need_delay(void)521 static gboolean need_delay(void)
522 {
523 static gint64 time_prev = 0; /* time in microseconds */
524 gint64 time_now;
525 GTimeVal t;
526 const gint timeout = 500; /* delay in milliseconds */
527 gboolean ret = FALSE;
528
529 g_get_current_time(&t);
530
531 time_now = ((gint64) t.tv_sec * G_USEC_PER_SEC) + t.tv_usec;
532
533 /* delay keypresses for 0.5 seconds */
534 if (time_now < (time_prev + (timeout * 1000)))
535 return TRUE;
536
537 if (check_line_data.check_while_typing_idle_source_id == 0)
538 {
539 check_line_data.check_while_typing_idle_source_id =
540 plugin_timeout_add(geany_plugin, timeout, check_lines, NULL);
541 ret = TRUE;
542 }
543
544 /* set current time for the next key press */
545 time_prev = time_now;
546
547 return ret;
548 }
549
550
check_on_text_changed(GeanyDocument * doc,gint position,gint lines_added)551 static void check_on_text_changed(GeanyDocument *doc, gint position, gint lines_added)
552 {
553 gint line_number;
554 gint line_count;
555
556 /* Iterating over all lines which changed as indicated by lines_added. lines_added is 0
557 * if only one line has changed, in this case set line_count to 1. Otherwise, iterating over all
558 * new lines makes spell checking work for pasted text. */
559 line_count = MAX(1, lines_added);
560
561 line_number = sci_get_line_from_position(doc->editor->sci, position);
562 /* TODO: storing these information in the global check_line_data struct isn't that good.
563 * The data gets overwritten when a new line is inserted and so there is a chance that the
564 * previous line is not checked to the end. One solution could be to simple maintain a list
565 * of line numbers which needs to be checked and do this in the timeout handler. */
566 check_line_data.doc = doc;
567 check_line_data.line_number = line_number;
568 check_line_data.line_count = line_count;
569
570 /* check only once in a while */
571 if (! need_delay())
572 check_lines(NULL);
573 }
574
575
sc_gui_editor_notify(GObject * object,GeanyEditor * editor,SCNotification * nt,gpointer data)576 gboolean sc_gui_editor_notify(GObject *object, GeanyEditor *editor,
577 SCNotification *nt, gpointer data)
578 {
579 if (! sc_info->check_while_typing)
580 return FALSE;
581
582 if (nt->modificationType & (SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT))
583 {
584 check_on_text_changed(editor->document, nt->position, nt->linesAdded);
585 }
586
587 return FALSE;
588 }
589
590
591 #if ! GTK_CHECK_VERSION(2, 16, 0)
gtk_menu_item_set_label(GtkMenuItem * menu_item,const gchar * label)592 static void gtk_menu_item_set_label(GtkMenuItem *menu_item, const gchar *label)
593 {
594 if (GTK_BIN(menu_item)->child != NULL)
595 {
596 GtkWidget *child = GTK_BIN(menu_item)->child;
597
598 if (GTK_IS_LABEL(child))
599 gtk_label_set_text(GTK_LABEL(child), label);
600 }
601 }
602 #endif
603
604
update_labels(void)605 static void update_labels(void)
606 {
607 gchar *label;
608
609 label = g_strdup_printf(_("Default (%s)"),
610 (sc_info->default_language != NULL) ? sc_info->default_language : _("unknown"));
611
612 gtk_menu_item_set_label(GTK_MENU_ITEM(sc_info->submenu_item_default), label);
613
614 g_free(label);
615
616 #if GTK_CHECK_VERSION(2, 12, 0)
617 if (sc_info->toolbar_button != NULL)
618 {
619 gchar *text = g_strdup_printf(
620 _("Toggle spell check while typing (current language: %s)"),
621 (sc_info->default_language != NULL) ? sc_info->default_language : _("unknown"));
622 gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(sc_info->toolbar_button), text);
623 g_free(text);
624 }
625 #endif
626 }
627
628
menu_item_toggled_cb(GtkCheckMenuItem * menuitem,gpointer gdata)629 static void menu_item_toggled_cb(GtkCheckMenuItem *menuitem, gpointer gdata)
630 {
631 GeanyDocument *doc;
632
633 if (sc_ignore_callback)
634 return;
635
636 if (menuitem != NULL &&
637 GTK_IS_CHECK_MENU_ITEM(menuitem) &&
638 ! gtk_check_menu_item_get_active(menuitem))
639 {
640 return;
641 }
642 doc = document_get_current();
643
644 /* Another language was chosen from the menu item, so make it default for this session. */
645 if (gdata != NULL)
646 {
647 setptr(sc_info->default_language, g_strdup(gdata));
648 sc_speller_reinit_enchant_dict();
649 sc_gui_update_menu();
650 update_labels();
651 }
652
653 perform_check(doc);
654 }
655
656
sc_gui_kb_run_activate_cb(guint key_id)657 void sc_gui_kb_run_activate_cb(guint key_id)
658 {
659 perform_check(document_get_current());
660 }
661
662
sc_gui_kb_toggle_typing_activate_cb(guint key_id)663 void sc_gui_kb_toggle_typing_activate_cb(guint key_id)
664 {
665 sc_info->check_while_typing = ! sc_info->check_while_typing;
666
667 print_typing_changed_message();
668
669 sc_gui_update_toolbar();
670 }
671
672
free_editor_menu_items(void)673 static void free_editor_menu_items(void)
674 {
675 if (sc_info->edit_menu != NULL)
676 {
677 gtk_widget_destroy(sc_info->edit_menu);
678 sc_info->edit_menu = NULL;
679 }
680 if (sc_info->edit_menu_sep != NULL)
681 {
682 gtk_widget_destroy(sc_info->edit_menu_sep);
683 sc_info->edit_menu_sep = NULL;
684 }
685 if (sc_info->edit_menu_items != NULL)
686 {
687 g_slist_free_full(sc_info->edit_menu_items, (GDestroyNotify) gtk_widget_destroy);
688 sc_info->edit_menu_items = NULL;
689 }
690 }
691
692
sc_gui_recreate_editor_menu(void)693 void sc_gui_recreate_editor_menu(void)
694 {
695 free_editor_menu_items();
696 if (sc_info->show_editor_menu_item_sub_menu)
697 {
698 sc_info->edit_menu = ui_image_menu_item_new(GTK_STOCK_SPELL_CHECK, _("Spelling Suggestions"));
699 gtk_container_add(GTK_CONTAINER(geany->main_widgets->editor_menu), sc_info->edit_menu);
700 gtk_menu_reorder_child(GTK_MENU(geany->main_widgets->editor_menu), sc_info->edit_menu, 0);
701
702 sc_info->edit_menu_sep = gtk_separator_menu_item_new();
703 gtk_container_add(GTK_CONTAINER(geany->main_widgets->editor_menu), sc_info->edit_menu_sep);
704 gtk_menu_reorder_child(GTK_MENU(geany->main_widgets->editor_menu), sc_info->edit_menu_sep, 1);
705
706 gtk_widget_show_all(sc_info->edit_menu);
707 }
708 }
709
710
sc_gui_update_menu(void)711 void sc_gui_update_menu(void)
712 {
713 GtkWidget *menu_item;
714 guint i;
715 static gboolean need_init = TRUE;
716 GSList *group = NULL;
717 gchar *label;
718
719 if (need_init)
720 {
721 gtk_container_add(GTK_CONTAINER(geany->main_widgets->tools_menu), sc_info->menu_item);
722 need_init = FALSE;
723 }
724
725 if (sc_info->main_menu != NULL)
726 gtk_widget_destroy(sc_info->main_menu);
727
728 sc_info->main_menu = gtk_menu_new();
729 gtk_menu_item_set_submenu(GTK_MENU_ITEM(sc_info->menu_item), sc_info->main_menu);
730
731 sc_info->submenu_item_default = gtk_menu_item_new_with_label(NULL);
732 gtk_container_add(GTK_CONTAINER(sc_info->main_menu), sc_info->submenu_item_default);
733 g_signal_connect(sc_info->submenu_item_default, "activate",
734 G_CALLBACK(menu_item_toggled_cb), NULL);
735
736 update_labels();
737
738 menu_item = gtk_separator_menu_item_new();
739 gtk_container_add(GTK_CONTAINER(sc_info->main_menu), menu_item);
740
741 sc_ignore_callback = TRUE;
742 for (i = 0; i < sc_info->dicts->len; i++)
743 {
744 label = g_ptr_array_index(sc_info->dicts, i);
745 menu_item = gtk_radio_menu_item_new_with_label(group, label);
746 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(menu_item));
747 if (utils_str_equal(sc_info->default_language, label))
748 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu_item), TRUE);
749 gtk_container_add(GTK_CONTAINER(sc_info->main_menu), menu_item);
750 g_signal_connect(menu_item, "toggled", G_CALLBACK(menu_item_toggled_cb), label);
751 }
752 sc_ignore_callback = FALSE;
753 gtk_widget_show_all(sc_info->main_menu);
754 }
755
756
sc_gui_init(void)757 void sc_gui_init(void)
758 {
759 clickinfo.word = NULL;
760 sc_info->edit_menu_items = NULL;
761 sc_info->edit_menu = NULL;
762 sc_info->edit_menu_sep = NULL;
763 sc_info->edit_menu_items = NULL;
764
765 sc_gui_recreate_editor_menu();
766 }
767
768
sc_gui_free(void)769 void sc_gui_free(void)
770 {
771 g_free(clickinfo.word);
772 if (check_line_data.check_while_typing_idle_source_id != 0)
773 g_source_remove(check_line_data.check_while_typing_idle_source_id);
774 if (sc_info->toolbar_button != NULL)
775 gtk_widget_destroy(GTK_WIDGET(sc_info->toolbar_button));
776 free_editor_menu_items();
777 }
778