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", &notifier_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