1 /* gui.c
2 * Artha - Free cross-platform open thesaurus
3 * Copyright (C) 2009, 2010 Sundaram Ramaswamy, legends2k@yahoo.com
4 *
5 * Artha is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * Artha 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
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with Artha; if not, write to the Free Software Foundation, Inc.,
17 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20
21 /*
22 * GUI Code
23 */
24
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include "gui.h"
31
32 /* Global variables */
33
34 static const gchar *strv_authors[] = {"Sundaram Ramaswamy <legends2k@yahoo.com>", NULL};
35
36 /* Names of relative tree tab widgets from UI file
37 Note that the 'tree" prefix will be stripped and will be used within code */
38 static const gchar *relative_tree[] = {"treeSynonyms", "treeAntonyms", "treeDerivatives", "treePertainyms", "treeAttributes", "treeSimilar",
39 "treeDomain", "treeCauses", "treeEntails", "treeHypernyms", "treeHyponyms", "treeHolonyms", "treeMeronyms"};
40
41 #define DOMAINS_COUNT (CLASS_END - CLASSIF_START + 1)
42 static const gchar *domain_types[] = {"Topic", "Usage", "Region", "Topic Terms", "Usage Terms", "Regional Terms"};
43
44 #define HOLO_MERO_COUNT 3
45 static const gchar *holo_mero_types[][3] = {{"Member Of", "Substance Of", "Part Of"}, {"Has Members", "Has Substances", "Has Parts"}};
46
47 #define FAMILIARITY_COUNT 8
48 static const gchar *familiarity[] = {"extremely rare","very rare","rare","uncommon","common", "familiar","very familiar","extremely familiar"};
49 static const gchar *freq_colors[] = {"Black", "SaddleBrown", "FireBrick", "SeaGreen", "DarkOrange", "gold", "PaleGoldenrod", "PeachPuff1"};
50 /* words for checking familiarity types - none, scroll (v), scroll (n), alright, sequence, set (n), set (v), give */
51
52 /* notifier_enabled is for the setting "Notify" and *notifier is for the module availability */
53 static GSList *results = NULL;
54 static gboolean was_double_click = FALSE, last_search_successful = FALSE, advanced_mode = FALSE, auto_contract = FALSE;
55 static gboolean hotkey_set = FALSE, mod_suggest = FALSE;
56 // options which default to true
57 static gboolean notifier_enabled = TRUE, show_polysemy = TRUE, launch_minimized = TRUE, show_trayicon = TRUE;
58 static gboolean last_lookup_a_notification = FALSE;
59 static gchar last_search[MAX_LEMMA_LEN] = "";
60 #ifdef X11_AVAILABLE
61 static Display *dpy = NULL;
62 gboolean hotkey_processing = FALSE;
63 guint32 last_hotkey_time = 0;
64 static guint num_lock_mask = 0, caps_lock_mask = 0, scroll_lock_mask = 0;
65 #endif
66 static guint hotkey_trials[] = {GDK_w, GDK_a, GDK_t, GDK_q};
67 GtkAccelKey app_hotkey = {0};
68 static gint notify_toolbar_index = -1;
69 static guint status_msg_context_id = 0;
70 static GString *wordnet_terms = NULL;
71 NotifyNotification *mod_notifier = NULL;
72 static GtkCheckMenuItem *menu_notify = NULL;
73 static GKeyFile *config_file = NULL;
74
75 #ifdef G_OS_WIN32
76 static HWND hMainWindow = NULL;
77 #endif
78
79
80 /* function declarations */
81
82 #ifdef X11_AVAILABLE
83 static int x_error_handler(Display *dpy, XErrorEvent *xevent);
84 #endif // X11_AVAILABLE
85 static void show_window(GtkWindow *window);
86 static void notification_toggled(GObject *obj, gpointer user_data);
87 static void mode_toggled(GtkToggleToolButton *toggle_button, gpointer user_data);
88 static void trayicon_menu_toggled(GtkMenuItem *menu, GtkBuilder *gui_builder);
89 static gchar* strip_invalid_edges(gchar *selection);
90 static gchar* strip_non_alpha_num(const gchar *str);
91 static gboolean are_same_after_strip(const gchar *strA, const gchar *strB);
92 #ifdef G_OS_WIN32
93 static inline gboolean is_alt_pressed();
94 static inline void print_last_error();
95 static gchar* win32_capture_selection();
96 static gboolean link_launch(GtkAboutDialog *about_dialog, gchar *uri, gpointer user_data);
97 #endif
98 static void notification_lookup(gchar *selection, GtkComboBox *combo_query);
99 static GdkFilterReturn hotkey_pressed(GdkXEvent *xevent, GdkEvent *event, gpointer user_data);
100 static void status_icon_activate(GtkStatusIcon *status_icon, gpointer user_data);
101 static void status_icon_popup(GtkStatusIcon *status_icon, guint button, guint active_time, gpointer user_data);
102 static void about_response_handle(GtkDialog *about_dialog, gint response_id, gpointer user_data);
103 static void about_activate(GtkToolButton *menu_item, gpointer user_data);
104 static void quit_activate(GtkWidget *widget, gpointer user_data);
105 static guint8 get_frequency(guint sense_count);
106 static void relatives_clear_all(GtkBuilder *gui_builder);
107 static void antonyms_load(GSList *antonyms, GtkBuilder *gui_builder);
108 static guint16 append_term(gchar *target, gchar *term, gboolean is_heading);
109 static void build_tree(GNode *node, GtkTreeStore *tree_store, GtkTreeIter *parent_iter);
110 static void add_nodes(GNode *tree, GtkTreeStore *tree_store, GtkTreeIter *node_parent, GSList **dupe_finder_list);
111 static void trees_load(GSList *properties, GtkBuilder *gui_builder, WNIRequestFlags id);
112 static void holo_mero_load(GSList *properties, GtkBuilder *gui_builder, WNIRequestFlags id);
113 static void list_relatives_load(GSList *properties, GtkBuilder *gui_builder, WNIRequestFlags id);
114 static void domains_load(GSList *properties, GtkBuilder *gui_builder);
115 static guint8 get_attribute_pos();
116 static void relatives_load(GtkBuilder *gui_builder, gboolean reset_tabs);
117 static gchar* wildmat_to_regex(const gchar *wildmat);
118 static gboolean set_regex_results(const gchar *wildmat_exp, GtkBuilder *gui_builder);
119 static gboolean is_wildmat_expr();
120 static gboolean handle_wildmat_expr(GtkBuilder *gui_builder, GtkTextBuffer *buffer);
121 static gboolean is_entry_unique(GtkTreeModel *query_list_store, GtkTreePath *path, GtkTreeIter *iter, gchar **lemma);
122 static gboolean update_history(GtkComboBox *combo_query, char *lemma);
123 static gboolean update_history_and_save(GtkComboBox *combo_query, char *lemma);
124 static void show_definitions(GtkBuilder *gui_builder, GtkTextBuffer *buffer);
125 static void show_suggestions(GtkBuilder *gui_builder, GtkTextBuffer *buffer, GtkTextIter *cur, GSList *suggestions);
126 static void construct_show_notification();
127 static void show_searching(GtkBuilder *gui_builder);
128 static void query_wni(gchar *search_str,
129 WNIRequestFlags lookup_type,
130 GSList **suggestions);
131 static void button_search_click(GtkButton *button, gpointer user_data);
132 static void combo_query_changed(GtkComboBox *combo_query, gpointer user_data);
133 static gboolean text_view_button_pressed(GtkWidget *widget, GdkEventButton *event, gpointer user_data);
134 static gboolean text_view_button_released(GtkWidget *widget, GdkEventButton *event, gpointer user_data);
135 static void text_view_selection_made(GtkWidget *widget, GtkSelectionData *sel_data, guint time, gpointer user_data);
136 static void expander_clicked(GtkExpander *expander, gpointer user_data);
137 static void query_list_updated(GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter, gpointer user_data);
138 static GtkTextMark *highlight_definition(guint8 id, guint16 sense, GtkTextView *text_view);
139 static void highlight_senses_from_synonyms(guint16 relative_index, GtkTextView *text_view);
140 static void highlight_senses_from_antonyms( guint8 category, guint16 relative_index, guint16 sub_level, GtkTextView *text_view);
141 static void highlight_senses_from_domain(guint8 category, guint16 relative_index, GtkTextView *text_view);
142 static void highlight_senses_from_relative_lists(WNIRequestFlags id, guint16 relative_index, GtkTextView *text_view);
143 static void relative_selection_changed(GtkTreeView *tree_view, gpointer user_data);
144 static void relative_row_activated(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data);
145 static void clear_history(GtkMenuItem *menu_item, GtkListStore *list_store_query);
146 static void save_history_to_file(GtkMenuItem *menu_item, gpointer user_data);
147 static void query_combo_popup(GtkEntry *query_entry_widget, GtkMenu *popup_menu, GtkListStore *list_store_query);
148 static void load_history(GtkListStore *list_store_query);
149 static void create_stores_renderers(GtkBuilder *gui_builder);
150 static void button_next_clicked(GtkToolButton *toolbutton, gpointer user_data);
151 static void button_prev_clicked(GtkToolButton *toolbutton, gpointer user_data);
152 static gboolean combo_query_scroll(GtkWidget *widget, GdkEventScroll *event, gpointer user_data);
153 static gboolean close_window(GtkAccelGroup *accel_group, GObject *acceleratable, guint keyval,
154 GdkModifierType modifier, GObject *user_data);
155 static void setup_toolbar(GtkBuilder *gui_builder);
156 static GtkMenu *create_popup_menu(GtkBuilder *gui_builder);
157 static void create_text_view_tags(GtkBuilder *gui_builder);
158 static void load_preference_hotkey(gboolean *first_run);
159 static gboolean load_preferences(GtkWindow *parent);
160 static gboolean update_history_in_file(const gchar *term);
161 static void save_preferences_to_file();
162 static void save_preferences();
163 static gboolean autocomplete_selected(GtkEntryCompletion *query_completion, GtkTreeModel *model, GtkTreeIter *iter, GtkButton *button);
164 static void show_loading(GtkBuilder *gui_builder);
165
166 typedef struct
167 {
168 GtkBuilder *gui_builder;
169 GtkListStore *completion_list;
170 gchar *index_file_contents;
171 gsize index_file_length;
172 gsize pos_in_file;
173 gchar *last_lemma;
174 }
175 WordnetTermsLoaderData;
176 static void attach_loaded_terms(WordnetTermsLoaderData *loader_data);
177 static gboolean terms_loader(WordnetTermsLoaderData *loader_data);
178 static gboolean wordnet_terms_load(WordnetTermsLoaderData *loader_data);
179
180 #ifdef X11_AVAILABLE
181 static void lookup_ignorable_modifiers(void);
182 #elif defined G_OS_WIN32
183
184 #ifndef MOD_NOREPEAT
185 #define MOD_NOREPEAT 0x4000
186 #endif
187
188 typedef struct
189 {
190 guint gdk_key;
191 BYTE vk_key;
192 } gdk_vk;
193
194 static gboolean try_convert_to_vk(guint gdk_key, BYTE *vk);
195 #endif //X11_AVAILABLE
196 gboolean grab_ungrab_with_ignorable_modifiers(GtkAccelKey *binding, gboolean grab);
197 static gboolean register_unregister_hotkey(gboolean first_run, gboolean setup_hotkey);
198 static void setup_settings_dialog(GtkBuilder *gui_builder);
199 static void set_settings_to_check_boxes(GtkBuilder *gui_builder);
200 static void get_settings_from_check_boxes(GtkBuilder *gui_builder,
201 gboolean *new_trayicon_show,
202 gboolean *new_launch_min,
203 gboolean *new_polysemy_show);
204 static void apply_and_save_settings(GtkBuilder *gui_builder);
205 static void revert_settings(GtkBuilder *gui_builder, GtkAccelKey *hotkey_backup);
206 static void show_settings_dialog(GtkToolButton *toolbutton, gpointer user_data);
207 static void destructor(GtkBuilder *gui_builder);
208 static void show_message_dlg(GtkWidget *parent_window, MessageResposeCode msg_code);
209 static gboolean window_visibility_toggled(GtkWidget *widget, GdkEventVisibility *event, gpointer user_data);
210
211
212
213 #ifdef X11_AVAILABLE
x_error_handler(Display * dpy,XErrorEvent * xevent)214 static int x_error_handler(Display *dpy, XErrorEvent *xevent)
215 {
216 if(BadAccess == xevent->error_code)
217 {
218 g_warning("Hotkey combo already occupied!\n");
219 }
220 else
221 g_error("X Server Error: %d\n", xevent->error_code);
222
223 return -1;
224 }
225 #endif // X11_AVAILABLE
226
show_window(GtkWindow * window)227 static void show_window(GtkWindow *window)
228 {
229 #ifdef X11_AVAILABLE
230 tomboy_window_present_hardcore(window);
231 #else
232 gtk_window_present(window);
233 #endif
234 }
235
236 /*
237 This will be called for both notify check pop-up menu and notify tool bar button
238 Hence the first argument is GObject. Since both have the propery "active" as its
239 current state, this common function will suffice for notification toggling.
240 */
notification_toggled(GObject * obj,gpointer user_data)241 static void notification_toggled(GObject *obj, gpointer user_data)
242 {
243 GtkBuilder *gui_builder = GTK_BUILDER(user_data);
244 GtkToolbar *toolbar = GTK_TOOLBAR(gtk_builder_get_object(gui_builder, TOOLBAR));
245 GtkToolItem *toolbar_notify = gtk_toolbar_get_nth_item(toolbar, notify_toolbar_index);
246 gboolean prev_state_of_notification = notifier_enabled; // flag to prevent showing hotkey-not-set alert twice
247
248 g_object_get(obj, "active", ¬ifier_enabled, NULL);
249
250 g_object_set(G_OBJECT(toolbar_notify), "active", notifier_enabled, NULL);
251 gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(toolbar_notify), notifier_enabled ? GTK_STOCK_YES : GTK_STOCK_NO);
252
253 gtk_check_menu_item_set_active(menu_notify, notifier_enabled);
254
255 save_preferences();
256
257 /* if not hotkey is set; then there's no point in enabling notifications
258 intimate this to the user */
259 if(!hotkey_set && notifier_enabled && prev_state_of_notification != notifier_enabled)
260 show_message_dlg(NULL, MSG_HOTKEY_NOTSET);
261 }
262
mode_toggled(GtkToggleToolButton * toggle_button,gpointer user_data)263 static void mode_toggled(GtkToggleToolButton *toggle_button, gpointer user_data)
264 {
265 GtkBuilder *gui_builder = GTK_BUILDER(user_data);
266
267 advanced_mode = gtk_toggle_tool_button_get_active(toggle_button);
268
269 if(last_search_successful)
270 {
271 G_MESSAGE("Re-requesting with changed mode: %d\n", advanced_mode);
272
273 wni_request_nyms(last_search, &results, WORDNET_INTERFACE_ALL, advanced_mode);
274 relatives_clear_all(gui_builder);
275 relatives_load(gui_builder, FALSE);
276 }
277
278 save_preferences();
279 }
280
trayicon_menu_toggled(GtkMenuItem * menu,GtkBuilder * gui_builder)281 static void trayicon_menu_toggled(GtkMenuItem *menu, GtkBuilder *gui_builder)
282 {
283 GtkStatusIcon *status_icon = GTK_STATUS_ICON(gtk_builder_get_object(gui_builder, STATUS_ICON));
284
285 show_trayicon = FALSE;
286 gtk_status_icon_set_visible(status_icon, show_trayicon);
287 save_preferences();
288 }
289
strip_invalid_edges(gchar * selection)290 static gchar* strip_invalid_edges(gchar *selection)
291 {
292 guint i = 0;
293 gboolean alphanum_encounterd = FALSE;
294
295 if(selection)
296 {
297 while(selection[i] != '\0')
298 {
299 if(!g_ascii_isalnum(selection[i]) && selection[i] != '-' && selection[i] != '_' &&
300 selection[i] != ' ' && selection[i] != '\'' && selection[i] != '.')
301 {
302 if(alphanum_encounterd)
303 {
304 selection[i] = '\0';
305 break;
306 }
307 else
308 {
309 selection[i] = ' ';
310 }
311 }
312 else
313 {
314 // before setting the alphanum_encountered, make sure the current char isn't other valid ones
315 if(!alphanum_encounterd && selection[i] != '-' && selection[i] != '_' && selection[i] != ' ' &&
316 selection[i] != '\'' && selection[i] != '.')
317 alphanum_encounterd = TRUE;
318 }
319 i++;
320 }
321 }
322
323 return selection;
324 }
325
strip_non_alpha_num(const gchar * str)326 static gchar* strip_non_alpha_num(const gchar *str)
327 {
328 gchar *stripped_str = g_strstrip(g_strdup(str));
329 guint16 i = 0, j = 0;
330 gchar ch;
331 while((ch = str[i++]))
332 {
333 if(g_ascii_isalnum(ch))
334 stripped_str[j++] = ch;
335 }
336 stripped_str[j] = '\0';
337 return stripped_str;
338 }
339
are_same_after_strip(const gchar * strA,const gchar * strB)340 static gboolean are_same_after_strip(const gchar *strA, const gchar *strB)
341 {
342 gchar *strAStripped = strip_non_alpha_num(strA);
343 gchar *strBStripped = strip_non_alpha_num(strB);
344 // NOT op since 0 means strings match as per strcmp
345 gboolean matches = !g_strcmp0(strAStripped, strBStripped);
346
347 g_free(strAStripped); strAStripped = NULL;
348 g_free(strBStripped); strBStripped = NULL;
349
350 return matches;
351 }
352
353 #ifdef G_OS_WIN32
354
is_alt_pressed()355 static inline gboolean is_alt_pressed()
356 {
357 return (0 != (0x8000 & GetAsyncKeyState(VK_MENU)));
358 }
359
print_last_error()360 static inline void print_last_error()
361 {
362 g_warning("Error getting selection (Win32): %ld", GetLastError());
363 }
364
win32_capture_selection()365 static gchar* win32_capture_selection()
366 {
367 GUITHREADINFO guiThread = {sizeof(GUITHREADINFO)};
368 gchar *selection = NULL;
369 INPUT input[KEY_COUNT] = {{INPUT_KEYBOARD}, {INPUT_KEYBOARD}, {INPUT_KEYBOARD}, {INPUT_KEYBOARD}};
370 const WORD inputKey[2] = {VK_CONTROL, VK_INSERT};
371 HGLOBAL hglb = NULL;
372 LPSTR charArrayFromClipboard = NULL;
373
374 if(GetGUIThreadInfo(0, &guiThread))
375 {
376 if(is_alt_pressed())
377 {
378 input[0].ki.wVk = VK_MENU;
379 input[0].ki.dwFlags = KEYEVENTF_KEYUP;
380 SendInput(1, input, sizeof(INPUT));
381 input[0].ki.dwFlags = 0;
382 G_MESSAGE("Virtually released ALT!\n");
383 }
384
385 input[0].ki.wVk = input[2].ki.wVk = inputKey[0];
386 input[1].ki.wVk = input[3].ki.wVk = inputKey[1];
387
388 input[2].ki.dwFlags = (input[3].ki.dwFlags |= KEYEVENTF_KEYUP);
389 SendInput(KEY_COUNT, input, sizeof(INPUT));
390
391 Sleep(200);
392
393 if(OpenClipboard(NULL))
394 {
395 if((hglb = GetClipboardData(CF_TEXT)))
396 {
397 if((charArrayFromClipboard = (LPSTR) GlobalLock(hglb)))
398 {
399 selection = g_strdup(charArrayFromClipboard);
400 GlobalUnlock(hglb);
401 }
402 else
403 print_last_error();
404 }
405 else
406 print_last_error();
407
408 CloseClipboard();
409 }
410 else
411 print_last_error();
412 }
413 else
414 print_last_error();
415
416 return selection;
417 }
418
link_launch(GtkAboutDialog * about_dialog,gchar * uri,gpointer user_data)419 static gboolean link_launch(GtkAboutDialog *about_dialog, gchar *uri, gpointer user_data)
420 {
421 return ((int)ShellExecute(hMainWindow, "open", uri, NULL, NULL, SW_SHOWNORMAL) > 32);
422 }
423 #endif
424
notification_lookup(gchar * selection,GtkComboBox * combo_query)425 static void notification_lookup(gchar *selection, GtkComboBox *combo_query)
426 {
427 gchar *stripped_lookup = g_strstrip(strip_invalid_edges(selection));
428 // make sure this isn't a zero length string; valid but zero length
429 if(stripped_lookup[0])
430 {
431 gboolean new_search = TRUE;
432 if(last_search_successful)
433 new_search = (g_ascii_strcasecmp(last_search, stripped_lookup) != 0);
434
435 if(new_search)
436 query_wni(stripped_lookup, WORDNET_INTERFACE_NONE, NULL);
437
438 if(results)
439 {
440 construct_show_notification();
441
442 gchar *lemma = ((WNIDefinitionItem*)((WNIOverview*)((WNINym*)results->data)->data)->definitions_list->data)->lemma;
443 update_history_and_save(combo_query, lemma);
444
445 g_stpcpy(last_search, stripped_lookup);
446 last_search_successful = TRUE;
447 }
448 else
449 {
450 gboolean update_successful = notify_notification_update(mod_notifier,
451 stripped_lookup,
452 STR_STATUS_QUERY_FAILED,
453 "gtk-dialog-warning");
454 if(!update_successful || (FALSE == notify_notification_show(mod_notifier, NULL)))
455 {
456 g_warning("Failed to display notification!\n");
457 }
458 last_search_successful = FALSE;
459 }
460 }
461 last_lookup_a_notification = TRUE;
462 }
463
hotkey_pressed(GdkXEvent * xevent,GdkEvent * event,gpointer user_data)464 static GdkFilterReturn hotkey_pressed(GdkXEvent *xevent, GdkEvent *event, gpointer user_data)
465 {
466 gchar *selection = NULL;
467 GtkBuilder *gui_builder = GTK_BUILDER(user_data);
468 GtkWindow *window = GTK_WINDOW(gtk_builder_get_object(gui_builder, WINDOW_MAIN));
469
470 #ifdef X11_AVAILABLE
471 XEvent *xe = (XEvent*) xevent;
472
473 // Since you have not registerd filter_add for NULL, no need to check the key code, you will get a call only when its the hotkey, else it won't be called
474 // Try this by removing below if and try to print debug text
475 if(xe->type == KeyPress)
476 {
477 #elif defined G_OS_WIN32
478 PMSG msg = (PMSG) xevent;
479
480 if(WM_HOTKEY == msg->message)
481 {
482 #endif
483 GtkComboBox *combo_query = GTK_COMBO_BOX(gtk_builder_get_object(gui_builder, COMBO_QUERY));
484
485 #ifdef X11_AVAILABLE
486 hotkey_processing = TRUE;
487 last_hotkey_time = xe->xkey.time;
488
489 // get the clipboard text and strip off any invalid characters
490 selection = gtk_clipboard_wait_for_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY));
491 //strip_invalid_edges(selection);
492 #elif defined G_OS_WIN32
493 selection = win32_capture_selection();
494 #endif
495
496 if(selection)
497 {
498 // setting the text is needed for both notification and normal lookup
499 // since on unobscuring the window, a proper lookup needs to be done
500 GtkEntry *text_entry_query = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo_query)));
501 gtk_entry_set_text(text_entry_query, selection);
502 //gtk_editable_set_position(GTK_EDITABLE(text_entry_query), -1);
503 if(gtk_widget_get_visible(GTK_WIDGET(window)) || !notifier_enabled)
504 {
505 GtkButton *button_search = GTK_BUTTON(gtk_builder_get_object(gui_builder, BUTTON_SEARCH));
506 gtk_button_clicked(button_search);
507 }
508 // if notifier_enabled and notifier mod is present
509 else if(mod_notifier)
510 {
511 notification_lookup(selection, combo_query);
512 }
513 g_free(selection);
514 selection = NULL;
515 }
516 else
517 {
518 // see if in case notify is selected, should we popup or we should just notify "No selection made!"
519 show_window(window);
520 gtk_widget_grab_focus(GTK_WIDGET(combo_query));
521 }
522 #ifdef X11_AVAILABLE
523 hotkey_processing = FALSE;
524 #endif
525
526 return GDK_FILTER_REMOVE;
527 }
528 #ifdef G_OS_WIN32
529 else if(WM_ARTHA_RELAUNCH == msg->message)
530 {
531 show_window(window);
532 return GDK_FILTER_REMOVE;
533 }
534 #endif
535
536 return GDK_FILTER_CONTINUE;
537 }
538
539 static void status_icon_activate(GtkStatusIcon *status_icon, gpointer user_data)
540 {
541 GtkBuilder *gui_builder = GTK_BUILDER(user_data);
542 GtkWindow *window = GTK_WINDOW(gtk_builder_get_object(gui_builder, WINDOW_MAIN));
543 GtkComboBox *combo_query = NULL;
544
545 if(gtk_widget_get_visible(GTK_WIDGET(window)))
546 gtk_widget_hide(GTK_WIDGET(window));
547 else
548 {
549
550 /* close notifications, if any */
551 if(mod_notifier)
552 notify_notification_close(mod_notifier, NULL);
553 show_window(window);
554 combo_query = GTK_COMBO_BOX(gtk_builder_get_object(gui_builder, COMBO_QUERY));
555 gtk_widget_grab_focus(GTK_WIDGET(combo_query));
556 }
557 }
558
559 static void status_icon_popup(GtkStatusIcon *status_icon, guint button, guint active_time, gpointer user_data)
560 {
561 GtkMenu *menu = GTK_MENU(user_data);
562 gtk_menu_popup(menu, NULL, NULL, gtk_status_icon_position_menu, status_icon, button, active_time);
563 }
564
565 static void about_response_handle(GtkDialog *about_dialog, gint response_id, gpointer user_data)
566 {
567 switch(response_id)
568 {
569 case ARTHA_RESPONSE_REPORT_BUG:
570 {
571 #ifndef G_OS_WIN32
572 GError *err = NULL;
573 if(!gtk_show_uri(NULL, STR_BUG_WEBPAGE, GDK_CURRENT_TIME, &err))
574 {
575 GtkDialog *err_msg = GTK_DIALOG(gtk_message_dialog_new(
576 GTK_WINDOW(about_dialog),
577 GTK_DIALOG_DESTROY_WITH_PARENT,
578 GTK_MESSAGE_ERROR,
579 GTK_BUTTONS_CLOSE,
580 "Error opening %s\n\n%s",
581 STR_BUG_WEBPAGE,
582 err->message));
583 gtk_dialog_run(err_msg);
584 gtk_widget_destroy(GTK_WIDGET(err_msg));
585 g_clear_error(&err);
586 }
587 #else
588 link_launch(GTK_ABOUT_DIALOG(about_dialog), STR_BUG_WEBPAGE, NULL);
589 #endif
590 break;
591 }
592 default:
593 g_warning("About Dialog: Unhandled response_id: %d!\n", response_id);
594 break;
595 }
596 gtk_widget_destroy(GTK_WIDGET(about_dialog));
597 }
598
599 static void about_activate(GtkToolButton *menu_item, gpointer user_data)
600 {
601 GtkWidget *about_dialog = gtk_about_dialog_new();
602
603 g_object_set(G_OBJECT(about_dialog), "license", STR_LICENCE, "copyright", STR_COPYRIGHT,
604 "comments", STR_ABOUT, "authors", strv_authors, "version", PACKAGE_VERSION,
605 "wrap-license", TRUE,
606 "website-label", STR_WEBSITE_LABEL,
607 "website", PACKAGE_URL, NULL);
608
609 gtk_dialog_add_button(GTK_DIALOG(about_dialog), STR_REPORT_BUG, ARTHA_RESPONSE_REPORT_BUG);
610 g_signal_connect(about_dialog, "response", G_CALLBACK(about_response_handle), NULL);
611
612 #ifdef G_OS_WIN32
613 g_signal_connect(about_dialog, "activate-link", G_CALLBACK(link_launch), NULL);
614 #endif
615
616 gtk_dialog_run(GTK_DIALOG(about_dialog));
617 }
618
619 static void quit_activate(GtkWidget *widget, gpointer user_data)
620 {
621 G_DEBUG("Destroy called!\n");
622
623 gtk_main_quit();
624 }
625
626 static guint8 get_frequency(guint sense_count)
627 {
628 guint8 frequency = 0;
629
630 if(sense_count == 0) frequency = 0;
631 if(sense_count == 1) frequency = 1;
632 if(sense_count == 2) frequency = 2;
633 if(sense_count >= 3 && sense_count <= 4) frequency = 3;
634 if(sense_count >= 5 && sense_count <= 8) frequency = 4;
635 if(sense_count >= 9 && sense_count <= 16) frequency = 5;
636 if(sense_count >= 17 && sense_count <= 32) frequency = 6;
637 if(sense_count > 32 ) frequency = 7;
638
639 return frequency;
640 }
641
642 static void relatives_clear_all(GtkBuilder *gui_builder)
643 {
644 guint8 i = 0;
645 GtkTreeView *temp_tree_view = NULL;
646 GtkTreeStore *temp_tree_store = NULL;
647 GtkWidget *relative_tab = NULL;
648
649 for(i = 0; i < TOTAL_RELATIVES; i++)
650 {
651 temp_tree_view = GTK_TREE_VIEW(gtk_builder_get_object(gui_builder, relative_tree[i]));
652 relative_tab = gtk_widget_get_parent(GTK_WIDGET(temp_tree_view));
653
654 if(gtk_widget_get_visible(GTK_WIDGET(relative_tab)))
655 {
656 temp_tree_store = GTK_TREE_STORE(gtk_tree_view_get_model(temp_tree_view));
657 if(temp_tree_store)
658 gtk_tree_store_clear(temp_tree_store);
659 }
660 else
661 gtk_widget_show(relative_tab);
662 }
663 }
664
665 static void antonyms_load(GSList *antonyms, GtkBuilder *gui_builder)
666 {
667 WNIAntonymItem *temp_antonym_item = NULL;
668 GSList *implications = NULL;
669 GtkTreeView *tree_antonyms = GTK_TREE_VIEW(gtk_builder_get_object(gui_builder, relative_tree[TREE_ANTONYMS]));
670 GtkTreeStore *antonyms_tree_store = GTK_TREE_STORE(gtk_tree_view_get_model(tree_antonyms));
671 GtkTreeIter iter = {0}, sub_iter = {0}, direct_iter = {0}, indirect_iter = {0};
672 GtkTreePath *expand_path = NULL;
673 gboolean direct_deleted = FALSE, has_indirect = FALSE;
674
675 g_assert(antonyms_tree_store);
676
677 if(advanced_mode)
678 {
679 gtk_tree_store_append(antonyms_tree_store, &direct_iter, NULL);
680 gtk_tree_store_set(antonyms_tree_store, &direct_iter, 0, STR_ANTONYM_HEADER_DIRECT, 1, PANGO_WEIGHT_SEMIBOLD, 2, NULL, -1);
681 gtk_tree_store_append(antonyms_tree_store, &indirect_iter, NULL);
682 gtk_tree_store_set(antonyms_tree_store, &indirect_iter, 0, STR_ANTONYM_HEADER_INDIRECT, 1, PANGO_WEIGHT_SEMIBOLD, 2, NULL, -1);
683 }
684
685 while(antonyms)
686 {
687 temp_antonym_item = antonyms->data;
688 if(temp_antonym_item)
689 {
690 if(DIRECT_ANT == temp_antonym_item->relation)
691 {
692 if(advanced_mode)
693 gtk_tree_store_append(antonyms_tree_store, &iter, &direct_iter);
694 else
695 gtk_tree_store_append(antonyms_tree_store, &iter, NULL);
696 gtk_tree_store_set(antonyms_tree_store, &iter, 0, temp_antonym_item->term, 1, PANGO_WEIGHT_NORMAL, 2, NULL, -1);
697 }
698 else
699 {
700 iter = indirect_iter;
701 has_indirect = TRUE;
702 }
703 implications = temp_antonym_item->implications;
704 while(implications)
705 {
706 gtk_tree_store_append(antonyms_tree_store, &sub_iter, &iter);
707 gtk_tree_store_set(antonyms_tree_store, &sub_iter, 0, ((WNIImplication*)implications->data)->term,
708 1, PANGO_WEIGHT_NORMAL, 2, (DIRECT_ANT == temp_antonym_item->relation)?NULL:temp_antonym_item->term, -1);
709 implications = g_slist_next(implications);
710 }
711 }
712 antonyms = g_slist_next(antonyms);
713 }
714
715 if(advanced_mode)
716 {
717 // if only Direct is present, then Indirect will be deleted, only indirect_iter becomes invalid
718 // if only Indirect is present, then Direct will be deleted, and the indirect delete won't work, because the indirect_iter will get invalid
719 // if both are present, none deleted, no iters become invalid
720 // if both are not present, we won't be here in the first place ;)
721 // notice the NOT op. for assigning direct_deleted, which is imp. coz it says true when there are no children under Direct i.e. Direct was deleted
722 if((direct_deleted = !gtk_tree_model_iter_has_child(GTK_TREE_MODEL(antonyms_tree_store), &direct_iter)))
723 gtk_tree_store_remove(antonyms_tree_store, &direct_iter);
724 if(!gtk_tree_model_iter_has_child(GTK_TREE_MODEL(antonyms_tree_store), &indirect_iter))
725 gtk_tree_store_remove(antonyms_tree_store, &indirect_iter);
726
727 // expand one level
728 expand_path = gtk_tree_path_new_from_indices(0, -1);
729
730 // if direct list was deleted, then this is the indirect list that we are dealing, so expand, else don't
731 gtk_tree_view_expand_row(tree_antonyms, expand_path, direct_deleted);
732 gtk_tree_path_free(expand_path);
733
734 // if there is a list in this path "1", then it should be indirect
735 expand_path = gtk_tree_path_new_from_indices(1, -1);
736 gtk_tree_view_expand_row(tree_antonyms, expand_path, TRUE);
737 gtk_tree_path_free(expand_path);
738 expand_path = NULL;
739 }
740
741 gtk_tree_view_set_headers_visible(tree_antonyms, has_indirect);
742 }
743
744 static guint16 append_term(gchar *target, gchar *term, gboolean is_heading)
745 {
746 guint16 i = 0;
747
748 while(term[i] != '\0')
749 {
750 *target = term[i++];
751 target++;
752 }
753 *target = (is_heading)? ':' : ',';
754 ++target;
755 *target = ' ';
756
757 return (i + 2);
758 }
759
760 static void build_tree(GNode *node, GtkTreeStore *tree_store, GtkTreeIter *parent_iter)
761 {
762 GNode *child = NULL;
763 GSList *temp_list = NULL;
764 WNIImplication *imp = NULL;
765 GtkTreeIter tree_iter = {0};
766 guint32 i = 0;
767 gchar terms[MAX_CONCAT_STR] = "";
768
769 if((child = g_node_first_child(node)))
770 do
771 {
772 i = 0;
773 terms[0] = '\0';
774
775 temp_list = ((WNITreeList*)child->data)->word_list;
776 while(temp_list)
777 {
778 imp = (WNIImplication*) temp_list->data;
779 i += append_term(&terms[i], imp->term, FALSE);
780
781 temp_list = g_slist_next(temp_list);
782 }
783 terms[i - 2] = '\0';
784
785 gtk_tree_store_append(tree_store, &tree_iter, parent_iter);
786 gtk_tree_store_set(tree_store, &tree_iter, 0, terms, 1, PANGO_WEIGHT_NORMAL, -1);
787
788 build_tree(child, tree_store, &tree_iter);
789
790 } while((child = g_node_next_sibling(child)));
791 }
792
793 static void add_nodes(GNode *tree, GtkTreeStore *tree_store, GtkTreeIter *node_parent, GSList **dupe_finder_list)
794 {
795 WNIImplication *imp = NULL;
796 gchar terms[MAX_CONCAT_STR] = "";
797 GtkTreeIter iter = {0};
798 gboolean is_dupe = FALSE;
799 guint16 i = 0;
800 GSList *term_list = ((WNITreeList*)tree->data)->word_list;
801
802 if(advanced_mode)
803 {
804 while(term_list)
805 {
806 imp = (WNIImplication*) term_list->data;
807 i += append_term(&terms[i], imp->term, FALSE);
808
809 term_list = g_slist_next(term_list);
810 }
811 terms[i - 2] = '\0';
812
813 gtk_tree_store_append(tree_store, &iter, node_parent);
814 gtk_tree_store_set(tree_store, &iter, 0, terms, 1, PANGO_WEIGHT_NORMAL, -1);
815
816 build_tree(tree, tree_store, &iter);
817 }
818 else
819 {
820 while(term_list)
821 {
822 imp = (WNIImplication*) term_list->data;
823
824 is_dupe = FALSE;
825 /* if dupe check was asked by the caller, then check else continue with append */
826 if(NULL != dupe_finder_list)
827 {
828 if(NULL == g_slist_find_custom(*dupe_finder_list, imp->term, (GCompareFunc) &g_strcmp0))
829 *dupe_finder_list = g_slist_append(*dupe_finder_list, imp->term);
830 else
831 is_dupe = TRUE;
832 }
833
834 if(!is_dupe)
835 {
836 gtk_tree_store_append(tree_store, &iter, node_parent);
837 gtk_tree_store_set(tree_store, &iter, 0, imp->term, 1, PANGO_WEIGHT_NORMAL, -1);
838 }
839
840 term_list = g_slist_next(term_list);
841 }
842 }
843 }
844
845 static void trees_load(GSList *properties, GtkBuilder *gui_builder, WNIRequestFlags id)
846 {
847 GNode *tree = NULL;
848 GtkTreeView *tree_view = NULL;
849 GtkTreeStore *tree_store = NULL;
850 GSList *dupe_finder_list = NULL;
851 guint8 i = (WORDNET_INTERFACE_HYPERNYMS == id) ? TREE_HYPERNYMS : ((WORDNET_INTERFACE_HYPONYMS == id) ? TREE_HYPONYMS : TREE_PERTAINYMS);
852
853 tree_view = GTK_TREE_VIEW(gtk_builder_get_object(gui_builder, relative_tree[i]));
854 tree_store = GTK_TREE_STORE(gtk_tree_view_get_model(tree_view));
855
856 g_assert(tree_store);
857
858 while(properties)
859 {
860 if(properties->data)
861 tree = g_node_first_child((GNode*)properties->data);
862
863 while(tree)
864 {
865 add_nodes(tree, tree_store, NULL, &dupe_finder_list);
866 tree = g_node_next_sibling(tree);
867 }
868
869 properties = g_slist_next(properties);
870 }
871
872 g_slist_free(dupe_finder_list);
873 dupe_finder_list = NULL;
874 }
875
876 static void holo_mero_load(GSList *properties, GtkBuilder *gui_builder, WNIRequestFlags id)
877 {
878 GNode *tree = NULL;
879 GtkTreeIter type_iter[HOLO_MERO_COUNT] = {{0}}, *relative_parent = NULL;
880 GSList *dupe_finder_list[HOLO_MERO_COUNT] = {NULL};
881 guint8 i = (WORDNET_INTERFACE_MERONYMS == id) ? TREE_MERONYMS : TREE_HOLONYMS;
882 GtkTreePath *head_level = NULL;
883 GtkTreeView *tree_view = GTK_TREE_VIEW(gtk_builder_get_object(gui_builder, relative_tree[i]));
884 GtkTreeStore *tree_store = GTK_TREE_STORE(gtk_tree_view_get_model(tree_view));
885
886 g_assert(tree_store);
887
888 for(guint8 j = 0; j < HOLO_MERO_COUNT; ++j)
889 {
890 gtk_tree_store_append(tree_store, &type_iter[j], NULL);
891 gtk_tree_store_set(tree_store, &type_iter[j], 0, holo_mero_types[i - TREE_HOLONYMS][j], 1, PANGO_WEIGHT_SEMIBOLD, -1);
892 }
893
894 while(properties)
895 {
896 if(properties->data) tree = g_node_first_child((GNode*)properties->data);
897
898 while(tree)
899 {
900 i = (((WNITreeList*)tree->data)->type) - ((WORDNET_INTERFACE_HOLONYMS == id) ? ISMEMBERPTR : HASMEMBERPTR);
901 relative_parent = (i < HOLO_MERO_COUNT) ? &type_iter[i] : NULL;
902 add_nodes(tree, tree_store, relative_parent, &dupe_finder_list[i]);
903 tree = g_node_next_sibling(tree);
904 }
905
906 properties = g_slist_next(properties);
907 }
908
909 for(i = 0; i < HOLO_MERO_COUNT; i++)
910 {
911 // if a node has child then expand it, else remove it
912 if(gtk_tree_model_iter_has_child(GTK_TREE_MODEL(tree_store), &type_iter[i]))
913 {
914 head_level = gtk_tree_model_get_path(GTK_TREE_MODEL(tree_store), &type_iter[i]);
915 gtk_tree_view_expand_row(tree_view, head_level, FALSE);
916 gtk_tree_path_free(head_level);
917 head_level = NULL;
918 }
919 else
920 {
921 gtk_tree_store_remove(tree_store, &type_iter[i]);
922 }
923 g_slist_free(dupe_finder_list[i]);
924 dupe_finder_list[i] = NULL;
925 }
926 }
927
928 static void list_relatives_load(GSList *properties, GtkBuilder *gui_builder, WNIRequestFlags id)
929 {
930 guint16 i = 0;
931 guint text_weight = PANGO_WEIGHT_NORMAL;
932 WNIPropertyItem *temp_property = NULL;
933 GtkTreeView *tree_view = NULL;
934 GtkTreeStore *tree_store = NULL;
935 GtkTreeIter iter = {0};
936
937 for(i = 0; 1 != id; (id = id >> 1), i++);
938
939 tree_view = GTK_TREE_VIEW(gtk_builder_get_object(gui_builder, relative_tree[i]));
940 tree_store = GTK_TREE_STORE(gtk_tree_view_get_model(tree_view));
941 g_assert(tree_store);
942
943 while(properties)
944 {
945 temp_property = (WNIPropertyItem*) properties->data;
946 if(temp_property)
947 {
948 gtk_tree_store_append(tree_store, &iter, NULL);
949 text_weight = PANGO_WEIGHT_NORMAL;
950
951 // check if synonym has more than one mapping
952 // we try to get the 2nd term (I.e. index 1) instead of checking the length; parsing the whole list is insane
953 if(NULL != g_slist_nth(temp_property->mapping, 1))
954 text_weight = PANGO_WEIGHT_BOLD;
955
956 gtk_tree_store_set(tree_store, &iter, 0, temp_property->term, 1, text_weight, -1);
957 }
958 properties = g_slist_next(properties);
959 }
960 }
961
962 static void domains_load(GSList *properties, GtkBuilder *gui_builder)
963 {
964 guint8 i = 0;
965 WNIClassItem *class_item = NULL;
966 GtkTreeView *tree_class = GTK_TREE_VIEW(gtk_builder_get_object(gui_builder, relative_tree[TREE_DOMAIN]));
967 GtkTreeStore *class_tree_store = GTK_TREE_STORE(gtk_tree_view_get_model(tree_class));
968 GtkTreeIter iter = {0}, class_iter[DOMAINS_COUNT] = {{0}};
969
970 g_assert(class_tree_store);
971
972 for(i = 0; i < DOMAINS_COUNT; i++)
973 {
974 gtk_tree_store_append(class_tree_store, &class_iter[i], NULL);
975 gtk_tree_store_set(class_tree_store, &class_iter[i], 0, domain_types[i], 1, PANGO_WEIGHT_SEMIBOLD, -1);
976 }
977
978 while(properties)
979 {
980 class_item = (WNIClassItem*) properties->data;
981 if(class_item)
982 {
983 gtk_tree_store_append(class_tree_store, &iter, &class_iter[class_item->type - CLASSIF_CATEGORY]);
984 gtk_tree_store_set(class_tree_store, &iter, 0, class_item->term, 1, PANGO_WEIGHT_NORMAL, -1);
985 }
986 properties = g_slist_next(properties);
987 }
988
989 for(i = 0; i < DOMAINS_COUNT; i++)
990 {
991 if(!gtk_tree_model_iter_has_child(GTK_TREE_MODEL(class_tree_store), &class_iter[i]))
992 gtk_tree_store_remove(class_tree_store, &class_iter[i]);
993 }
994
995 gtk_tree_view_expand_all(tree_class);
996 }
997
998 static guint8 get_attribute_pos()
999 {
1000 GSList *temp_results = results, *def_list = NULL;
1001 WNINym *temp_nym = NULL;
1002 WNIPropertyItem *prop_item = NULL;
1003 WNIPropertyMapping *prop_mapping = NULL;
1004 WNIDefinitionItem *def_item = NULL;
1005
1006 while(temp_results)
1007 {
1008 temp_nym = (WNINym*) temp_results->data;
1009 if(WORDNET_INTERFACE_ATTRIBUTES == temp_nym->id)
1010 {
1011 prop_item = (WNIPropertyItem*) (((WNIProperties*) temp_nym->data)->properties_list)->data;
1012 prop_mapping = (WNIPropertyMapping*) prop_item->mapping->data;
1013 break;
1014 }
1015 temp_results = g_slist_next(temp_results);
1016 }
1017
1018 temp_results = results;
1019 while(temp_results)
1020 {
1021 temp_nym = (WNINym*) temp_results->data;
1022 if(WORDNET_INTERFACE_OVERVIEW == temp_nym->id)
1023 {
1024 def_list = ((WNIOverview*) temp_nym->data)->definitions_list;
1025 while(def_list)
1026 {
1027 if(def_list)
1028 {
1029 def_item = (WNIDefinitionItem*) def_list->data;
1030 if(def_item->id == prop_mapping->id)
1031 {
1032 return def_item->pos;
1033 }
1034 }
1035 def_list = g_slist_next(def_list);
1036 }
1037
1038 break;
1039 }
1040
1041 temp_results = g_slist_next(temp_results);
1042 }
1043
1044 return 0; // control should never reach here
1045 }
1046
1047 static void relatives_load(GtkBuilder *gui_builder, gboolean reset_tabs)
1048 {
1049 GSList *temp_results = results;
1050 WNINym *temp_nym = NULL;
1051
1052 guint8 i = 0;
1053 gboolean store_empty = TRUE, page_set = FALSE;
1054 GtkTreeView *temp_tree_view = NULL;
1055 GtkTreeStore *temp_tree_store = NULL;
1056 GtkWidget *relative_tab = NULL, *expander = NULL;
1057 GtkTreeIter temp_iter = {0};
1058
1059 GtkLabel *tab_label = NULL;
1060
1061 // instead of invalidate rect redraw, we just toggle visibility
1062 expander = GTK_WIDGET(gtk_builder_get_object(gui_builder, EXPANDER));
1063 gtk_widget_hide(expander);
1064
1065 while(temp_results)
1066 {
1067 temp_nym = (WNINym*) temp_results->data;
1068 switch(temp_nym->id)
1069 {
1070 case WORDNET_INTERFACE_OVERVIEW:
1071 {
1072 list_relatives_load(((WNIOverview*) temp_nym->data)->synonyms_list, gui_builder, temp_nym->id);
1073 break;
1074 }
1075 case WORDNET_INTERFACE_ANTONYMS:
1076 {
1077 antonyms_load(((WNIProperties*) temp_nym->data)->properties_list, gui_builder);
1078 break;
1079 }
1080 case WORDNET_INTERFACE_ATTRIBUTES:
1081 case WORDNET_INTERFACE_DERIVATIONS:
1082 case WORDNET_INTERFACE_CAUSES:
1083 case WORDNET_INTERFACE_ENTAILS:
1084 case WORDNET_INTERFACE_SIMILAR:
1085 {
1086 list_relatives_load(((WNIProperties*) temp_nym->data)->properties_list, gui_builder, temp_nym->id);
1087 break;
1088 }
1089 case WORDNET_INTERFACE_PERTAINYMS:
1090 case WORDNET_INTERFACE_HYPERNYMS:
1091 case WORDNET_INTERFACE_HYPONYMS:
1092 {
1093 trees_load(((WNIProperties*) temp_nym->data)->properties_list, gui_builder, temp_nym->id);
1094 break;
1095 }
1096 case WORDNET_INTERFACE_HOLONYMS:
1097 case WORDNET_INTERFACE_MERONYMS:
1098 {
1099 holo_mero_load(((WNIProperties*) temp_nym->data)->properties_list, gui_builder, temp_nym->id);
1100 break;
1101 }
1102 case WORDNET_INTERFACE_CLASS:
1103 {
1104 domains_load(((WNIProperties*) temp_nym->data)->properties_list, gui_builder);
1105 break;
1106 }
1107 default:
1108 break;
1109 }
1110 temp_results = g_slist_next(temp_results);
1111 i++;
1112 }
1113
1114 // set tab visibility
1115 for(i = 0; i < TOTAL_RELATIVES; i++)
1116 {
1117 temp_tree_view = GTK_TREE_VIEW(gtk_builder_get_object(gui_builder, relative_tree[i]));
1118 relative_tab = gtk_widget_get_parent(GTK_WIDGET(temp_tree_view));
1119
1120 temp_tree_store = GTK_TREE_STORE(gtk_tree_view_get_model(temp_tree_view));
1121 if(temp_tree_store)
1122 {
1123 store_empty = !gtk_tree_model_get_iter_first(GTK_TREE_MODEL(temp_tree_store), &temp_iter);
1124 }
1125 if(store_empty || NULL == temp_tree_store)
1126 gtk_widget_hide(relative_tab);
1127 else if(reset_tabs && !page_set)
1128 {
1129 gtk_notebook_set_current_page(GTK_NOTEBOOK(gtk_builder_get_object(gui_builder, NOTEBOOK)), i);
1130 page_set = TRUE;
1131 }
1132
1133 // see if the term has attributes in first place (check visibility for that)
1134 // if available, set the label based on the POS
1135 if(gtk_widget_get_visible(GTK_WIDGET(relative_tab)) && TREE_ATTRIBUTES == i)
1136 {
1137 tab_label = GTK_LABEL(gtk_builder_get_object(gui_builder, LABEL_ATTRIBUTES));
1138 if(get_attribute_pos() == ADJ)
1139 gtk_label_set_text(tab_label, LABEL_TEXT_ATTRIBUTE_OF);
1140 else
1141 gtk_label_set_text(tab_label, LABEL_TEXT_ATTRIBUTES);
1142 }
1143 }
1144
1145 // instead of invalidate rect redraw, we just toggle visibility
1146 gtk_widget_show(expander);
1147
1148 // if the expander was contracted automatically, then expand it back again
1149 if(!gtk_expander_get_expanded(GTK_EXPANDER(expander)) && auto_contract)
1150 gtk_expander_set_expanded(GTK_EXPANDER(expander), TRUE);
1151 }
1152
1153 static gchar* wildmat_to_regex(const gchar *wildmat)
1154 {
1155 guint16 i = 0;
1156 gchar ch = 0;
1157 GString *regex = g_string_new("^");
1158
1159 /* Conversion Rules are simple at this point.
1160 1. To get the proper term from start to end, make sure the beginning and end of the regex is ^ and $ respectively
1161 2. E.g. in 'a*b', * is applied to a in regex, while in wildmat, 'a' & 'b' is sure included and * is anything b/w them
1162 so in regex make it as 'a.*b', which will make * applied to . (dot means any char. except newline in regex)
1163 3. A dot/period in regex acts as a Joker (?) in wildmat, so make a direct conv.
1164 4. Other chars go in as such, its the same in both for stuff like [r|m|s]{m,n} or [a-m]+, etc. */
1165
1166 while((ch = wildmat[i++]) != '\0')
1167 {
1168 if(ch == '*')
1169 regex = g_string_append(regex, ".*");
1170 else if(ch == '?')
1171 regex = g_string_append_c(regex, '.');
1172 else
1173 regex = g_string_append_c(regex, ch);
1174
1175 }
1176
1177 regex = g_string_append_c(regex, '$');
1178 regex = g_string_append_c(regex, '\0');
1179
1180 return g_string_free(regex, FALSE);
1181 }
1182
1183 static gboolean set_regex_results(const gchar *wildmat_exp, GtkBuilder *gui_builder)
1184 {
1185 gchar *regex_pattern = NULL, *lemma = NULL;
1186 GRegex *regex = NULL;
1187 GMatchInfo *match_info = NULL;
1188 guint32 count = 0;
1189 GtkTextView *text_view = NULL;
1190 GtkTextBuffer *text_buffer = NULL;
1191 GtkTextIter cur = {0};
1192 GtkStatusbar *status_bar = GTK_STATUSBAR(gtk_builder_get_object(gui_builder, STATUSBAR));
1193 gchar status_msg[MAX_STATUS_MSG] = "";
1194 gboolean results_set = FALSE;
1195
1196
1197 // convert the wilmat expr. to PERL regex
1198 if(wildmat_exp)
1199 regex_pattern = wildmat_to_regex(wildmat_exp);
1200
1201 if(regex_pattern)
1202 {
1203 text_view = GTK_TEXT_VIEW(gtk_builder_get_object(gui_builder, TEXT_VIEW_DEFINITIONS));
1204 text_buffer = gtk_text_view_get_buffer(text_view);
1205
1206 // clear text and get the iter
1207 gtk_text_buffer_set_text(text_buffer, "", -1);
1208 gtk_text_buffer_get_start_iter(text_buffer, &cur);
1209
1210 // set heading that Regex mode is now in action
1211 gtk_text_buffer_insert_with_tags_by_name(text_buffer, &cur, STR_REGEX_DETECTED, -1, TAG_LEMMA, NULL);
1212 gtk_text_buffer_insert(text_buffer, &cur, NEW_LINE, -1);
1213
1214 // compile a GRegex
1215 regex = g_regex_new(regex_pattern, G_REGEX_MULTILINE|G_REGEX_CASELESS|G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, G_REGEX_MATCH_NOTEMPTY, NULL);
1216
1217 if(regex)
1218 {
1219 // do the actual regex matching and get the count
1220 g_regex_match(regex, wordnet_terms->str, G_REGEX_MATCH_NOTEMPTY, &match_info);
1221 count = g_match_info_get_match_count(match_info);
1222
1223 // make a header for the search results to follow
1224 if(count > 0)
1225 {
1226 gtk_text_buffer_insert_with_tags_by_name(text_buffer, &cur, STR_SUGGEST_MATCHES, -1, TAG_MATCH, NULL);
1227 }
1228
1229 // initialize count to 0 for the actual count to be displayed in the status bar
1230 count = 0;
1231
1232 // insert the matches one by one
1233 while(g_match_info_matches(match_info))
1234 {
1235
1236 lemma = g_match_info_fetch(match_info, 0);
1237 gtk_text_buffer_insert(text_buffer, &cur, NEW_LINE, -1);
1238 gtk_text_buffer_insert_with_tags_by_name(text_buffer, &cur, lemma, -1, TAG_SUGGESTION, NULL);
1239 g_free(lemma);
1240 lemma = NULL;
1241 g_match_info_next (match_info, NULL);
1242 count++;
1243 }
1244
1245 // free all the initialised data
1246 g_match_info_free(match_info);
1247 match_info = NULL;
1248 g_regex_unref(regex);
1249
1250 // set the status bar accordingly with the count
1251 gtk_statusbar_pop(status_bar, status_msg_context_id);
1252 g_snprintf(status_msg, MAX_STATUS_MSG, STR_STATUS_REGEX, count, count > 0?STR_STATUS_LOOKUP_HINT:"");
1253 status_msg_context_id = gtk_statusbar_get_context_id(status_bar, STATUS_DESC_REGEX_RESULTS);
1254 gtk_statusbar_push(status_bar, status_msg_context_id, status_msg);
1255 }
1256
1257 // if regex was invalid (it would be NULL)
1258 // or if the expr. fetched no results, set regex failed message
1259 if(NULL == regex || 0 == count)
1260 {
1261 gtk_text_buffer_insert_with_tags_by_name(text_buffer, &cur, STR_REGEX_FAILED, -1, TAG_SUGGESTION, NULL);
1262 }
1263 else
1264 results_set = TRUE;
1265
1266 g_free(regex_pattern);
1267 regex_pattern = NULL;
1268 }
1269 return results_set;
1270 }
1271
1272 static gboolean is_wildmat_expr(const gchar *expr)
1273 {
1274 guint16 i = 0;
1275 gchar ch = 0;
1276
1277 if(expr)
1278 {
1279 // if the passed expr. has any of the char below deem it as a wildmat expr.
1280 while((ch = expr[i++]) != '\0')
1281 {
1282 if(ch == '*' || ch == '?' || ch == '[' || ch == ']' || ch =='{' || ch == '}' || ch == '+')
1283 return TRUE;
1284 }
1285 }
1286
1287 return FALSE;
1288 }
1289
1290 static gboolean handle_wildmat_expr(GtkBuilder *gui_builder, GtkTextBuffer *buffer)
1291 {
1292 GtkWindow *window = GTK_WINDOW(gtk_builder_get_object(gui_builder, WINDOW_MAIN));
1293 GtkExpander *expander = GTK_EXPANDER(gtk_builder_get_object(gui_builder, EXPANDER));
1294 GtkStatusbar *status_bar = GTK_STATUSBAR(gtk_builder_get_object(gui_builder, STATUSBAR));
1295 GtkComboBox *combo_query = GTK_COMBO_BOX(gtk_builder_get_object(gui_builder, COMBO_QUERY));
1296 GtkEntry *query_entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo_query)));
1297
1298 gchar status_msg[MAX_STATUS_MSG] = "";
1299 gboolean results_set = FALSE;
1300
1301 // Check if the fed string is a wildmat expr.
1302 // If true, call the regex mod. to set the results and return
1303 gchar *regex_text = g_strstrip(g_strdup(gtk_entry_get_text(query_entry)));
1304 if(is_wildmat_expr(regex_text))
1305 {
1306 // clear previously set relatives
1307 // clear search successful flag; without this when the prev. looked up
1308 // word is again dbl clicked from the results, it won't jump to the word
1309 relatives_clear_all(gui_builder);
1310 last_search_successful = FALSE;
1311
1312 // contract the expander, since relatives aren't required here
1313 if(gtk_expander_get_expanded(expander))
1314 {
1315 gtk_expander_set_expanded(expander, FALSE);
1316 auto_contract = TRUE;
1317 }
1318
1319 gtk_statusbar_pop(status_bar, status_msg_context_id);
1320
1321 if(wordnet_terms)
1322 {
1323 // set the status bar to inform that the search is on going
1324 g_snprintf(status_msg, MAX_STATUS_MSG, STR_STATUS_SEARCHING);
1325 status_msg_context_id = gtk_statusbar_get_context_id(status_bar, STATUS_DESC_REGEX_SEARCHING);
1326 gtk_statusbar_push(status_bar, status_msg_context_id, status_msg);
1327
1328 // if visible, update the statusbar; the window will usually go on a freeze
1329 // without updating the statusbar, until GDK is idle
1330 // this call forces GDK to first redraw the statusbar and proceed
1331 if(gtk_widget_get_visible(GTK_WIDGET(window)))
1332 gdk_window_process_updates(((GtkWidget*)status_bar)->window, FALSE);
1333
1334 /* if the results were set rightly, then add it to the search history */
1335 if(set_regex_results(regex_text, gui_builder))
1336 update_history(combo_query, regex_text);
1337 }
1338 else
1339 {
1340 GtkTextIter cur = {0};
1341 gchar *lemma = NULL;
1342 // report that the search index (index.sense) is missing
1343 g_snprintf(status_msg, MAX_STATUS_MSG, STR_STATUS_REGEX_FILE_MISSING);
1344 status_msg_context_id = gtk_statusbar_get_context_id(status_bar, STATUS_DESC_REGEX_ERROR);
1345 gtk_statusbar_push(status_bar, status_msg_context_id, status_msg);
1346
1347 gtk_text_buffer_set_text(buffer, "", -1);
1348 gtk_text_buffer_get_end_iter(buffer, &cur);
1349
1350 lemma = g_strdup_printf(STR_REGEX_FILE_MISSING, SetSearchdir());
1351 if(lemma)
1352 {
1353 gtk_text_buffer_insert_with_tags_by_name(buffer, &cur, lemma, -1, TAG_POS, NULL);
1354
1355 g_free(lemma);
1356 lemma = NULL;
1357 }
1358 }
1359 results_set = TRUE;
1360 }
1361
1362 g_free(regex_text);
1363 regex_text = NULL;
1364
1365 return results_set;
1366 }
1367
1368 static gboolean is_entry_unique(GtkTreeModel *query_list_store, GtkTreePath *path, GtkTreeIter *iter, gchar **lemma)
1369 {
1370 gboolean duplicate = FALSE;
1371 gchar *str_list_item = NULL;
1372 gtk_tree_model_get(query_list_store, iter, 0, &str_list_item, -1);
1373 duplicate = (0 == g_strcmp0(*lemma, str_list_item));
1374 g_free(str_list_item);
1375 str_list_item = NULL;
1376 if(duplicate)
1377 {
1378 *lemma = NULL;
1379 }
1380 return duplicate;
1381 }
1382
1383 static gboolean update_history(GtkComboBox *combo_query, char *lemma)
1384 {
1385 GtkListStore *query_list_store = GTK_LIST_STORE(gtk_combo_box_get_model(combo_query));
1386 gboolean inserted = FALSE;
1387
1388 if(!query_list_store)
1389 g_error("Unable to get query combo box's model!");
1390
1391 gtk_tree_model_foreach(GTK_TREE_MODEL(query_list_store), (GtkTreeModelForeachFunc) is_entry_unique, (gpointer) &lemma);
1392
1393 if(lemma)
1394 {
1395 GtkTreeIter query_list_iter = {0};
1396 gtk_list_store_prepend(query_list_store, &query_list_iter);
1397 gtk_list_store_set(query_list_store, &query_list_iter, 0, lemma, -1);
1398 inserted = TRUE;
1399 }
1400 return inserted;
1401 }
1402
1403 static gboolean update_history_and_save(GtkComboBox *combo_query, char *lemma)
1404 {
1405 gboolean saved = FALSE;
1406 if(update_history(combo_query, lemma))
1407 {
1408 static gboolean update_history_in_file_fail_notifed = FALSE;
1409 if(update_history_in_file(lemma))
1410 {
1411 saved = TRUE;
1412 }
1413 else if(!update_history_in_file_fail_notifed)
1414 {
1415 GtkDialog *err_msg = GTK_DIALOG(gtk_message_dialog_new(NULL,
1416 0,
1417 GTK_MESSAGE_ERROR,
1418 GTK_BUTTONS_CLOSE,
1419 STR_HISTORY_FILE_UPDATE_FAILED));
1420 gtk_dialog_run(err_msg);
1421 gtk_widget_destroy(GTK_WIDGET(err_msg));
1422 update_history_in_file_fail_notifed = TRUE;
1423 }
1424 }
1425 return saved;
1426 }
1427
1428 static void show_definitions(GtkBuilder *gui_builder, GtkTextBuffer *buffer)
1429 {
1430 g_assert(results);
1431
1432 GSList *def_list = ((WNIOverview*)((WNINym*)results->data)->data)->definitions_list;
1433 WNIDefinitionItem *def_item = NULL;
1434 GSList *definitions_list = NULL, *example = NULL;
1435 GtkTextMark *freq_marker = NULL;
1436 GtkTextIter cur = {0};
1437 glong count = 0;
1438 WNIDefinition *defn = NULL;
1439 guint16 total_results = 0;
1440 guint8 total_pos = 0;
1441
1442 gchar str_count[MAX_SENSE_DIGITS] = "", status_msg[MAX_STATUS_MSG] = "";
1443 GtkStatusbar *status_bar = GTK_STATUSBAR(gtk_builder_get_object(gui_builder, STATUSBAR));
1444 GtkComboBox *combo_query = GTK_COMBO_BOX(gtk_builder_get_object(gui_builder, COMBO_QUERY));
1445
1446 while(def_list)
1447 {
1448 if(def_list->data)
1449 {
1450 def_item = (WNIDefinitionItem*) def_list->data;
1451 definitions_list = def_item->definitions;
1452
1453 gtk_text_buffer_get_end_iter(buffer, &cur);
1454 gtk_text_buffer_insert_with_tags_by_name(buffer, &cur, def_item->lemma, -1, TAG_LEMMA, NULL);
1455 gtk_text_buffer_insert(buffer, &cur, " ~ ", -1);
1456 gtk_text_buffer_insert_with_tags_by_name(buffer, &cur, partnames[def_item->pos], -1, TAG_POS, NULL);
1457
1458 freq_marker = gtk_text_buffer_create_mark(buffer, NULL, &cur, TRUE);
1459
1460 gtk_text_buffer_insert_with_tags_by_name(buffer, &cur, NEW_LINE, -1, TAG_POS, NULL);
1461
1462 count = 1;
1463 while(definitions_list)
1464 {
1465 defn = (WNIDefinition*) definitions_list->data;
1466
1467 total_results++;
1468 g_snprintf(str_count, MAX_SENSE_DIGITS, "%3d", total_results);
1469 gtk_text_buffer_create_mark(buffer, str_count, &cur, TRUE);
1470
1471 g_snprintf(str_count, MAX_SENSE_DIGITS, "%2d. ", (int)count);
1472
1473 gtk_text_buffer_insert_with_tags_by_name(buffer, &cur, str_count, -1, TAG_COUNTER, NULL);
1474 gtk_text_buffer_insert(buffer, &cur, defn->definition, -1);
1475
1476 example = defn->examples;
1477
1478 definitions_list = g_slist_next(definitions_list);
1479 if(example || definitions_list) gtk_text_buffer_insert(buffer, &cur, NEW_LINE, -1);
1480
1481 while(example)
1482 {
1483 gtk_text_buffer_insert_with_tags_by_name(buffer, &cur, (gchar*) example->data, -1, TAG_EXAMPLE, NULL);
1484 example = g_slist_next(example);
1485 // for each example come to appear in a new line enabled this line and
1486 // comment the next two buffer inserts of "; " and new line
1487 //if(example || definitions_list) gtk_text_buffer_insert(buffer, &cur, NEW_LINE, -1);
1488
1489 if(example)
1490 gtk_text_buffer_insert(buffer, &cur, "; ", -1);
1491 else if(definitions_list)
1492 gtk_text_buffer_insert(buffer, &cur, NEW_LINE, -1);
1493 }
1494 count++;
1495 }
1496
1497 if(show_polysemy)
1498 {
1499 gtk_text_buffer_get_iter_at_mark(buffer, &cur, freq_marker);
1500
1501 gtk_text_buffer_insert(buffer, &cur, " ", -1);
1502 gtk_text_buffer_insert_with_tags_by_name(buffer,
1503 &cur,
1504 familiarity[get_frequency(count - 1)],
1505 -1,
1506 familiarity[get_frequency(count - 1)],
1507 NULL);
1508 }
1509 }
1510 def_list = g_slist_next(def_list);
1511 if(def_list)
1512 {
1513 gtk_text_buffer_get_end_iter(buffer, &cur);
1514 gtk_text_buffer_insert(buffer, &cur, NEW_LINE, -1);
1515 }
1516
1517 total_pos++;
1518 }
1519 // end of setting definitions
1520
1521 /* scroll to the text view top to show the first definition
1522 gtk_text_buffer_get_start_iter(buffer, &cur);
1523 gtk_text_view_scroll_to_iter(text_view, &cur, 0.0, FALSE, 0.0, 0.0); */
1524
1525 // set status
1526 gtk_statusbar_pop(status_bar, status_msg_context_id);
1527 g_snprintf(status_msg, MAX_STATUS_MSG, STR_STATUS_QUERY_SUCCESS, total_results, total_pos);
1528 status_msg_context_id = gtk_statusbar_get_context_id(status_bar, STATUS_DESC_SEARCH_SUCCESS);
1529 gtk_statusbar_push(status_bar, status_msg_context_id, status_msg);
1530
1531 // manage history
1532 update_history_and_save(combo_query,
1533 ((WNIDefinitionItem*)((WNIOverview*)((WNINym*)results->data)->data)->definitions_list->data)->lemma);
1534 }
1535
1536 static void show_suggestions(GtkBuilder *gui_builder, GtkTextBuffer *buffer, GtkTextIter *cur, GSList *suggestions)
1537 {
1538 GtkExpander *expander = GTK_EXPANDER(gtk_builder_get_object(gui_builder, EXPANDER));
1539
1540 gtk_text_buffer_insert(buffer, cur, NEW_LINE, -1);
1541 gtk_text_buffer_insert_with_tags_by_name(buffer, cur, STR_SUGGEST_MATCHES, -1, TAG_MATCH, NULL);
1542
1543 while(suggestions)
1544 {
1545 gtk_text_buffer_insert(buffer, cur, NEW_LINE, -1);
1546 gtk_text_buffer_insert_with_tags_by_name(buffer,
1547 cur,
1548 suggestions->data,
1549 -1,
1550 TAG_SUGGESTION,
1551 NULL);
1552 suggestions = g_slist_next(suggestions);
1553 }
1554
1555 // contract the expander, since relatives aren't required here
1556 if(gtk_expander_get_expanded(expander))
1557 {
1558 gtk_expander_set_expanded(expander, FALSE);
1559 auto_contract = TRUE;
1560 }
1561 }
1562
1563 static void construct_show_notification()
1564 {
1565 const gchar *lemma = ((WNIDefinitionItem*)((WNIOverview*)((WNINym*)results->data)->data)->definitions_list->data)->lemma;
1566 gchar *definition =
1567 g_strconcat(partnames[((WNIDefinitionItem*)((WNIOverview*)((WNINym*)results->data)->data)->definitions_list->data)->pos],
1568 ". ",
1569 ((WNIDefinition*)((WNIDefinitionItem*)((WNIOverview*)((WNINym*)results->data)->data)->definitions_list->data)->definitions->data)->definition,
1570 NULL);
1571
1572 // close any previous updates
1573 notify_notification_close(mod_notifier, NULL);
1574
1575 if(definition)
1576 {
1577 gboolean update_successful = notify_notification_update(mod_notifier, lemma, definition, "gtk-dialog-info");
1578 if(!update_successful || (FALSE == notify_notification_show(mod_notifier, NULL)))
1579 {
1580 g_warning("Failed to display notification\n");
1581 }
1582 g_free(definition);
1583 definition = NULL;
1584 }
1585 }
1586
1587 static void show_searching(GtkBuilder *gui_builder)
1588 {
1589 gchar status_msg[MAX_STATUS_MSG] = "";
1590 GtkWindow *window = GTK_WINDOW(gtk_builder_get_object(gui_builder, WINDOW_MAIN));
1591 GtkStatusbar *status_bar = GTK_STATUSBAR(gtk_builder_get_object(gui_builder, STATUSBAR));
1592
1593 // set the status bar and update the window
1594 gtk_statusbar_pop(status_bar, status_msg_context_id);
1595 g_snprintf(status_msg, MAX_STATUS_MSG, STR_STATUS_SEARCHING);
1596 status_msg_context_id = gtk_statusbar_get_context_id(status_bar, STATUS_DESC_REGEX_SEARCHING);
1597 gtk_statusbar_push(status_bar, status_msg_context_id, status_msg);
1598 // if visible, repaint the statusbar after setting the status message, for it to get reflected
1599 if(gtk_widget_get_visible(GTK_WIDGET(window)))
1600 gdk_window_process_updates(((GtkWidget*)status_bar)->window, FALSE);
1601 }
1602
1603 static void query_wni(gchar *search_str,
1604 WNIRequestFlags lookup_type,
1605 GSList **suggestions)
1606 {
1607 G_MESSAGE("'%s' requested from WNI!\n", search_str);
1608
1609 wni_request_nyms(search_str, &results, lookup_type, advanced_mode);
1610
1611 // if the search failed and suggestions are available, check if the prime
1612 // suggestion is the same as the search string
1613 if(!results && mod_suggest)
1614 {
1615 GSList *temp_suggestions = suggestions_get(search_str);
1616 if(temp_suggestions && are_same_after_strip(search_str, temp_suggestions->data))
1617 {
1618 wni_request_nyms(temp_suggestions->data, &results, lookup_type, advanced_mode);
1619 }
1620 // if the caller wanted the suggestions and the suggestions lookup succeeded
1621 // then don't free the results, but pass it on
1622 else if(suggestions)
1623 {
1624 *suggestions = temp_suggestions;
1625 }
1626
1627 // if not passing on, free
1628 if(!suggestions || (*suggestions != temp_suggestions))
1629 {
1630 g_slist_free_full(temp_suggestions, g_free);
1631 }
1632 }
1633 }
1634
1635 static void button_search_click(GtkButton *button, gpointer user_data)
1636 {
1637 GtkBuilder *gui_builder = GTK_BUILDER(user_data);
1638 GtkComboBox *combo_query = GTK_COMBO_BOX(gtk_builder_get_object(gui_builder, COMBO_QUERY));
1639 GtkEntry *combo_entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo_query)));
1640 GtkTextView *text_view = GTK_TEXT_VIEW(gtk_builder_get_object(gui_builder, TEXT_VIEW_DEFINITIONS));
1641 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text_view);
1642
1643 gchar *search_str = NULL;
1644
1645 // if it was a regex, then results are now furnished, so return
1646 if(handle_wildmat_expr(gui_builder, buffer))
1647 return;
1648
1649 // from here on normal lookup starts
1650 search_str = g_strdup(gtk_entry_get_text(combo_entry));
1651 search_str = g_strstrip(strip_invalid_edges(search_str));
1652
1653 // make sure this isn't a 0 length string
1654 if(search_str && search_str[0])
1655 {
1656 gboolean new_search = TRUE;
1657 GSList *suggestions = NULL;
1658
1659 G_MESSAGE("'%s' queried!\n", search_str);
1660
1661 if(last_search_successful)
1662 new_search = (g_ascii_strcasecmp(last_search, search_str) != 0);
1663
1664 // check if this isn't the previous search query
1665 if(new_search)
1666 {
1667 show_searching(gui_builder);
1668 query_wni(search_str, WORDNET_INTERFACE_ALL, &suggestions);
1669
1670 // clear prior text in definitons text view & relatives
1671 gtk_text_buffer_set_text(buffer, "", -1);
1672 relatives_clear_all(gui_builder);
1673
1674 // set definitions
1675 if(results)
1676 {
1677 G_MESSAGE("Results successful!\n");
1678
1679 show_definitions(gui_builder, buffer);
1680 relatives_load(gui_builder, TRUE);
1681
1682 g_stpcpy(last_search, search_str);
1683 last_search_successful = TRUE;
1684 }
1685 else
1686 {
1687 GtkStatusbar *status_bar = GTK_STATUSBAR(gtk_builder_get_object(gui_builder, STATUSBAR));
1688 GtkTextIter cur = {0};
1689 gtk_text_buffer_set_text(buffer, "", -1);
1690 gtk_text_buffer_get_start_iter(buffer, &cur);
1691 gtk_text_buffer_insert_with_tags_by_name(buffer, &cur, STR_QUERY_FAILED, -1, TAG_POS, NULL);
1692
1693 if(suggestions)
1694 show_suggestions(gui_builder, buffer, &cur, suggestions);
1695
1696 /* status bar should be updated */
1697 gtk_statusbar_pop(status_bar, status_msg_context_id);
1698 status_msg_context_id = gtk_statusbar_get_context_id(status_bar, STATUS_DESC_SEARCH_FAILURE);
1699 gtk_statusbar_push(status_bar, status_msg_context_id, STR_STATUS_QUERY_FAILED);
1700
1701 last_search_successful = FALSE;
1702 }
1703 if(suggestions)
1704 {
1705 g_slist_free_full(suggestions, g_free);
1706 suggestions = NULL;
1707 }
1708 GtkWindow *window = GTK_WINDOW(gtk_builder_get_object(gui_builder, WINDOW_MAIN));
1709 show_window(window);
1710 }
1711
1712 gtk_editable_select_region(GTK_EDITABLE(combo_entry), 0, -1);
1713 gtk_widget_grab_focus(GTK_WIDGET(combo_query));
1714 }
1715
1716 if(search_str)
1717 {
1718 g_free(search_str);
1719 search_str = NULL;
1720 }
1721 last_lookup_a_notification = FALSE;
1722 }
1723
1724 static void combo_query_changed(GtkComboBox *combo_query, gpointer user_data)
1725 {
1726 GtkBuilder *gui_builder = GTK_BUILDER(user_data);
1727 GtkButton *button_search = GTK_BUTTON(gtk_builder_get_object(gui_builder, BUTTON_SEARCH));
1728 GtkToolbar *tool_bar = GTK_TOOLBAR(gtk_builder_get_object(gui_builder, TOOLBAR));
1729 GtkToolItem *toolbar_prev = NULL, *toolbar_next = NULL;
1730 GtkTreeModel *tree_model = NULL;
1731 gint selected_item = gtk_combo_box_get_active(combo_query);
1732 gint total_items = 0;
1733
1734 if(selected_item != -1)
1735 {
1736 gtk_button_clicked(button_search);
1737 tree_model = gtk_combo_box_get_model(combo_query);
1738 total_items = gtk_tree_model_iter_n_children(tree_model, NULL);
1739
1740 if(total_items > 1)
1741 {
1742 toolbar_next = gtk_toolbar_get_nth_item(tool_bar, 1);
1743 toolbar_prev = gtk_toolbar_get_nth_item(tool_bar, 0);
1744
1745 if(0 == selected_item)
1746 {
1747 gtk_widget_set_sensitive(GTK_WIDGET(toolbar_prev), TRUE);
1748 gtk_widget_set_sensitive(GTK_WIDGET(toolbar_next), FALSE);
1749 }
1750 else if(total_items == selected_item + 1)
1751 {
1752 gtk_widget_set_sensitive(GTK_WIDGET(toolbar_prev), FALSE);
1753 gtk_widget_set_sensitive(GTK_WIDGET(toolbar_next), TRUE);
1754 }
1755 else
1756 {
1757 gtk_widget_set_sensitive(GTK_WIDGET(toolbar_next), TRUE);
1758 gtk_widget_set_sensitive(GTK_WIDGET(toolbar_prev), TRUE);
1759 }
1760 }
1761 }
1762 }
1763
1764 static gboolean text_view_button_pressed(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
1765 {
1766 if(GDK_2BUTTON_PRESS == event->type)
1767 was_double_click = TRUE;
1768
1769 return FALSE;
1770 }
1771
1772 static gboolean text_view_button_released(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
1773 {
1774 GtkBuilder *gui_builder = GTK_BUILDER(user_data);
1775 GtkTextView *defn_text_view = GTK_TEXT_VIEW(gtk_builder_get_object(gui_builder, TEXT_VIEW_DEFINITIONS));
1776 GtkTextBuffer *defn_text_buffer = gtk_text_view_get_buffer(defn_text_view);
1777 GtkTextMark *insert_mark = NULL, *selection_mark = NULL;
1778 GtkTextIter ins_iter = {0}, sel_iter = {0};
1779 gchar *trial_text = NULL;
1780 guint8 i = 0;
1781 gboolean iter_move_success = FALSE;
1782
1783 // set the selection if it was a double click or a CTRL + mouse release
1784 if(was_double_click || (GDK_CONTROL_MASK & event->state))
1785 {
1786 // if it was a double click, try selecting diff. combinations
1787 // for the sake of compound lemmas like 'work out', 'in a nutshell', etc.
1788 if(was_double_click)
1789 {
1790 was_double_click = FALSE;
1791
1792 if(gtk_text_buffer_get_has_selection(defn_text_buffer))
1793 {
1794 // get the 'selection_bound' and 'insert' markers
1795 selection_mark = gtk_text_buffer_get_selection_bound(defn_text_buffer);
1796 insert_mark = gtk_text_buffer_get_mark(defn_text_buffer, "insert");
1797
1798 for(i = 0; ((i < 6) && (!iter_move_success)); i++)
1799 {
1800 // on every iteration of the loop, set the iters to the actual sel. marks
1801 gtk_text_buffer_get_iter_at_mark(defn_text_buffer, &ins_iter, insert_mark);
1802 gtk_text_buffer_get_iter_at_mark(defn_text_buffer, &sel_iter, selection_mark);
1803
1804 switch(i)
1805 {
1806 case 0:
1807 // try selecting two words after the actual sel.
1808 if(!gtk_text_iter_forward_visible_word_ends(&sel_iter, 2))
1809 gtk_text_iter_forward_to_end(&sel_iter);
1810 iter_move_success = TRUE;
1811 break;
1812 case 1:
1813 // try selecting two words before the actual sel.
1814 if(gtk_text_iter_backward_visible_word_starts(&ins_iter, 2))
1815 iter_move_success = TRUE;
1816 break;
1817 case 2:
1818 // try selecting one word before and after the actual sel.
1819 if((gtk_text_iter_forward_visible_word_end(&sel_iter)) && (gtk_text_iter_backward_visible_word_start(&ins_iter)))
1820 iter_move_success = TRUE;
1821 break;
1822 case 3:
1823 // try selecting one word next to the actual sel.
1824 if(!gtk_text_iter_forward_visible_word_end(&sel_iter))
1825 gtk_text_iter_forward_to_end(&sel_iter);
1826 iter_move_success = TRUE;
1827 break;
1828 case 4:
1829 // try selecting one word before the actual sel.
1830 if(gtk_text_iter_backward_visible_word_start(&ins_iter))
1831 iter_move_success = TRUE;
1832 break;
1833 case 5:
1834 // fallback to just the actual selection
1835 iter_move_success = TRUE;
1836 break;
1837 }
1838
1839 if(iter_move_success)
1840 {
1841 // obtain the text for the set iters
1842 trial_text = gtk_text_buffer_get_text(defn_text_buffer, &ins_iter, &sel_iter, FALSE);
1843
1844 // search the trial selection in WordNet, on a successful lookup iter_move_success is set
1845 iter_move_success = wni_request_nyms(trial_text, NULL, (WNIRequestFlags) 0, FALSE);
1846
1847 // free the obtained text
1848 g_free(trial_text);
1849 trial_text = NULL;
1850 }
1851 }
1852
1853
1854 // set the 'insert' and 'selection_bound' markers respectivly to the newly placed iters
1855 gtk_text_buffer_move_mark(defn_text_buffer, insert_mark, &ins_iter);
1856 gtk_text_buffer_move_mark(defn_text_buffer, selection_mark, &sel_iter);
1857 }
1858 }
1859
1860 // request the "TARGETS" target for the primary selection
1861 gtk_selection_convert(widget, GDK_SELECTION_PRIMARY, GDK_TARGET_STRING, event->time);
1862 }
1863
1864 return FALSE;
1865 }
1866
1867 static void text_view_selection_made(GtkWidget *widget, GtkSelectionData *sel_data, guint time, gpointer user_data)
1868 {
1869 GtkBuilder *gui_builder = GTK_BUILDER(user_data);
1870 GtkComboBox *combo_query = NULL;
1871 GtkEntry *txtQuery = NULL;
1872 GtkButton *button_search = NULL;
1873 gchar *selection = (gchar*) gtk_selection_data_get_text(sel_data);
1874
1875 if(selection)
1876 {
1877 combo_query = GTK_COMBO_BOX(gtk_builder_get_object(gui_builder, COMBO_QUERY));
1878 txtQuery = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo_query)));
1879 button_search = GTK_BUTTON(gtk_builder_get_object(gui_builder, BUTTON_SEARCH));
1880
1881 if(0 != g_strcmp0(gtk_entry_get_text(txtQuery), selection))
1882 {
1883 gtk_entry_set_text(txtQuery, selection);
1884 //gtk_editable_set_position(GTK_EDITABLE(txtQuery), -1);
1885 }
1886 g_free(selection);
1887 selection = NULL;
1888
1889 gtk_button_clicked(button_search);
1890 }
1891 }
1892
1893 static void expander_clicked(GtkExpander *expander, gpointer user_data)
1894 {
1895 GtkBuilder *gui_builder = GTK_BUILDER(user_data);
1896 GtkPaned *vpaned = GTK_PANED(gtk_builder_get_object(gui_builder, VPANE));
1897
1898 gtk_paned_set_position(vpaned, -1); // unset the pane's position so that it goes to the original state
1899
1900 // if the user manually expands/contracts it, reset the auto flag
1901 auto_contract = FALSE;
1902 }
1903
1904 static void query_list_updated(GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter, gpointer user_data)
1905 {
1906 GtkBuilder *gui_builder = GTK_BUILDER(user_data);
1907 GtkToolbar *tool_bar = GTK_TOOLBAR(gtk_builder_get_object(gui_builder, TOOLBAR));
1908 GtkToolItem *toolbar_prev = gtk_toolbar_get_nth_item(tool_bar, 0);
1909 GtkToolItem *toolbar_next = gtk_toolbar_get_nth_item(tool_bar, 1);
1910 GtkTreeIter temp_iter = {0};
1911
1912 if(gtk_tree_model_iter_nth_child(tree_model, &temp_iter, NULL, 1))
1913 {
1914 gtk_widget_set_sensitive(GTK_WIDGET(toolbar_prev), TRUE);
1915 gtk_widget_set_sensitive(GTK_WIDGET(toolbar_next), FALSE);
1916 }
1917 }
1918
1919 static GtkTextMark* highlight_definition(guint8 id, guint16 sense, GtkTextView *text_view)
1920 {
1921 guint16 defn_no = 0;
1922 GSList *wni_results = results, *definitions_list = NULL;
1923 GtkTextBuffer *buffer = NULL;
1924 GtkTextMark *mark = NULL;
1925 GtkTextIter start = {0}, end = {0};
1926 gchar str_index[MAX_SENSE_DIGITS] = "";
1927
1928 while(wni_results)
1929 {
1930 if(WORDNET_INTERFACE_OVERVIEW == ((WNINym*)wni_results->data)->id)
1931 {
1932 definitions_list = ((WNIOverview*)((WNINym*)wni_results->data)->data)->definitions_list;
1933
1934 while(definitions_list)
1935 {
1936 if(id == ((WNIDefinitionItem*)definitions_list->data)->id)
1937 {
1938 defn_no += sense + 1;
1939 buffer = gtk_text_view_get_buffer(text_view);
1940
1941 g_snprintf(str_index, MAX_SENSE_DIGITS, "%3d", defn_no);
1942 mark = gtk_text_buffer_get_mark(buffer, str_index);
1943 if(mark)
1944 {
1945 gtk_text_buffer_get_iter_at_mark(buffer, &start, mark);
1946 gtk_text_buffer_get_iter_at_offset(buffer, &end, gtk_text_iter_get_offset(&start) + 3);
1947 gtk_text_buffer_apply_tag_by_name(buffer, TAG_HIGHLIGHT, &start, &end);
1948 }
1949
1950 break;
1951 }
1952 else
1953 defn_no += ((WNIDefinitionItem*)definitions_list->data)->count;
1954
1955 definitions_list = g_slist_next(definitions_list);
1956 }
1957
1958 break;
1959 }
1960 wni_results = g_slist_next(wni_results);
1961 }
1962
1963 return mark;
1964 }
1965
1966 static void highlight_senses_from_synonyms(guint16 relative_index, GtkTextView *text_view)
1967 {
1968 GSList *wni_results = results;
1969 GSList *synonyms_list = NULL;
1970 GSList *synonym_mapping = NULL;
1971 GtkTextMark *highlighted_mark = NULL, *scroll_mark = NULL;
1972 GtkTextBuffer *buffer = NULL;
1973 GtkTextIter iter = {0};
1974 guint16 last_offset = 0, current_offset = 0;
1975
1976 while(wni_results)
1977 {
1978 if(WORDNET_INTERFACE_OVERVIEW == ((WNINym*)wni_results->data)->id)
1979 {
1980 synonyms_list = ((WNIOverview*) ((WNINym*)wni_results->data)->data)->synonyms_list;
1981 for(; relative_index; relative_index--, synonyms_list = g_slist_next(synonyms_list));
1982 synonym_mapping = ((WNIPropertyItem*) synonyms_list->data)->mapping;
1983
1984 buffer = gtk_text_view_get_buffer(text_view);
1985 gtk_text_buffer_get_end_iter(buffer, &iter);
1986 // store the last line number
1987 last_offset = gtk_text_iter_get_line(&iter);
1988
1989 while(synonym_mapping)
1990 {
1991 highlighted_mark = highlight_definition(((WNISynonymMapping*) synonym_mapping->data)->id, ((WNISynonymMapping*) synonym_mapping->data)->sense, text_view);
1992
1993 gtk_text_buffer_get_iter_at_mark(buffer, &iter, highlighted_mark);
1994
1995 // see if this mark is at a higher line
1996 current_offset = gtk_text_iter_get_line(&iter);
1997
1998 if(current_offset <= last_offset)
1999 {
2000 last_offset = current_offset;
2001 scroll_mark = highlighted_mark;
2002 }
2003
2004 synonym_mapping = g_slist_next(synonym_mapping);
2005 }
2006
2007 gtk_text_view_scroll_to_mark(text_view, scroll_mark, 0.0, TRUE, 0, 0);
2008
2009 break;
2010 }
2011
2012 wni_results = g_slist_next(wni_results);
2013 }
2014 }
2015
2016 static void highlight_senses_from_antonyms( guint8 category, guint16 relative_index, guint16 sub_level, GtkTextView *text_view)
2017 {
2018 GSList *wni_results = results;
2019 GSList *antonyms_list = NULL;
2020 GSList *antonym_mapping = NULL;
2021 WNIAntonymItem *antonym_item = NULL;
2022 guint16 direct_count = 0, indirect_count = 0, count = 0;
2023 GtkTextBuffer *buffer = NULL;
2024 GtkTextMark *scroll_mark = NULL, *highlighted_mark = NULL;
2025 GtkTextIter iter = {0}, end = {0};
2026
2027 while(wni_results)
2028 {
2029 if(WORDNET_INTERFACE_ANTONYMS == ((WNINym*)wni_results->data)->id)
2030 {
2031 antonyms_list = ((WNIProperties*) ((WNINym*) wni_results->data)->data)->properties_list;
2032
2033 while(antonyms_list)
2034 {
2035 antonym_item = (WNIAntonymItem*) antonyms_list->data;
2036
2037 if(antonym_item->relation <= DIRECT_ANT)
2038 direct_count++;
2039 else
2040 {
2041 count = g_slist_length(antonym_item->implications);
2042 indirect_count += count;
2043
2044 if(indirect_count > relative_index)
2045 indirect_count = relative_index + 1;
2046 }
2047
2048 if((category <= DIRECT_ANT && direct_count == relative_index + 1) ||
2049 (category == INDIRECT_ANT && indirect_count == relative_index + 1))
2050 {
2051 antonym_mapping = antonym_item->mapping;
2052
2053 buffer = gtk_text_view_get_buffer(text_view);
2054 gtk_text_buffer_get_end_iter(buffer, &end);
2055
2056 // store the last line number
2057 direct_count = gtk_text_iter_get_line(&end);
2058
2059 while(antonym_mapping)
2060 {
2061 if(sub_level)
2062 {
2063 count += ((WNIAntonymMapping*) antonym_mapping->data)->related_word_count;
2064 if(sub_level > count)
2065 {
2066 antonym_mapping = g_slist_next(antonym_mapping);
2067 continue;
2068 }
2069 }
2070
2071 highlighted_mark = highlight_definition(((WNIAntonymMapping*) antonym_mapping->data)->id,
2072 ((WNIAntonymMapping*) antonym_mapping->data)->sense, text_view);
2073
2074 gtk_text_buffer_get_iter_at_mark(buffer, &iter, highlighted_mark);
2075
2076 // see if this mark is at a higher line
2077 indirect_count = gtk_text_iter_get_line(&iter);
2078
2079 if(indirect_count <= direct_count)
2080 {
2081 direct_count = indirect_count;
2082 scroll_mark = highlighted_mark;
2083 }
2084
2085 antonym_mapping = g_slist_next(antonym_mapping);
2086
2087 if(sub_level)
2088 {
2089 if(sub_level <= count) break;
2090 }
2091 }
2092
2093 gtk_text_view_scroll_to_mark(text_view, scroll_mark, 0.0, TRUE, 0, 0);
2094
2095 break;
2096 }
2097
2098 antonyms_list = g_slist_next(antonyms_list);
2099 }
2100
2101 break;
2102 }
2103
2104 wni_results = g_slist_next(wni_results);
2105 }
2106 }
2107
2108 static void highlight_senses_from_domain(guint8 category, guint16 relative_index, GtkTextView *text_view)
2109 {
2110 GSList *wni_results = results;
2111 GSList *class_list = NULL;
2112 WNIClassItem *class_item = NULL;
2113 guint16 count = 0;
2114
2115 while(wni_results)
2116 {
2117 if(WORDNET_INTERFACE_CLASS == ((WNINym*) wni_results->data)->id)
2118 {
2119 class_list = ((WNIProperties*) ((WNINym*) wni_results->data)->data)->properties_list;
2120
2121 while(class_list)
2122 {
2123 class_item = (WNIClassItem*)class_list->data;
2124 if(category == class_item->type - CLASSIF_CATEGORY)
2125 count++;
2126
2127 if(count == relative_index + 1)
2128 {
2129 gtk_text_view_scroll_to_mark(text_view, highlight_definition(class_item->id, class_item->sense, text_view), 0.0, TRUE, 0, 0);
2130 break;
2131 }
2132
2133 class_list = g_slist_next(class_list);
2134 }
2135
2136 break;
2137 }
2138
2139 wni_results = g_slist_next(wni_results);
2140 }
2141 }
2142
2143 static void highlight_senses_from_relative_lists(WNIRequestFlags id, guint16 relative_index, GtkTextView *text_view)
2144 {
2145 GSList *wni_results = results;
2146 GSList *properties_list = NULL;
2147 GSList *property_mapping = NULL;
2148 WNIPropertyMapping *mapping = NULL;
2149 GtkTextMark *highlighted_mark = NULL, *scroll_mark = NULL;
2150 GtkTextBuffer *buffer = NULL;
2151 GtkTextIter iter = {0};
2152 guint16 last_offset = 0, current_offset = 0;
2153
2154 while(wni_results)
2155 {
2156 if(id == ((WNINym*)wni_results->data)->id)
2157 {
2158 properties_list = ((WNIProperties*)((WNINym*)wni_results->data)->data)->properties_list;
2159
2160 for(; relative_index; relative_index--, properties_list = g_slist_next(properties_list));
2161 property_mapping = ((WNIPropertyItem*) properties_list->data)->mapping;
2162
2163 buffer = gtk_text_view_get_buffer(text_view);
2164 gtk_text_buffer_get_end_iter(buffer, &iter);
2165 last_offset = gtk_text_iter_get_line(&iter);
2166
2167 while(property_mapping)
2168 {
2169 mapping = (WNIPropertyMapping*) property_mapping->data;
2170 highlighted_mark = highlight_definition(mapping->id, mapping->sense, text_view);
2171
2172 gtk_text_buffer_get_iter_at_mark(buffer, &iter, highlighted_mark);
2173
2174 // see if this mark is at a higher line
2175 current_offset = gtk_text_iter_get_line(&iter);
2176
2177 if(current_offset <= last_offset)
2178 {
2179 last_offset = current_offset;
2180 scroll_mark = highlighted_mark;
2181 }
2182
2183 property_mapping = g_slist_next(property_mapping);
2184 }
2185
2186 gtk_text_view_scroll_to_mark(text_view, scroll_mark, 0.0, TRUE, 0, 0);
2187
2188 break;
2189 }
2190 wni_results = g_slist_next(wni_results);
2191 }
2192 }
2193
2194 static void relative_selection_changed(GtkTreeView *tree_view, gpointer user_data)
2195 {
2196 GtkTreeIter iter = {0};
2197 GtkTreeModel *tree_store = NULL;
2198 gchar *term = NULL, *str_path = NULL, *str_category = NULL, *str_demark = NULL;
2199 gchar str_root[3] = "";
2200 GtkBuilder *gui_builder = GTK_BUILDER(user_data);
2201 GtkNotebook *notebook = GTK_NOTEBOOK(gtk_builder_get_object(gui_builder, NOTEBOOK));
2202 GtkTextView *text_view = GTK_TEXT_VIEW(gtk_builder_get_object(gui_builder, TEXT_VIEW_DEFINITIONS));
2203 GtkTextBuffer *buffer = NULL;
2204 GtkTextIter start = {0}, end = {0};
2205 guint16 sub_level = 0;
2206
2207 if(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(tree_view), &tree_store, &iter))
2208 {
2209 gtk_tree_model_get(tree_store, &iter, 0, &term, -1);
2210 str_path = gtk_tree_model_get_string_from_iter(tree_store, &iter);
2211
2212 buffer = gtk_text_view_get_buffer(text_view);
2213 gtk_text_buffer_get_start_iter(buffer, &start);
2214 gtk_text_buffer_get_end_iter(buffer, &end);
2215 gtk_text_buffer_remove_tag_by_name(buffer, TAG_HIGHLIGHT, &start, &end);
2216
2217 if(term && str_path)
2218 {
2219 switch(gtk_notebook_get_current_page(notebook))
2220 {
2221 case TREE_SYNONYMS:
2222 highlight_senses_from_synonyms((guint16) g_ascii_strtoull(str_path, NULL, 10), text_view);
2223 break;
2224 case TREE_ANTONYMS:
2225 // to make sure no highlighting is done for categories, check for ':' in the path
2226 // Categories (Direct/Indirect) won't have a parent, hence only one char. will be
2227 // there in the path; if not, a word has got selected
2228 if(advanced_mode && (str_demark = g_strstr_len(str_path, -1, ":")) != NULL)
2229 {
2230 str_demark++;
2231 str_demark = g_strstr_len(str_demark, -1, ":");
2232 if(str_demark)
2233 {
2234 str_demark++;
2235 sub_level = (guint16) g_ascii_strtoull(str_demark, NULL, 10) + 1;
2236 }
2237
2238 gtk_tree_model_get_iter_first(tree_store, &iter);
2239 gtk_tree_model_get(tree_store, &iter, 0, &str_category, -1);
2240 if(str_category)
2241 {
2242 if(0 != g_strcmp0(str_category, STR_ANTONYM_HEADER_DIRECT)) str_path[0] = '1';
2243 g_free(str_category);
2244 str_category = NULL;
2245 }
2246
2247 strip_invalid_edges(&str_path[2]);
2248 /* if its a word, parent 0 means Direct and 1 Indirect */
2249 highlight_senses_from_antonyms((0 == str_path[0] - '0') ? DIRECT_ANT : INDIRECT_ANT,
2250 (guint16) g_ascii_strtoull(&str_path[2], NULL, 10),
2251 sub_level,
2252 text_view);
2253 }
2254 else if(!advanced_mode)
2255 // if its simple mode, set category to 0
2256 highlight_senses_from_antonyms(0, (guint16) g_ascii_strtoull(str_path, NULL, 10), sub_level, text_view);
2257 break;
2258 case TREE_DOMAIN:
2259 if((str_demark = g_strstr_len(str_path, -1, ":")) != NULL)
2260 {
2261 g_snprintf(str_root, 3, "%c", str_path[0]);
2262 gtk_tree_model_get_iter_from_string(tree_store, &iter, str_root);
2263
2264 gtk_tree_model_get(tree_store, &iter, 0, &str_category, -1);
2265 for(sub_level = 0; (0 != g_strcmp0(domain_types[sub_level], str_category)); sub_level++);
2266 g_free(str_category);
2267 str_category = NULL;
2268
2269 str_demark++;
2270 highlight_senses_from_domain((guint8) sub_level, (guint16) g_ascii_strtoull(str_demark, NULL, 10), text_view);
2271 }
2272 break;
2273 case TREE_PERTAINYMS:
2274 case TREE_HYPERNYMS:
2275 case TREE_HYPONYMS:
2276 case TREE_HOLONYMS:
2277 case TREE_MERONYMS:
2278 //highlight_senses_from_relative_trees(1 << gtk_notebook_get_current_page(notebook), g_ascii_strtoull(str_path, NULL, 10), text_view);
2279 break;
2280 default:
2281 highlight_senses_from_relative_lists(1 << gtk_notebook_get_current_page(notebook), (guint16) g_ascii_strtoull(str_path, NULL, 10), text_view);
2282 break;
2283 }
2284 }
2285
2286 g_free(term);
2287 g_free(str_path);
2288 term = str_path = NULL;
2289 }
2290 }
2291
2292 static void relative_row_activated(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data)
2293 {
2294 gchar *sel_term = NULL;
2295 GtkTreeModel *tree_store = NULL;
2296 GtkTreeIter iter = {0};
2297 GtkComboBox *combo_query = NULL;
2298 GtkEntry *txtQuery = NULL;
2299 GtkButton *button_search = NULL;
2300 GtkBuilder *gui_builder = GTK_BUILDER(user_data);
2301
2302
2303 if(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(tree_view), &tree_store, &iter))
2304 {
2305 gtk_tree_model_get(tree_store, &iter, 0, &sel_term, -1);
2306
2307 if(sel_term)
2308 {
2309 combo_query = GTK_COMBO_BOX(gtk_builder_get_object(gui_builder, COMBO_QUERY));
2310 txtQuery = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo_query)));
2311 button_search = GTK_BUTTON(gtk_builder_get_object(gui_builder, BUTTON_SEARCH));
2312
2313 gtk_entry_set_text(txtQuery, sel_term);
2314
2315 gtk_button_clicked(button_search);
2316
2317 g_free(sel_term);
2318 sel_term = NULL;
2319 }
2320 }
2321 }
2322
2323 static void clear_history(GtkMenuItem *menu_item, GtkListStore *list_store_query)
2324 {
2325 gchar *hist_file_path = NULL;
2326 GFile *history_file = NULL;
2327
2328 gtk_list_store_clear(list_store_query);
2329 hist_file_path = g_strconcat(g_get_user_config_dir(), G_DIR_SEPARATOR_S, PACKAGE_TARNAME, HISTORY_FILE_EXT, NULL);
2330 history_file = g_file_new_for_path(hist_file_path);
2331 g_free(hist_file_path);
2332 hist_file_path = NULL;
2333 if(g_file_query_exists(history_file, NULL))
2334 {
2335 g_file_delete(history_file, NULL, NULL);
2336 }
2337 g_object_unref(history_file);
2338 history_file = NULL;
2339 }
2340
2341 static void save_history_to_file(GtkMenuItem *menu_item, gpointer user_data)
2342 {
2343 GtkFileChooser *dialog = GTK_FILE_CHOOSER(gtk_file_chooser_dialog_new("Save As",
2344 NULL,
2345 GTK_FILE_CHOOSER_ACTION_SAVE,
2346 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2347 GTK_STOCK_SAVE, GTK_RESPONSE_OK,
2348 NULL));
2349 gchar *dest_filename = NULL;
2350 gint response = 0;
2351 gtk_file_chooser_set_do_overwrite_confirmation(dialog, TRUE);
2352 gtk_file_chooser_set_local_only(dialog, TRUE);
2353 gtk_file_chooser_set_create_folders(dialog, TRUE);
2354
2355 response = gtk_dialog_run(GTK_DIALOG(dialog));
2356 dest_filename = gtk_file_chooser_get_filename(dialog);
2357 gtk_widget_destroy(GTK_WIDGET(dialog));
2358 dialog = NULL;
2359
2360 if((GTK_RESPONSE_OK == response) && dest_filename)
2361 {
2362 GFile *history_file = NULL;
2363 gchar *hist_file_path = g_strconcat(g_get_user_config_dir(), G_DIR_SEPARATOR_S, PACKAGE_TARNAME, HISTORY_FILE_EXT, NULL);
2364 if(!hist_file_path)
2365 {
2366 g_free(dest_filename);
2367 dest_filename = NULL;
2368 g_error("Failed creating path to load history");
2369 }
2370 history_file = g_file_new_for_path(hist_file_path);
2371 g_free(hist_file_path);
2372 hist_file_path = NULL;
2373 if(g_file_query_exists(history_file, NULL))
2374 {
2375 GFile *dest_file = g_file_new_for_path(dest_filename);
2376 GError *err = NULL;
2377 GtkWidget *save_complete_dialog = NULL;
2378 if(g_file_copy(history_file, dest_file, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, &err))
2379 {
2380 save_complete_dialog = gtk_message_dialog_new(NULL,
2381 GTK_DIALOG_DESTROY_WITH_PARENT,
2382 GTK_MESSAGE_INFO,
2383 GTK_BUTTONS_OK,
2384 STR_HISTORY_FILE_SAVE_SUCCESS);
2385 g_object_set(save_complete_dialog, "title", STR_HISTORY_FILE_SAVE_SUCCESS_TITLE, NULL);
2386 gtk_dialog_run(GTK_DIALOG(save_complete_dialog));
2387 }
2388 else
2389 {
2390 save_complete_dialog = gtk_message_dialog_new(NULL,
2391 GTK_DIALOG_DESTROY_WITH_PARENT,
2392 GTK_MESSAGE_WARNING,
2393 GTK_BUTTONS_OK,
2394 STR_HISTORY_FILE_SAVE_FAILED,
2395 err->message);
2396 g_object_set(save_complete_dialog, "title", STR_HISTORY_FILE_SAVE_FAILURE_TITLE, NULL);
2397 gtk_dialog_run(GTK_DIALOG(save_complete_dialog));
2398 g_clear_error(&err);
2399 }
2400 gtk_widget_destroy(save_complete_dialog);
2401
2402 g_object_unref(dest_file);
2403 dest_file = NULL;
2404 }
2405 else
2406 {
2407 GtkWidget *save_failed_msgbox = gtk_message_dialog_new(NULL,
2408 GTK_DIALOG_DESTROY_WITH_PARENT,
2409 GTK_MESSAGE_WARNING,
2410 GTK_BUTTONS_OK,
2411 STR_HISTORY_FILE_SAVE_FAILED,
2412 STR_HISTORY_MISSING);
2413 g_object_set(save_failed_msgbox, "title", STR_HISTORY_FILE_SAVE_FAILURE_TITLE, NULL);
2414 gtk_dialog_run(GTK_DIALOG(save_failed_msgbox));
2415 gtk_widget_destroy(save_failed_msgbox);
2416 save_failed_msgbox = NULL;
2417 }
2418 g_object_unref(history_file);
2419 history_file = NULL;
2420 }
2421 if(dest_filename)
2422 {
2423 g_free(dest_filename);
2424 dest_filename = NULL;
2425 }
2426 }
2427
2428 static void query_combo_popup(GtkEntry *query_entry_widget, GtkMenu *popup_menu, GtkListStore *list_store_query)
2429 {
2430 GtkWidget *menu_item = NULL;
2431 GtkTreeIter iter = {0};
2432 gboolean menu_item_enabled = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(list_store_query), &iter);
2433
2434 menu_item = GTK_WIDGET(gtk_separator_menu_item_new());
2435 gtk_menu_shell_prepend(GTK_MENU_SHELL(popup_menu), menu_item);
2436 gtk_widget_show(menu_item);
2437
2438 menu_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_SAVE, NULL);
2439 gtk_menu_item_set_label(GTK_MENU_ITEM(menu_item), "_Save History");
2440 gtk_widget_set_sensitive(menu_item, menu_item_enabled);
2441 g_signal_connect(menu_item, "activate", G_CALLBACK(save_history_to_file), NULL);
2442 gtk_menu_shell_prepend(GTK_MENU_SHELL(popup_menu), menu_item);
2443 gtk_widget_show(menu_item);
2444
2445 menu_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_CLEAR, NULL);
2446 gtk_menu_item_set_label(GTK_MENU_ITEM(menu_item), "Cl_ear History");
2447 gtk_widget_set_sensitive(menu_item, menu_item_enabled);
2448 g_signal_connect(menu_item, "activate", G_CALLBACK(clear_history), list_store_query);
2449 gtk_menu_shell_prepend(GTK_MENU_SHELL(popup_menu), menu_item);
2450 gtk_widget_show(menu_item);
2451 }
2452
2453 static void load_history(GtkListStore *list_store_query)
2454 {
2455 gchar *hist_file_path = g_strconcat(g_get_user_config_dir(), G_DIR_SEPARATOR_S, PACKAGE_TARNAME, HISTORY_FILE_EXT, NULL);
2456 if(!hist_file_path)
2457 {
2458 g_warning("Failed creating path to load history");
2459 return;
2460 }
2461 FILE *hist_file = fopen(hist_file_path, "r");
2462 g_free(hist_file_path);
2463 hist_file_path = NULL;
2464 if(hist_file)
2465 {
2466 gchar lookup[MAX_LEMMA_LEN] = "";
2467 while(fgets(lookup, MAX_LEMMA_LEN, hist_file))
2468 {
2469 size_t lookup_len = strlen(lookup);
2470 // some char(s) and '\n' is minimum
2471 if(lookup_len > 1 && lookup[lookup_len - 1] == '\n')
2472 {
2473 // remove the new line character
2474 lookup[lookup_len - 1] = '\0';
2475 GtkTreeIter iter = {0};
2476 gtk_list_store_prepend(list_store_query, &iter);
2477 gtk_list_store_set(list_store_query, &iter, 0, lookup, -1);
2478 }
2479 }
2480 fclose(hist_file);
2481 }
2482 }
2483
2484 static void create_stores_renderers(GtkBuilder *gui_builder)
2485 {
2486 guint8 i = 0;
2487 GtkComboBox *combo_query = NULL;
2488 GtkListStore *list_store_query = NULL;
2489
2490 GtkTreeView *tree_view = NULL;
2491 GtkTreeStore *tree_store = NULL;
2492 GtkCellRenderer *tree_renderer = NULL;
2493 const gchar *col_name = NULL;
2494 GtkEntry *query_entry = NULL;
2495
2496 // combo box data store
2497 combo_query = GTK_COMBO_BOX(gtk_builder_get_object(gui_builder, COMBO_QUERY));
2498 list_store_query = gtk_list_store_new(1, G_TYPE_STRING);
2499
2500 load_history(list_store_query);
2501 // popup for saving & clearing history
2502 query_entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo_query)));
2503 g_signal_connect(query_entry, "populate-popup", G_CALLBACK(query_combo_popup), list_store_query);
2504
2505 g_signal_connect(GTK_TREE_MODEL(list_store_query), "row-inserted", G_CALLBACK(query_list_updated), gui_builder);
2506 gtk_combo_box_set_model(combo_query, GTK_TREE_MODEL(list_store_query));
2507 gtk_combo_box_set_entry_text_column(combo_query, 0);
2508 g_object_unref(list_store_query);
2509 list_store_query = NULL;
2510
2511 for(i = 0; i < TOTAL_RELATIVES; i++)
2512 {
2513 tree_view = GTK_TREE_VIEW(gtk_builder_get_object(gui_builder, relative_tree[i]));
2514 tree_renderer = gtk_cell_renderer_text_new();
2515 col_name = relative_tree[i];
2516 col_name = col_name + 4; // skip the first 4 chars "tree" in relative_tree string array
2517 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree_view), -1, col_name, tree_renderer, "text", 0, "weight", 1, NULL);
2518
2519 if(i == TREE_ANTONYMS)
2520 {
2521 gtk_tree_view_insert_column_with_attributes(tree_view, -1, STR_ANTONYM_HEADER_INDIRECT_VIA, tree_renderer, "text", 2, NULL);
2522 tree_store = gtk_tree_store_new(3, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_STRING);
2523 }
2524 else
2525 tree_store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_UINT);
2526
2527 gtk_tree_view_set_model(tree_view, GTK_TREE_MODEL(tree_store));
2528 g_object_unref(tree_store);
2529 tree_store = NULL;
2530
2531 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), FALSE);
2532
2533 g_signal_connect(tree_view, "cursor-changed", G_CALLBACK(relative_selection_changed), gui_builder);
2534 g_signal_connect(tree_view, "row-activated", G_CALLBACK(relative_row_activated), gui_builder);
2535 }
2536 }
2537
2538 static void button_next_clicked(GtkToolButton *toolbutton, gpointer user_data)
2539 {
2540 GtkBuilder *gui_builder = GTK_BUILDER(user_data);
2541 GtkComboBox *combo_query = GTK_COMBO_BOX(gtk_builder_get_object(gui_builder, COMBO_QUERY));
2542 gint16 active_item = gtk_combo_box_get_active(combo_query);
2543 GtkTreeModel *tree_model = NULL;
2544
2545 if(-1 == active_item)
2546 {
2547 tree_model = gtk_combo_box_get_model(combo_query);
2548 active_item = gtk_tree_model_iter_n_children(tree_model, NULL);
2549 }
2550
2551 gtk_combo_box_set_active(combo_query, active_item - 1);
2552 }
2553
2554 static void button_prev_clicked(GtkToolButton *toolbutton, gpointer user_data)
2555 {
2556 GtkBuilder *gui_builder = GTK_BUILDER(user_data);
2557 GtkComboBox *combo_query = GTK_COMBO_BOX(gtk_builder_get_object(gui_builder, COMBO_QUERY));
2558 gint16 active_item = gtk_combo_box_get_active(combo_query);
2559
2560 // in case if the search was successful and the index is 0 (current word)
2561 // then prev. should move it 1st element, so set active_item = 0, which + 1 will make index as 1
2562 // else if search had failed earlier, then prev. should take it to the 0th element
2563 // so leave it as -1; if it is something lesser than -1, make it -1
2564 if(-1 == active_item && last_search_successful)
2565 {
2566 active_item = 0;
2567 }
2568 else if(active_item < -1)
2569 {
2570 active_item = -1;
2571 }
2572
2573 gtk_combo_box_set_active(combo_query, active_item + 1);
2574 }
2575
2576 static gboolean combo_query_scroll(GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
2577 {
2578 gint current_index = 0;
2579
2580 if((GDK_SCROLL_UP == event->direction) || (GDK_SCROLL_DOWN == event->direction))
2581 {
2582 GtkComboBox *combo_box = GTK_COMBO_BOX(widget);
2583 GtkTreeModel *model = gtk_combo_box_get_model(combo_box);
2584 gint history_count = gtk_tree_model_iter_n_children(model, NULL);
2585
2586 current_index = gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
2587
2588 if(-1 == current_index && history_count > 0) current_index = 0;
2589
2590 if(GDK_SCROLL_DOWN == event->direction)
2591 {
2592 current_index++;
2593 if(history_count <= current_index) current_index = 0;
2594 }
2595 else
2596 {
2597 current_index--;
2598 if(current_index < 0) current_index = history_count - 1;
2599 }
2600
2601 gtk_combo_box_set_active(GTK_COMBO_BOX(widget), current_index);
2602
2603 return TRUE;
2604 }
2605
2606 return FALSE;
2607 }
2608
2609 static gboolean close_window(GtkAccelGroup *accel_group,
2610 GObject *acceleratable,
2611 guint keyval,
2612 GdkModifierType modifier,
2613 GObject *user_data)
2614 {
2615 GtkBuilder *gui_builder = GTK_BUILDER(user_data);
2616 GtkWidget *main_window = GTK_WIDGET(gtk_builder_get_object(gui_builder, WINDOW_MAIN));
2617
2618 gboolean deleted = FALSE;
2619 g_signal_emit_by_name(main_window, "delete-event", G_TYPE_BOOLEAN, &deleted);
2620 return TRUE;
2621 }
2622
2623 static void setup_toolbar(GtkBuilder *gui_builder)
2624 {
2625 GtkToolbar *toolbar = NULL;
2626 GtkToolItem *toolbar_item = NULL;
2627 GtkAccelGroup *accel_group = NULL;
2628 GClosure *close_window_closure = NULL;
2629 GClosure *close_window_closure_dummy = NULL;
2630
2631 // toolbar code starts here
2632 toolbar = GTK_TOOLBAR(gtk_builder_get_object(gui_builder, TOOLBAR));
2633
2634 toolbar_item = gtk_tool_button_new_from_stock(GTK_STOCK_GO_BACK);
2635 gtk_tool_button_set_use_underline(GTK_TOOL_BUTTON(toolbar_item), TRUE);
2636 gtk_tool_button_set_label(GTK_TOOL_BUTTON(toolbar_item), STR_TOOLITEM_PREV);
2637 gtk_tool_item_set_tooltip_text(toolbar_item, TOOLITEM_TOOLTIP_PREV);
2638 gtk_widget_set_sensitive(GTK_WIDGET(toolbar_item), FALSE);
2639 g_signal_connect(toolbar_item, "clicked", G_CALLBACK(button_prev_clicked), gui_builder);
2640 gtk_toolbar_insert(toolbar, toolbar_item, -1);
2641
2642 toolbar_item = gtk_tool_button_new_from_stock(GTK_STOCK_GO_FORWARD);
2643 gtk_tool_button_set_use_underline(GTK_TOOL_BUTTON(toolbar_item), TRUE);
2644 gtk_tool_button_set_label(GTK_TOOL_BUTTON(toolbar_item), STR_TOOLITEM_NEXT);
2645 gtk_tool_item_set_tooltip_text(toolbar_item, TOOLITEM_TOOLTIP_NEXT);
2646 gtk_widget_set_sensitive(GTK_WIDGET(toolbar_item), FALSE);
2647 g_signal_connect(toolbar_item, "clicked", G_CALLBACK(button_next_clicked), gui_builder);
2648 gtk_toolbar_insert(toolbar, toolbar_item, -1);
2649
2650 toolbar_item = gtk_separator_tool_item_new();
2651 gtk_toolbar_insert(toolbar, toolbar_item, -1);
2652
2653 toolbar_item = gtk_toggle_tool_button_new_from_stock(GTK_STOCK_DIALOG_INFO);
2654 gtk_tool_button_set_use_underline(GTK_TOOL_BUTTON(toolbar_item), TRUE);
2655 gtk_tool_button_set_label(GTK_TOOL_BUTTON(toolbar_item), STR_TOOLITEM_MODE);
2656 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(toolbar_item), advanced_mode);
2657 gtk_tool_item_set_tooltip_text(toolbar_item, TOOLITEM_TOOLTIP_MODE);
2658 g_signal_connect(toolbar_item, "toggled", G_CALLBACK(mode_toggled), gui_builder);
2659 gtk_toolbar_insert(toolbar, toolbar_item, -1);
2660
2661 toolbar_item = gtk_tool_button_new_from_stock(GTK_STOCK_PREFERENCES);
2662 gtk_tool_button_set_label(GTK_TOOL_BUTTON(toolbar_item), STR_TOOLITEM_OPTIONS);
2663 gtk_tool_button_set_use_underline(GTK_TOOL_BUTTON(toolbar_item), TRUE);
2664 gtk_tool_item_set_tooltip_text(toolbar_item, TOOLITEM_TOOLTIP_OPTIONS);
2665 g_signal_connect(toolbar_item, "clicked", G_CALLBACK(show_settings_dialog), gui_builder);
2666 gtk_toolbar_insert(toolbar, toolbar_item, -1);
2667
2668 /* if mod notify is present */
2669 if(mod_notifier)
2670 {
2671 toolbar_item = gtk_toggle_tool_button_new_from_stock(notifier_enabled ? GTK_STOCK_YES : GTK_STOCK_NO);
2672 gtk_tool_button_set_use_underline(GTK_TOOL_BUTTON(toolbar_item), TRUE);
2673 gtk_tool_button_set_label(GTK_TOOL_BUTTON(toolbar_item), STR_TOOLITEM_NOTIFY);
2674 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(toolbar_item), notifier_enabled);
2675 gtk_tool_item_set_tooltip_text(toolbar_item, TOOLITEM_TOOLTIP_NOTIFY);
2676 g_signal_connect(toolbar_item, "toggled", G_CALLBACK(notification_toggled), gui_builder);
2677 gtk_toolbar_insert(toolbar, toolbar_item, -1);
2678
2679 notify_toolbar_index = gtk_toolbar_get_item_index(toolbar, toolbar_item);
2680 }
2681
2682 toolbar_item = gtk_separator_tool_item_new();
2683 gtk_toolbar_insert(toolbar, toolbar_item, -1);
2684
2685 toolbar_item = gtk_tool_button_new_from_stock(GTK_STOCK_ABOUT);
2686 gtk_tool_button_set_use_underline(GTK_TOOL_BUTTON(toolbar_item), TRUE);
2687 gtk_tool_button_set_label(GTK_TOOL_BUTTON(toolbar_item), STR_TOOLITEM_ABOUT);
2688 gtk_tool_item_set_tooltip_text(toolbar_item, TOOLITEM_TOOLTIP_ABOUT);
2689 g_signal_connect(toolbar_item, "clicked", G_CALLBACK(about_activate), NULL);
2690 gtk_toolbar_insert(toolbar, toolbar_item, -1);
2691
2692 toolbar_item = gtk_tool_button_new_from_stock(GTK_STOCK_QUIT);
2693 gtk_tool_button_set_use_underline(GTK_TOOL_BUTTON(toolbar_item), TRUE);
2694 gtk_tool_button_set_label(GTK_TOOL_BUTTON(toolbar_item), STR_TOOLITEM_QUIT);
2695 gtk_tool_item_set_tooltip_text(toolbar_item, TOOLITEM_TOOLTIP_QUIT);
2696 g_signal_connect(toolbar_item, "clicked", G_CALLBACK(quit_activate), NULL);
2697 gtk_toolbar_insert(toolbar, toolbar_item, -1);
2698
2699 // add accelerators
2700 GtkWindow *window = GTK_WINDOW(gtk_builder_get_object(gui_builder, WINDOW_MAIN));
2701 accel_group = gtk_accel_group_new();
2702 gtk_window_add_accel_group(window, accel_group);
2703 gtk_widget_add_accelerator(GTK_WIDGET(toolbar_item),
2704 "clicked",
2705 accel_group,
2706 GDK_q,
2707 GDK_CONTROL_MASK,
2708 0);
2709 close_window_closure = g_cclosure_new_object(G_CALLBACK(close_window), G_OBJECT(gui_builder));
2710 /* due to GLib implementation's limitation, a dummy closure needs to be created
2711 refer: gtk_accel_group_connect documentation */
2712 close_window_closure_dummy = g_cclosure_new_object(G_CALLBACK(close_window), G_OBJECT(gui_builder));
2713
2714 gtk_accel_group_connect(accel_group, GDK_w, GDK_CONTROL_MASK, 0, close_window_closure);
2715 gtk_accel_group_connect(accel_group, GDK_Escape, 0, 0, close_window_closure_dummy);
2716
2717 gtk_widget_show_all(GTK_WIDGET(toolbar));
2718 }
2719
2720 static GtkMenu *create_popup_menu(GtkBuilder *gui_builder)
2721 {
2722 GtkMenu *menu = NULL;
2723 GtkSeparatorMenuItem *menu_separator = NULL;
2724 GtkImageMenuItem *menu_item = NULL;
2725
2726 // Initialize popup menu/sub menus
2727 menu = GTK_MENU(gtk_menu_new());
2728
2729 /* if mod notify is present, setup a notifications menu */
2730 if(mod_notifier)
2731 {
2732 menu_notify = GTK_CHECK_MENU_ITEM(gtk_check_menu_item_new_with_mnemonic(STR_TOOLITEM_NOTIFY));
2733 // load the settings value
2734 gtk_check_menu_item_set_active(menu_notify, notifier_enabled);
2735
2736 menu_separator = GTK_SEPARATOR_MENU_ITEM(gtk_separator_menu_item_new());
2737 gtk_menu_shell_append(GTK_MENU_SHELL(menu), GTK_WIDGET(menu_notify));
2738 gtk_menu_shell_append(GTK_MENU_SHELL(menu), GTK_WIDGET(menu_separator));
2739 g_signal_connect(menu_notify, "toggled", G_CALLBACK(notification_toggled), gui_builder);
2740 }
2741
2742 menu_item = GTK_IMAGE_MENU_ITEM(gtk_image_menu_item_new_from_stock(GTK_STOCK_CLOSE, NULL));
2743 gtk_menu_item_set_use_underline(GTK_MENU_ITEM(menu_item), TRUE);
2744 gtk_menu_item_set_label(GTK_MENU_ITEM(menu_item), "_Hide");
2745 g_signal_connect(menu_item, "activate", G_CALLBACK(trayicon_menu_toggled), gui_builder);
2746 gtk_menu_shell_append(GTK_MENU_SHELL(menu), GTK_WIDGET(menu_item));
2747
2748 menu_item = GTK_IMAGE_MENU_ITEM(gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL));
2749 g_signal_connect(menu_item, "activate", G_CALLBACK(quit_activate), NULL);
2750 gtk_menu_shell_append(GTK_MENU_SHELL(menu), GTK_WIDGET(menu_item));
2751
2752 gtk_widget_show_all(GTK_WIDGET(menu));
2753
2754 return menu;
2755 }
2756
2757 static void create_text_view_tags(GtkBuilder *gui_builder)
2758 {
2759 guint8 i = 0;
2760 GtkTextView *text_view = NULL;
2761 GtkTextBuffer *buffer = NULL;
2762
2763 // create text view tags
2764 text_view = GTK_TEXT_VIEW(gtk_builder_get_object(gui_builder, TEXT_VIEW_DEFINITIONS));
2765 buffer = gtk_text_view_get_buffer(text_view);
2766
2767 gtk_text_buffer_create_tag(buffer, TAG_LEMMA, "weight", PANGO_WEIGHT_BOLD, NULL);
2768 gtk_text_buffer_create_tag(buffer, TAG_POS, "foreground", "red", "style", PANGO_STYLE_ITALIC, NULL);
2769 gtk_text_buffer_create_tag(buffer, TAG_COUNTER, "left_margin", 15, "weight", PANGO_WEIGHT_BOLD, "foreground", "gray", NULL);
2770 gtk_text_buffer_create_tag(buffer, TAG_EXAMPLE, "foreground", "blue", "font", "Serif Italic 10", "left_margin", 45, NULL);
2771 gtk_text_buffer_create_tag(buffer, TAG_MATCH, "foreground", "DarkGreen", "weight", PANGO_WEIGHT_SEMIBOLD, NULL);
2772 gtk_text_buffer_create_tag(buffer, TAG_SUGGESTION, "foreground", "blue" , "left_margin", 25, NULL);
2773 gtk_text_buffer_create_tag(buffer, TAG_HIGHLIGHT, "background", "black", "foreground", "white", NULL);
2774
2775 for(i = 0; i < FAMILIARITY_COUNT; i++)
2776 {
2777 gtk_text_buffer_create_tag(buffer, familiarity[i], "background", freq_colors[i],
2778 "foreground", (i < 4)? "white" : "black", "font", "Monospace 9", NULL);
2779 }
2780
2781 g_signal_connect(text_view, "button-press-event", G_CALLBACK(text_view_button_pressed), gui_builder);
2782 g_signal_connect(text_view, "button-release-event", G_CALLBACK(text_view_button_released), gui_builder);
2783 g_signal_connect(text_view, "selection-received", G_CALLBACK(text_view_selection_made), gui_builder);
2784 }
2785
2786 static void load_preference_hotkey(gboolean *first_run)
2787 {
2788 GError *err = NULL;
2789 gchar *version = g_key_file_get_string(config_file, GROUP_SETTINGS, KEY_VERSION, NULL);
2790
2791 if(version)
2792 {
2793 if(0 != g_strcmp0(version, PACKAGE_VERSION))
2794 {
2795 /* version number is split here. Major, Minor, Micro */
2796 gchar **version_numbers = g_strsplit(version, ".", 3);
2797 gchar **current_version_numbers = g_strsplit(PACKAGE_VERSION, ".", 3);
2798
2799 for(guint8 i = 0; (NULL != version_numbers[i] && NULL != current_version_numbers[i]); i++)
2800 {
2801 if(g_ascii_digit_value(version_numbers[i][0]) < g_ascii_digit_value(current_version_numbers[i][0]))
2802 {
2803 *first_run = TRUE;
2804 break;
2805 }
2806 }
2807
2808 g_strfreev(version_numbers);
2809 g_strfreev(current_version_numbers);
2810 version_numbers = current_version_numbers = NULL;
2811 }
2812 g_free(version);
2813 version = NULL;
2814 }
2815
2816 /* zero can be returned in three cases:
2817 i. Older version - no key set in conf (err will be set)
2818 ii. New version with no key in conf file set (err will be set)
2819 iii. New version & entries present (err will not be set) */
2820
2821 app_hotkey.accel_key = g_key_file_get_integer(config_file, GROUP_SETTINGS, KEY_ACCEL_KEY, &err);
2822 app_hotkey.accel_mods = g_key_file_get_integer(config_file, GROUP_SETTINGS, KEY_ACCEL_MODS, NULL);
2823 app_hotkey.accel_flags = g_key_file_get_integer(config_file, GROUP_SETTINGS, KEY_ACCEL_FLAGS, NULL);
2824
2825 if(err)
2826 {
2827 g_error_free(err);
2828 err = NULL;
2829 }
2830 else if(*first_run) /* if no error was set, then the key is present, in that case */
2831 *first_run = FALSE; /* a first run is not right, it's just a version above 1.0.1 where hotkeys are already set */
2832 }
2833
2834 static gboolean load_preferences(GtkWindow *parent)
2835 {
2836 gboolean first_run = FALSE;
2837
2838 config_file = g_key_file_new();
2839 if(!config_file)
2840 {
2841 g_error("Error creating GKeyFile to load/store preferences");
2842 }
2843
2844 gchar *conf_file_path = g_strconcat(g_get_user_config_dir(), G_DIR_SEPARATOR_S, PACKAGE_TARNAME, CONF_FILE_EXT, NULL);
2845 if(g_key_file_load_from_file(config_file, conf_file_path, G_KEY_FILE_KEEP_COMMENTS, NULL))
2846 {
2847 advanced_mode = g_key_file_get_boolean(config_file, GROUP_SETTINGS, KEY_MODE, NULL);
2848 notifier_enabled = g_key_file_get_boolean(config_file, GROUP_SETTINGS, KEY_NOTIFICATIONS, NULL);
2849 GError *err = NULL;
2850 show_polysemy = g_key_file_get_boolean(config_file, GROUP_SETTINGS, KEY_POLYSEMY, &err);
2851 if(err)
2852 {
2853 show_polysemy = TRUE;
2854 g_clear_error(&err);
2855 }
2856 show_trayicon = g_key_file_get_boolean(config_file, GROUP_SETTINGS, KEY_TRAYICON, &err);
2857 if(err)
2858 {
2859 show_trayicon = TRUE;
2860 g_clear_error(&err);
2861 }
2862 load_preference_hotkey(&first_run);
2863 launch_minimized = g_key_file_get_boolean(config_file, GROUP_SETTINGS, KEY_LAUNCH_HIDDEN, &err);
2864 if(err)
2865 {
2866 launch_minimized = TRUE;
2867 g_clear_error(&err);
2868 }
2869 }
2870 else
2871 first_run = TRUE;
2872
2873 if(conf_file_path)
2874 {
2875 g_free(conf_file_path);
2876 conf_file_path = NULL;
2877 }
2878
2879 /* The user might never close the app. and just shut down the system.
2880 Conf file won't be written to disk in that case. Hence save the
2881 conf file when its a first run with the defaults. This is done
2882 by the main func. */
2883 return first_run;
2884 }
2885
2886 /* this function uses FILE instead of GFile to workaround
2887 * a bug in GDataOutputStream, which doesn't convert '\n'
2888 * (C's std. new file literal)
2889 * to the target OS's line character; while std. FILE does */
2890 static gboolean update_history_in_file(const gchar *term)
2891 {
2892 gboolean update_succeeded = FALSE;
2893 gchar *hist_file_path = g_strconcat(g_get_user_config_dir(), G_DIR_SEPARATOR_S, PACKAGE_TARNAME, HISTORY_FILE_EXT, NULL);
2894 if(!hist_file_path)
2895 {
2896 g_error("Failed creating path to save history");
2897 }
2898 FILE *hist_file = fopen(hist_file_path, "a+");
2899 g_free(hist_file_path);
2900 hist_file_path = NULL;
2901 if(hist_file)
2902 {
2903 update_succeeded = (fprintf(hist_file, "%s\n", term) > 0);
2904 fclose(hist_file);
2905 hist_file = NULL;
2906 }
2907 return update_succeeded;
2908 }
2909
2910 static void save_preferences_to_file()
2911 {
2912 gsize file_len = 0;
2913 gchar *conf_file_path = g_strconcat(g_get_user_config_dir(), G_DIR_SEPARATOR_S, PACKAGE_TARNAME, CONF_FILE_EXT, NULL);
2914 gchar *file_contents = g_key_file_to_data(config_file, &file_len, NULL);
2915 GError *err = NULL;
2916
2917 if(!conf_file_path || !file_contents)
2918 {
2919 g_error("Error saving preferences to file");
2920 }
2921 if(!g_file_set_contents(conf_file_path, file_contents, file_len, &err))
2922 {
2923 g_warning("Failed to save preferences to %s\nError: %s\n", conf_file_path, err->message);
2924 g_error_free(err);
2925 err = NULL;
2926 }
2927
2928 g_free(conf_file_path);
2929 conf_file_path = NULL;
2930 g_free(file_contents);
2931 file_contents = NULL;
2932 }
2933
2934 static void save_preferences()
2935 {
2936 g_key_file_set_comment(config_file, NULL, NULL, SETTINGS_COMMENT, NULL);
2937
2938 g_key_file_set_string(config_file, GROUP_SETTINGS, KEY_VERSION, PACKAGE_VERSION);
2939 g_key_file_set_integer(config_file, GROUP_SETTINGS, KEY_ACCEL_KEY, app_hotkey.accel_key);
2940 g_key_file_set_integer(config_file, GROUP_SETTINGS, KEY_ACCEL_MODS, app_hotkey.accel_mods);
2941 g_key_file_set_integer(config_file, GROUP_SETTINGS, KEY_ACCEL_FLAGS, app_hotkey.accel_flags);
2942 g_key_file_set_boolean(config_file, GROUP_SETTINGS, KEY_MODE, advanced_mode);
2943 g_key_file_set_boolean(config_file, GROUP_SETTINGS, KEY_NOTIFICATIONS, notifier_enabled);
2944 g_key_file_set_boolean(config_file, GROUP_SETTINGS, KEY_POLYSEMY, show_polysemy);
2945 g_key_file_set_boolean(config_file, GROUP_SETTINGS, KEY_TRAYICON, show_trayicon);
2946 g_key_file_set_boolean(config_file, GROUP_SETTINGS, KEY_LAUNCH_HIDDEN, launch_minimized);
2947
2948 save_preferences_to_file();
2949 }
2950
2951 static gboolean autocomplete_selected(GtkEntryCompletion *query_completion, GtkTreeModel *model, GtkTreeIter *iter, GtkButton *button)
2952 {
2953 gchar *match = NULL;
2954 gtk_tree_model_get(model, iter, 0, &match, -1);
2955 if(match)
2956 {
2957 GtkEntry *query_entry = GTK_ENTRY(gtk_entry_completion_get_entry(query_completion));
2958 gtk_entry_set_text(query_entry, match);
2959 g_free(match);
2960 match = NULL;
2961 gtk_button_clicked(button);
2962 return TRUE;
2963 }
2964 return FALSE;
2965 }
2966
2967 static void show_loading(GtkBuilder *gui_builder)
2968 {
2969 gchar status_msg[MAX_STATUS_MSG] = "";
2970 GtkWindow *window = GTK_WINDOW(gtk_builder_get_object(gui_builder, WINDOW_MAIN));
2971 GtkStatusbar *status_bar = GTK_STATUSBAR(gtk_builder_get_object(gui_builder, STATUSBAR));
2972 // set the loading message on status bar
2973 gtk_statusbar_pop(status_bar, status_msg_context_id);
2974 g_snprintf(status_msg, MAX_STATUS_MSG, STR_STATUS_INDEXING);
2975 status_msg_context_id = gtk_statusbar_get_context_id(status_bar, STATUS_DESC_LOADING_INDEX);
2976 gtk_statusbar_push(status_bar, status_msg_context_id, status_msg);
2977
2978 // if visible, repaint the statusbar after setting the status message, for it to get reflected
2979 if(gtk_widget_get_visible(GTK_WIDGET(window)))
2980 gdk_window_process_updates(((GtkWidget*)status_bar)->window, FALSE);
2981 }
2982
2983 static void attach_loaded_terms(WordnetTermsLoaderData *loader_data)
2984 {
2985 g_free(loader_data->index_file_contents);
2986 loader_data->index_file_contents = NULL;
2987 loader_data->index_file_length = loader_data->pos_in_file = 0;
2988 loader_data->last_lemma = NULL;
2989
2990 GtkEntryCompletion *query_entry_completion = gtk_entry_completion_new();
2991 gtk_entry_completion_set_model(query_entry_completion, GTK_TREE_MODEL(loader_data->completion_list));
2992 g_object_unref(loader_data->completion_list);
2993 loader_data->completion_list = NULL;
2994 gtk_entry_completion_set_text_column(query_entry_completion, 0);
2995 gtk_entry_completion_set_minimum_key_length(query_entry_completion, 3);
2996 GtkButton *query_button = GTK_BUTTON(gtk_builder_get_object(loader_data->gui_builder, BUTTON_SEARCH));
2997 g_signal_connect(query_entry_completion, "match-selected", G_CALLBACK(autocomplete_selected), query_button);
2998
2999 GtkBin *query_combo = GTK_BIN(gtk_builder_get_object(loader_data->gui_builder, COMBO_QUERY));
3000 GtkEntry *query_entry = GTK_ENTRY(gtk_bin_get_child(query_combo));
3001 gtk_entry_set_completion(query_entry, query_entry_completion);
3002
3003 // clear the loading message from status bar
3004 GtkStatusbar *status_bar = GTK_STATUSBAR(gtk_builder_get_object(loader_data->gui_builder, STATUSBAR));
3005 gtk_statusbar_pop(status_bar, status_msg_context_id);
3006 }
3007
3008 static gboolean terms_loader(WordnetTermsLoaderData *loader_data)
3009 {
3010 gboolean load_pending = TRUE, is_reading = TRUE;
3011 gchar lemma[MAX_LEMMA_LEN] = "", ch = 0;
3012 gsize i = 0;
3013 while((ch = loader_data->index_file_contents[loader_data->pos_in_file++]) != '\n')
3014 {
3015 if(is_reading)
3016 {
3017 if(ch == '%')
3018 {
3019 is_reading = FALSE;
3020 lemma[i++] = '\n';
3021 lemma[i] = '\0';
3022 if(g_strcmp0(lemma, loader_data->last_lemma) != 0)
3023 {
3024 g_string_append(wordnet_terms, lemma);
3025 loader_data->last_lemma = wordnet_terms->str + wordnet_terms->len - i;
3026
3027 lemma[i - 1] = '\0';
3028 GtkTreeIter iter = {0};
3029 gtk_list_store_append(loader_data->completion_list, &iter);
3030 gtk_list_store_set(loader_data->completion_list, &iter, 0, lemma, -1);
3031 }
3032 }
3033 else
3034 {
3035 lemma[i++] = (ch == '_') ? ' ' : ch;
3036 }
3037 }
3038 }
3039
3040 if(loader_data->pos_in_file == loader_data->index_file_length)
3041 {
3042 attach_loaded_terms(loader_data);
3043 load_pending = FALSE;
3044 }
3045
3046 return load_pending;
3047 }
3048
3049 static gboolean wordnet_terms_load(WordnetTermsLoaderData *loader_data)
3050 {
3051 gboolean succeeded = FALSE;
3052 gchar *index_file_path = g_strdup_printf(SENSEIDXFILE, SetSearchdir());
3053 if(g_file_get_contents(index_file_path,
3054 &loader_data->index_file_contents,
3055 &loader_data->index_file_length,
3056 NULL))
3057 {
3058 show_loading(loader_data->gui_builder);
3059
3060 wordnet_terms = g_string_new("");
3061 loader_data->last_lemma = wordnet_terms->str;
3062 loader_data->completion_list = gtk_list_store_new(1, G_TYPE_STRING);
3063
3064 g_idle_add((GSourceFunc) terms_loader, loader_data);
3065 succeeded = TRUE;
3066 }
3067 g_free(index_file_path);
3068 index_file_path = NULL;
3069
3070 return succeeded;
3071 }
3072
3073 #ifdef X11_AVAILABLE
3074 static void lookup_ignorable_modifiers ()
3075 {
3076 GdkKeymap *keymap = gdk_keymap_get_default();
3077
3078 /* caps_lock */
3079 egg_keymap_resolve_virtual_modifiers (keymap, EGG_VIRTUAL_LOCK_MASK, &caps_lock_mask);
3080 /* num_lock */
3081 egg_keymap_resolve_virtual_modifiers (keymap, EGG_VIRTUAL_NUM_LOCK_MASK, &num_lock_mask);
3082 /* scroll_lock */
3083 egg_keymap_resolve_virtual_modifiers (keymap, EGG_VIRTUAL_SCROLL_LOCK_MASK, &scroll_lock_mask);
3084 }
3085
3086 gboolean grab_ungrab_with_ignorable_modifiers (GtkAccelKey *binding, gboolean grab)
3087 {
3088 guint i = 0, actual_mods = 0;
3089 Window rootwin = XDefaultRootWindow(dpy);
3090 Bool x_error = False;
3091 guint mod_masks [] =
3092 {
3093 0, /* modifier only */
3094 num_lock_mask,
3095 caps_lock_mask,
3096 scroll_lock_mask,
3097 num_lock_mask | caps_lock_mask,
3098 num_lock_mask | scroll_lock_mask,
3099 caps_lock_mask | scroll_lock_mask,
3100 num_lock_mask | caps_lock_mask | scroll_lock_mask,
3101 };
3102
3103 egg_keymap_resolve_virtual_modifiers (gdk_keymap_get_default(), binding->accel_mods, &actual_mods);
3104
3105 for(i = 0; (i < G_N_ELEMENTS (mod_masks) && (False == x_error)); i++)
3106 {
3107 if(grab)
3108 {
3109 XGrabKey (dpy,
3110 XKeysymToKeycode(dpy, binding->accel_key),
3111 actual_mods | mod_masks [i],
3112 rootwin,
3113 False,
3114 GrabModeAsync,
3115 GrabModeAsync);
3116 }
3117 else
3118 {
3119 XUngrabKey (dpy,
3120 XKeysymToKeycode(dpy, binding->accel_key),
3121 actual_mods | mod_masks [i],
3122 rootwin);
3123 }
3124 }
3125
3126 return !x_error;
3127 }
3128
3129 #elif defined G_OS_WIN32
3130
3131 static gboolean try_convert_to_vk(guint gdk_key, BYTE *vk)
3132 {
3133 gboolean converted = FALSE;
3134 static const gdk_vk keys[] = {
3135 {GDK_KEY_Insert, VK_INSERT},
3136 {GDK_KEY_Delete, VK_DELETE},
3137 {GDK_KEY_Select, VK_SELECT},
3138 {GDK_KEY_Print, VK_PRINT},
3139 {GDK_KEY_Execute, VK_EXECUTE},
3140 {GDK_KEY_Page_Up, VK_PRIOR},
3141 {GDK_KEY_Page_Down, VK_NEXT},
3142 {GDK_KEY_End, VK_END},
3143 {GDK_KEY_Clear, VK_CLEAR},
3144 {GDK_KEY_Pause, VK_PAUSE},
3145 {GDK_KEY_Cancel, VK_CANCEL},
3146 {GDK_KEY_Menu, VK_MENU},
3147 {GDK_KEY_3270_PrintScreen, VK_SNAPSHOT},
3148 {GDK_KEY_Help, VK_HELP},
3149 {GDK_KEY_Super_L, VK_LWIN},
3150 {GDK_KEY_Super_R, VK_RWIN},
3151 {GDK_KEY_Sleep, VK_SLEEP},
3152 {GDK_KEY_Back, VK_BROWSER_BACK},
3153 {GDK_KEY_Forward, VK_BROWSER_FORWARD},
3154 {GDK_KEY_Refresh, VK_BROWSER_REFRESH},
3155 {GDK_KEY_Stop, VK_BROWSER_STOP},
3156 {GDK_KEY_Search, VK_BROWSER_SEARCH},
3157 {GDK_KEY_Favorites, VK_BROWSER_FAVORITES},
3158 {GDK_KEY_HomePage, VK_BROWSER_HOME},
3159 {GDK_KEY_AudioMute, VK_VOLUME_MUTE},
3160 {GDK_KEY_AudioLowerVolume, VK_VOLUME_DOWN},
3161 {GDK_KEY_AudioRaiseVolume, VK_VOLUME_UP},
3162 {GDK_KEY_AudioNext, VK_MEDIA_NEXT_TRACK},
3163 {GDK_KEY_AudioPrev, VK_MEDIA_PREV_TRACK},
3164 {GDK_KEY_AudioStop, VK_MEDIA_STOP},
3165 {GDK_KEY_AudioPlay, VK_MEDIA_PLAY_PAUSE},
3166 {GDK_KEY_Mail, VK_LAUNCH_MAIL},
3167 {GDK_KEY_3270_Play, VK_PLAY}
3168 };
3169 // function keys
3170 if((gdk_key >= GDK_KEY_F1) && (gdk_key <= GDK_KEY_F24))
3171 {
3172 *vk = VK_F1 + gdk_key - GDK_KEY_F1;
3173 converted = TRUE;
3174 }
3175 // numpad 0 to 9
3176 else if((gdk_key >= GDK_KEY_KP_0) && (gdk_key <= GDK_KEY_KP_9))
3177 {
3178 *vk = VK_NUMPAD0 + gdk_key - GDK_KEY_KP_0;
3179 converted = TRUE;
3180 }
3181 // home & arrow keys
3182 else if((gdk_key >= GDK_KEY_Home) && (gdk_key <= GDK_KEY_Down))
3183 {
3184 *vk = VK_HOME + gdk_key - GDK_KEY_Home;
3185 converted = TRUE;
3186 }
3187 // numpad multiply to divide (includes separator)
3188 else if((gdk_key >= GDK_KEY_KP_Multiply) && (gdk_key <= GDK_KEY_KP_Divide))
3189 {
3190 *vk = VK_MULTIPLY + gdk_key - GDK_KEY_KP_Multiply;
3191 converted = TRUE;
3192 }
3193 else
3194 {
3195 guint8 i = 0;
3196 for(; i < G_N_ELEMENTS(keys); ++i)
3197 {
3198 if(keys[i].gdk_key == gdk_key)
3199 {
3200 *vk = keys[i].vk_key;
3201 converted = TRUE;
3202 break;
3203 }
3204 }
3205 }
3206 return converted;
3207 }
3208
3209 gboolean grab_ungrab_with_ignorable_modifiers (GtkAccelKey *binding, gboolean grab)
3210 {
3211 gboolean reg_success = FALSE;
3212
3213 if(grab)
3214 {
3215 SHORT key = 0;
3216 BYTE vk_key = 0;
3217
3218 if(try_convert_to_vk(binding->accel_key, &vk_key) || ((key = VkKeyScan(binding->accel_key)) != -1))
3219 {
3220 if((key != -1) && (key != 0))
3221 {
3222 vk_key = LOBYTE(key);
3223 }
3224 UINT win_mods = 0;
3225 win_mods |= ((binding->accel_mods & GDK_SHIFT_MASK) || (HIBYTE(key) & 1)) ? MOD_SHIFT : 0;
3226 win_mods |= ((binding->accel_mods & GDK_CONTROL_MASK) || (HIBYTE(key) & 2)) ? MOD_CONTROL : 0;
3227 win_mods |= ((binding->accel_mods & GDK_MOD1_MASK) || (HIBYTE(key) & 4)) ? MOD_ALT : 0;
3228 win_mods |= (binding->accel_mods & GDK_SUPER_MASK) ? MOD_WIN : 0;
3229
3230 reg_success = RegisterHotKey(hMainWindow, 1, win_mods, vk_key);
3231 }
3232 }
3233 else
3234 {
3235 reg_success = UnregisterHotKey(hMainWindow, 1);
3236 }
3237
3238 return reg_success;
3239 }
3240 #endif // X11_AVAILABLE
3241
3242 static gboolean register_unregister_hotkey(gboolean first_run, gboolean setup_hotkey)
3243 {
3244 guint i = 0;
3245
3246 /* if it's first run and hotkey needs to be setup; try with the preset hotkey trials
3247 else just grab or ungrab as per the last param passed */
3248 if(first_run && setup_hotkey)
3249 {
3250 app_hotkey.accel_mods = GDK_CONTROL_MASK | GDK_MOD1_MASK;
3251
3252 for(i = 0; i < G_N_ELEMENTS(hotkey_trials); ++i)
3253 {
3254 app_hotkey.accel_key = hotkey_trials[i];
3255
3256 if(grab_ungrab_with_ignorable_modifiers(&app_hotkey, TRUE))
3257 return TRUE;
3258 }
3259
3260 /* when control reaches here, it means hotkey couldn't be set; but since
3261 it's first run, save_pref will be called and whatever value is present
3262 in app_hotkey (last key in hotkey_trials) will be saved; so 0 it */
3263 app_hotkey.accel_key = app_hotkey.accel_mods = app_hotkey.accel_flags = 0;
3264
3265 return FALSE;
3266 }
3267
3268 return(grab_ungrab_with_ignorable_modifiers(&app_hotkey, setup_hotkey));
3269 }
3270
3271 static void setup_settings_dialog(GtkBuilder *gui_builder)
3272 {
3273 GtkLabel *hotkey_label = GTK_LABEL(gtk_builder_get_object(gui_builder, LABEL_HOTKEY));
3274 GtkContainer *hbox = GTK_CONTAINER(gtk_builder_get_object(gui_builder, HBOX_HOTKEY));
3275 GtkDialog *settings_dialog = GTK_DIALOG(gtk_builder_get_object(gui_builder, DIALOG_OPTIONS));
3276 GtkWidget *hotkey_accel_cell = create_hotkey_editor();
3277
3278 gtk_container_add(hbox, hotkey_accel_cell);
3279 gtk_label_set_mnemonic_widget(hotkey_label, hotkey_accel_cell);
3280 g_signal_connect(GTK_WIDGET(settings_dialog), "response", G_CALLBACK(gtk_widget_hide), NULL);
3281 set_settings_to_check_boxes(gui_builder);
3282 }
3283
3284 static void set_settings_to_check_boxes(GtkBuilder *gui_builder)
3285 {
3286 GtkToggleButton *polysemy_show_btn = GTK_TOGGLE_BUTTON(gtk_builder_get_object(gui_builder, CHKBTN_POLYSEMY));
3287 GtkToggleButton *status_show_btn = GTK_TOGGLE_BUTTON(gtk_builder_get_object(gui_builder, CHKBTN_STATUS));
3288 GtkToggleButton *launch_min_btn = GTK_TOGGLE_BUTTON(gtk_builder_get_object(gui_builder, CHKBTN_LAUNCH_MIN));
3289
3290 // show_trayicon is the variable, while the checkbox is "hide status icon"
3291 // hence the NOT op.; same goes for launch_minimized too
3292 gtk_toggle_button_set_active(status_show_btn, !show_trayicon);
3293 gtk_toggle_button_set_active(launch_min_btn, !launch_minimized);
3294 gtk_toggle_button_set_active(polysemy_show_btn, show_polysemy);
3295 }
3296
3297 static void get_settings_from_check_boxes(GtkBuilder *gui_builder,
3298 gboolean *new_trayicon_show,
3299 gboolean *new_launch_min,
3300 gboolean *new_polysemy_show)
3301 {
3302 GtkToggleButton *polysemy_show_btn = GTK_TOGGLE_BUTTON(gtk_builder_get_object(gui_builder, CHKBTN_POLYSEMY));
3303 GtkToggleButton *status_show_btn = GTK_TOGGLE_BUTTON(gtk_builder_get_object(gui_builder, CHKBTN_STATUS));
3304 GtkToggleButton *launch_min_btn = GTK_TOGGLE_BUTTON(gtk_builder_get_object(gui_builder, CHKBTN_LAUNCH_MIN));
3305
3306 // show_trayicon is the variable, while the checkbox is "hide status icon"
3307 // hence the NOT op.; same goes for launch_minimized
3308 *new_trayicon_show = !gtk_toggle_button_get_active(status_show_btn);
3309 *new_launch_min = !gtk_toggle_button_get_active(launch_min_btn);
3310 *new_polysemy_show = gtk_toggle_button_get_active(polysemy_show_btn);
3311 }
3312
3313 static void apply_and_save_settings(GtkBuilder *gui_builder)
3314 {
3315 GtkButton *search_button = GTK_BUTTON(gtk_builder_get_object(gui_builder, BUTTON_SEARCH));
3316 gboolean new_show_trayicon = FALSE, new_show_polysemy = FALSE;
3317
3318 get_settings_from_check_boxes(gui_builder, &new_show_trayicon, &launch_minimized, &new_show_polysemy);
3319 if(show_polysemy != new_show_polysemy)
3320 {
3321 show_polysemy = new_show_polysemy;
3322 last_search_successful = FALSE;
3323 gtk_button_clicked(search_button);
3324 }
3325 if(show_trayicon != new_show_trayicon)
3326 {
3327 GtkStatusIcon *status_icon = GTK_STATUS_ICON(gtk_builder_get_object(gui_builder, STATUS_ICON));
3328 show_trayicon = new_show_trayicon;
3329 gtk_status_icon_set_visible(status_icon, show_trayicon);
3330 }
3331
3332 save_preferences();
3333 }
3334
3335 /* flags when changed and reverted don't affect, since only Apply code makes permanent
3336 * changes, while hotkey alone is an exception to this; when changed, it captures the
3337 * hotkey changed to; hence when reverted, this alone should be undone and the old
3338 * hotkey should be set again
3339 */
3340 static void revert_settings(GtkBuilder *gui_builder, GtkAccelKey *hotkey_backup)
3341 {
3342 /* if prev. hotkey and app_hotkey are not the same and 'Apply' wasn't clicked
3343 then revert hotkey before saving the preferences */
3344 if(hotkey_backup->accel_key != app_hotkey.accel_key ||
3345 hotkey_backup->accel_mods != app_hotkey.accel_mods ||
3346 hotkey_backup->accel_flags != app_hotkey.accel_flags)
3347 {
3348 grab_ungrab_with_ignorable_modifiers(&app_hotkey, FALSE);
3349 if(grab_ungrab_with_ignorable_modifiers(hotkey_backup, TRUE))
3350 app_hotkey = *hotkey_backup;
3351 else
3352 {
3353 show_message_dlg(NULL, MSG_HOTKEY_FAILED);
3354 memset(&app_hotkey, 0, sizeof(GtkAccelKey));
3355 }
3356 }
3357 }
3358
3359 static void show_settings_dialog(GtkToolButton *toolbutton, gpointer user_data)
3360 {
3361 GtkAccelKey hotkey_backup = app_hotkey;
3362 GtkBuilder *gui_builder = GTK_BUILDER(user_data);
3363 GtkDialog *settings_dialog = GTK_DIALOG(gtk_builder_get_object(gui_builder, DIALOG_OPTIONS));
3364 gint hotkey_dialog_response = 0;
3365
3366 // load settings from variables to check boxes
3367 /* update the old value on the controls
3368 * this is done for a scenario where the user changes the form value and presses cancel
3369 * in the next launch of the Options dialog, the old 'Cancel'ed form states shouldn't be there
3370 */
3371 set_settings_to_check_boxes(gui_builder);
3372
3373 hotkey_dialog_response = gtk_dialog_run(settings_dialog);
3374 if(GTK_RESPONSE_APPLY == hotkey_dialog_response)
3375 {
3376 apply_and_save_settings(gui_builder);
3377 }
3378 else
3379 {
3380 revert_settings(gui_builder, &hotkey_backup);
3381 }
3382 /* set hotkey flag */
3383 hotkey_set = (gboolean) app_hotkey.accel_key;
3384 }
3385
3386 static void destructor(GtkBuilder *gui_builder)
3387 {
3388 #ifdef G_OS_WIN32
3389 GtkWidget *window = GTK_WIDGET(gtk_builder_get_object(gui_builder, WINDOW_MAIN));
3390 #endif
3391
3392 if(config_file)
3393 {
3394 g_key_file_free(config_file);
3395 config_file = NULL;
3396 }
3397
3398 if(wordnet_terms)
3399 {
3400 g_string_free(wordnet_terms, TRUE);
3401 wordnet_terms = NULL;
3402 }
3403
3404 /* be responsible, give back the OS what you got from it :) */
3405 if(results)
3406 {
3407 wni_free(&results);
3408 results = NULL;
3409 }
3410
3411 /* if hotkey is registered, unregister it */
3412 if(hotkey_set)
3413 {
3414 // remove the preset event filter
3415 #ifdef X11_AVAILABLE
3416 gdk_window_remove_filter(gdk_get_default_root_window(), hotkey_pressed, gui_builder);
3417 #elif defined G_OS_WIN32
3418 gdk_window_remove_filter(window->window, hotkey_pressed, gui_builder);
3419 #endif
3420 register_unregister_hotkey(FALSE, FALSE);
3421
3422 hotkey_set = FALSE;
3423 }
3424
3425 if(mod_suggest)
3426 {
3427 suggestions_uninit();
3428 mod_suggest = FALSE;
3429 }
3430
3431 if(mod_notifier)
3432 {
3433 mod_notify_uninit();
3434 }
3435 }
3436
3437 static void show_message_dlg(GtkWidget *parent_window, MessageResposeCode msg_code)
3438 {
3439 GtkWidget *msg_dialog = NULL;
3440 GtkMessageType msg_type = GTK_MESSAGE_INFO;
3441 gchar *str_body = NULL;
3442 gchar *str_temp1 = NULL, *str_temp2 = NULL;
3443
3444 if(MSG_DB_LOAD_ERROR == msg_code)
3445 {
3446 str_body = STR_MSG_WN_ERROR;
3447 msg_type = GTK_MESSAGE_ERROR;
3448 }
3449 else if(MSG_HOTKEY_SUCCEEDED_FIRST_RUN == msg_code)
3450 {
3451 str_temp1 = gtk_accelerator_get_label(app_hotkey.accel_key, app_hotkey.accel_mods);
3452 str_temp2 = g_strdup_printf(STR_MSG_WELCOME_ARBITRARY_SUCCEEDED, str_temp1);
3453 g_free(str_temp1); str_temp1 = NULL;
3454
3455 str_body = str_temp1 = g_strdup_printf("%s\n\n%s %s",
3456 STR_MSG_WELCOME_HOTKEY_HEADER,
3457 str_temp2,
3458 STR_MSG_WELCOME_HOTKEY_FOOTER);
3459 }
3460 else if(MSG_HOTKEY_FAILED_FIRST_RUN == msg_code)
3461 {
3462 str_body = str_temp1 = g_strdup_printf("%s\n%s",
3463 STR_MSG_WELCOME_HOTKEY_HEADER,
3464 STR_MSG_WELCOME_HOTKEY_FOOTER);
3465 }
3466 else if(MSG_HOTKEY_FAILED == msg_code)
3467 {
3468 str_temp1 = gtk_accelerator_get_label(app_hotkey.accel_key, app_hotkey.accel_mods);
3469 str_temp2 = g_strdup_printf(STR_MSG_WELCOME_HOTKEY_FAILED, str_temp1);
3470 g_free(str_temp1); str_temp1 = NULL;
3471
3472 str_body = str_temp1 = g_strdup_printf("%s %s", str_temp2, STR_MSG_WELCOME_HOTKEY_FOOTER);
3473 msg_type = GTK_MESSAGE_WARNING;
3474 }
3475 else if(MSG_HOTKEY_NOTSET == msg_code)
3476 {
3477 str_body = STR_MSG_HOTKEY_NOTSET;
3478 msg_type = GTK_MESSAGE_WARNING;
3479 }
3480
3481 msg_dialog = gtk_message_dialog_new_with_markup(parent_window ? GTK_WINDOW(parent_window) : NULL,
3482 GTK_DIALOG_MODAL,
3483 msg_type,
3484 GTK_BUTTONS_OK,
3485 str_body, NULL);
3486
3487 g_object_set(msg_dialog, "title", PACKAGE_NAME, NULL);
3488 gtk_dialog_run(GTK_DIALOG(msg_dialog));
3489 gtk_widget_destroy(msg_dialog);
3490
3491 if(str_temp1)
3492 {
3493 g_free(str_temp1);
3494 str_temp1 = NULL;
3495 }
3496
3497 if(str_temp2)
3498 {
3499 g_free(str_temp2);
3500 str_temp2 = NULL;
3501 }
3502 }
3503
3504 static gboolean window_visibility_toggled(GtkWidget *widget, GdkEventVisibility *event, gpointer user_data)
3505 {
3506 /* when the window becomes fully visible, make sure a proper lookup is made
3507 if the last lookup was a notification i.e. a light lookup */
3508
3509 if(GDK_VISIBILITY_UNOBSCURED == event->state && last_lookup_a_notification)
3510 {
3511 last_search_successful = FALSE;
3512 gtk_button_clicked(GTK_BUTTON(user_data));
3513 }
3514
3515 return FALSE;
3516 }
3517
3518 int main(int argc, char *argv[])
3519 {
3520 GtkBuilder *gui_builder = NULL;
3521 GtkWidget *window = NULL, *button_search = NULL, *combo_query = NULL, *combo_entry = NULL, *settings_dialog = NULL;
3522 GtkMenu *popup_menu = NULL;
3523 GtkExpander *expander = NULL;
3524 GdkPixbuf *app_icon = NULL;
3525 GError *err = NULL;
3526 gboolean first_run = FALSE, hotkey_reg_failed = FALSE;
3527 gchar *ui_file_path = NULL, *icon_file_path = NULL;
3528
3529 #ifdef G_OS_WIN32
3530 /* logic for single instance apps. on Win32 */
3531 HANDLE single_instance_mutex = CreateMutex(NULL, FALSE, TEXT(PACKAGE_NAME));
3532
3533 if(ERROR_ALREADY_EXISTS == GetLastError() || !single_instance_mutex)
3534 {
3535 hMainWindow = FindWindow(NULL, TEXT(STR_APP_TITLE));
3536 if(hMainWindow)
3537 PostMessage(hMainWindow, WM_ARTHA_RELAUNCH, 0, 0);
3538 return 0;
3539 }
3540 #endif
3541
3542 g_set_application_name(PACKAGE_NAME);
3543
3544 if(gtk_init_check(&argc, &argv))
3545 {
3546 #ifdef DBUS_AVAILABLE
3547 /* if we're not the first instance of artha, then quit */
3548 if(FALSE == instance_handler_am_i_unique())
3549 {
3550 /* signal the primary instance of this invocation */
3551 instance_handler_send_signal();
3552
3553 /* notify the desktop env. that the start up is complete */
3554 gdk_notify_startup_complete();
3555
3556 return 0;
3557 }
3558 #endif
3559 gui_builder = gtk_builder_new();
3560 if(gui_builder)
3561 {
3562 ui_file_path = g_build_filename(APP_DIR, UI_FILE, NULL);
3563 if(gtk_builder_add_from_file(gui_builder, ui_file_path, &err))
3564 {
3565 /* if WordNet's database files are not opened, open it */
3566 if((OpenDB != 1) && (wninit() != 0))
3567 show_message_dlg(NULL, MSG_DB_LOAD_ERROR);
3568
3569 window = GTK_WIDGET(gtk_builder_get_object(gui_builder, WINDOW_MAIN));
3570 if(window)
3571 {
3572 GtkStatusIcon *status_icon = NULL;
3573 #ifdef DBUS_AVAILABLE
3574 /* if the control's here, then it's sure that we're the unique instance;
3575 register for dbus signals from possible duplicate instances */
3576 if(!instance_handler_register_signal(GTK_WINDOW(window)))
3577 {
3578 g_error("Unable to register for duplicate instance signals!\n");
3579 }
3580 #endif
3581 /* try to load preferences and see if this is the first run */
3582 first_run = load_preferences(GTK_WINDOW(window));
3583
3584 #ifdef X11_AVAILABLE
3585
3586 /* Most Important: do not use the XServer's XGetDisplay, you will have to do XNextEvent (blocking) to
3587 get the event call, so get GDK's display; its X11 display equivalent */
3588 dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default());
3589 if(NULL == dpy)
3590 {
3591 g_error("Can't open Display %s!\n", gdk_display_get_name(gdk_display_get_default()));
3592 return -1;
3593 }
3594
3595 G_DEBUG("XDisplay Opened!\n");
3596
3597 XSynchronize(dpy, True); /* Without calling this you get the error call back dealyed, much delayed! */
3598 XSetErrorHandler(x_error_handler); /* Set the error handler for setting the flag */
3599
3600 lookup_ignorable_modifiers();
3601
3602 /* Add a filter function to handle low level events, like X events.
3603 For Param1 use gdk_get_default_root_window() instead of NULL or window, so that
3604 only when hotkey combo is pressed will the filter func. be called, unlike
3605 others, where it will be called for all events beforehand GTK handles */
3606 gdk_window_add_filter(gdk_get_default_root_window(), hotkey_pressed, gui_builder);
3607
3608 #elif defined G_OS_WIN32
3609
3610 /* if the main window widget is not yet realised, then the underlying
3611 GdkWindow will be a NULL member, so realise it and get the HWND */
3612 if(!window->window)
3613 gtk_widget_realize(window);
3614
3615 hMainWindow = (HWND) GDK_WINDOW_HWND(window->window);
3616 gdk_window_add_filter(window->window, hotkey_pressed, gui_builder);
3617
3618 #endif // X11_AVAILABLE
3619
3620 if(app_hotkey.accel_key || first_run)
3621 {
3622 if(register_unregister_hotkey(first_run, TRUE))
3623 {
3624 hotkey_set = TRUE;
3625
3626 if(first_run)
3627 {
3628 show_message_dlg(window, MSG_HOTKEY_SUCCEEDED_FIRST_RUN);
3629 }
3630 }
3631 else
3632 {
3633 if(first_run)
3634 show_message_dlg(window, MSG_HOTKEY_FAILED_FIRST_RUN);
3635 else
3636 {
3637 hotkey_reg_failed = TRUE;
3638
3639 show_message_dlg(window, MSG_HOTKEY_FAILED);
3640 /* since the previously set hotkey is now not registerable, memzero
3641 (i.e. disable) app_hotkey and save prefs */
3642 app_hotkey.accel_key = app_hotkey.accel_mods = (GdkModifierType) (app_hotkey.accel_flags = 0);
3643 }
3644 }
3645 }
3646
3647 /* save preferences here - after all setting based loads are done */
3648 save_preferences();
3649
3650 setup_settings_dialog(gui_builder);
3651 settings_dialog = GTK_WIDGET(gtk_builder_get_object(gui_builder, DIALOG_OPTIONS));
3652
3653 icon_file_path = g_build_filename(ICON_DIR, ICON_FILE, NULL);
3654 if(g_file_test(icon_file_path, G_FILE_TEST_IS_REGULAR))
3655 {
3656 status_icon = GTK_STATUS_ICON(gtk_builder_get_object(gui_builder, STATUS_ICON));
3657 gtk_status_icon_set_from_file(status_icon, icon_file_path);
3658 gtk_status_icon_set_tooltip_text(status_icon, STR_APP_TITLE);
3659 gtk_status_icon_set_visible(status_icon, show_trayicon);
3660 }
3661 else
3662 {
3663 g_warning("Error loading icon file!\n%s not found!\n", icon_file_path);
3664 }
3665 g_free(icon_file_path);
3666 icon_file_path = NULL;
3667
3668 mod_notify_init();
3669
3670 setup_toolbar(gui_builder);
3671
3672 /* pop-up menu creation should be after assessing the availability of notifications
3673 since if it is not available, the Notify menu option can be stripped
3674 create pop-up menu */
3675 popup_menu = create_popup_menu(gui_builder);
3676
3677 /* status icon connections made here since popup menu should be ready for this
3678 * which wouldn't be ready when status_icon is inited */
3679 if(status_icon)
3680 {
3681 g_signal_connect(status_icon, "activate", G_CALLBACK(status_icon_activate), gui_builder);
3682 g_signal_connect(status_icon, "popup-menu", G_CALLBACK(status_icon_popup), GTK_WIDGET(popup_menu));
3683 }
3684
3685
3686 // using the status icon, app. icon is also set
3687 g_object_get(status_icon, "pixbuf", &app_icon, NULL);
3688 if(app_icon)
3689 {
3690 gtk_window_set_default_icon(app_icon);
3691 g_object_unref(app_icon);
3692 app_icon = NULL;
3693 }
3694
3695
3696 button_search = GTK_WIDGET(gtk_builder_get_object(gui_builder, BUTTON_SEARCH));
3697 g_signal_connect(button_search, "clicked", G_CALLBACK(button_search_click), gui_builder);
3698
3699 combo_query = GTK_WIDGET(gtk_builder_get_object(gui_builder, COMBO_QUERY));
3700 g_signal_connect(combo_query, "changed", G_CALLBACK(combo_query_changed), gui_builder);
3701
3702 /* get the GtkEntry in GtkComboBox and set activates-default to TRUE; so that
3703 it pass the ENTER key signal to query button */
3704 combo_entry = gtk_bin_get_child(GTK_BIN(combo_query));
3705 g_object_set(combo_entry, "activates-default", TRUE, NULL);
3706
3707 create_text_view_tags(gui_builder);
3708
3709 /* do main window specific connects */
3710 g_signal_connect(window, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
3711 g_signal_connect(window, "visibility-notify-event", G_CALLBACK(window_visibility_toggled), button_search);
3712
3713 g_signal_connect(combo_query, "scroll-event", G_CALLBACK(combo_query_scroll), button_search);
3714
3715 expander = GTK_EXPANDER(gtk_builder_get_object(gui_builder, EXPANDER));
3716 g_signal_connect(expander, "activate", G_CALLBACK(expander_clicked), gui_builder);
3717
3718 gtk_widget_grab_focus(GTK_WIDGET(combo_query));
3719
3720 G_DEBUG("GUI loaded successfully!\n");
3721
3722 create_stores_renderers(gui_builder);
3723
3724 mod_suggest = suggestions_init();
3725
3726 // show the window if it's a first run or a hotkey couldn't be set
3727 // if the window is not shown, set notify the startup is complete
3728 if(first_run || hotkey_reg_failed || !launch_minimized)
3729 gtk_widget_show_all(window);
3730 else
3731 gdk_notify_startup_complete();
3732
3733 // index all wordnet terms from the index.sense onto memory
3734 WordnetTermsLoaderData loader_data = { gui_builder };
3735 wordnet_terms_load(&loader_data);
3736
3737 gtk_main();
3738
3739 /* on Win32 platform, icon stays even after app close, this is
3740 a workaround to fix that */
3741 gtk_status_icon_set_visible(status_icon, FALSE);
3742 destructor(gui_builder);
3743
3744 /* GtkBuilder drops references to any held, except toplevel widgets */
3745 gtk_widget_destroy(GTK_WIDGET(popup_menu));
3746 gtk_widget_destroy(settings_dialog);
3747 gtk_widget_destroy(window);
3748 }
3749 else
3750 {
3751 g_error("Error loading GUI!\n Corrupted data in UI file!\n");
3752 }
3753 }
3754 else
3755 {
3756 /* shouldn't call g_error directly, since it's a terminating call;
3757 so print the error, free the resources and then do it */
3758 g_print("%s: ", err->message);
3759 g_error_free(err);
3760 err = NULL;
3761 g_error("Error loading GUI from %s!\n", ui_file_path);
3762 }
3763
3764 g_free(ui_file_path);
3765 ui_file_path = NULL;
3766 }
3767 else
3768 {
3769 g_error("Error creating GtkBuilder!\n");
3770 }
3771 }
3772 else
3773 {
3774 g_error("Error initializing GUI!\n");
3775 }
3776
3777 #ifdef G_OS_WIN32
3778 CloseHandle(single_instance_mutex);
3779 #endif
3780
3781 return 0;
3782 }
3783
3784