/* gui.c
* Artha - Free cross-platform open thesaurus
* Copyright (c) 2009 Sundaram Ramaswamy, legends2k@yahoo.com
*
* Artha is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Artha is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Artha; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* GUI Code
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gui.h"
/* Global variables */
static const gchar *strv_authors[] = {"Sundaram Ramaswamy <legends2k@yahoo.com>", NULL};
/* Names of relative tree tab widgets from UI file
Note that the 'tree" prefix will be stripped and will be used within code */
static const gchar *relative_tree[] = {"treeSynonyms", "treeAntonyms", "treeDerivatives", "treePertainyms", "treeAttributes", "treeSimilar",
"treeDomain", "treeCauses", "treeEntails", "treeHypernyms", "treeHyponyms", "treeHolonyms", "treeMeronyms"};
#define DOMAINS_COUNT (CLASS_END - CLASSIF_START + 1)
static const gchar *domain_types[] = {"Topic", "Usage", "Region", "Topic Terms", "Usage Terms", "Regional Terms"};
#define HOLO_MERO_COUNT 3
static const gchar *holo_mero_types[][3] = {{"Member Of", "Substance Of", "Part Of"}, {"Has Members", "Has Substances", "Has Parts"}};
#define FAMILIARITY_COUNT 8
static const gchar *familiarity[] = {"extremely rare","very rare","rare","uncommon","common", "familiar","very familiar","extremely familiar"};
static const gchar *freq_colors[] = {"Black", "SaddleBrown", "FireBrick", "SeaGreen", "DarkOrange", "gold", "PaleGoldenrod", "PeachPuff1"};
/* words for checking familiarity types - none, scroll (v), scroll (n), alright, sequence, set (n), set (v), give */
/* notifier_enabled is for the setting "Notify" and *notifier is for the module availability */
static GSList *results = NULL;
static gboolean was_double_click = FALSE, last_search_successful = FALSE, advanced_mode = FALSE, auto_contract = FALSE;
static gboolean hotkey_set = FALSE, mod_suggest = FALSE;
// options which default to true
static gboolean notifier_enabled = TRUE, show_polysemy = TRUE, launch_minimized = TRUE, show_trayicon = TRUE;
static gboolean last_lookup_a_notification = FALSE;
static gchar last_search[MAX_LEMMA_LEN] = "";
#ifdef X11_AVAILABLE
static Display *dpy = NULL;
gboolean hotkey_processing = FALSE;
guint32 last_hotkey_time = 0;
static guint num_lock_mask = 0, caps_lock_mask = 0, scroll_lock_mask = 0;
#endif
static guint hotkey_trials[] = {GDK_w, GDK_a, GDK_t, GDK_q};
GtkAccelKey app_hotkey = {0};
static gint notify_toolbar_index = -1;
static guint status_msg_context_id = 0;
static GString *wordnet_terms = NULL;
NotifyNotification *mod_notifier = NULL;
static GtkCheckMenuItem *menu_notify = NULL;
static GKeyFile *config_file = NULL;
#ifdef G_OS_WIN32
static HWND hMainWindow = NULL;
#endif
/* function declarations */
#ifdef X11_AVAILABLE
static int x_error_handler(Display *dpy, XErrorEvent *xevent);
#endif // X11_AVAILABLE
static void show_window(GtkWindow *window);
static void notification_toggled(GObject *obj, gpointer user_data);
static void mode_toggled(GtkToggleToolButton *toggle_button, gpointer user_data);
static void trayicon_menu_toggled(GtkMenuItem *menu, GtkBuilder *gui_builder);
static gchar* strip_invalid_edges(gchar *selection);
static gchar* strip_non_alpha_num(const gchar *str);
static gboolean are_same_after_strip(const gchar *strA, const gchar *strB);
#ifdef G_OS_WIN32
static inline gboolean is_alt_pressed();
static inline void print_last_error();
static gchar* win32_capture_selection();
static gboolean link_launch(GtkAboutDialog *about_dialog, gchar *uri, gpointer user_data);
#endif
static void notification_lookup(gchar *selection, GtkComboBox *combo_query);
static GdkFilterReturn hotkey_pressed(GdkXEvent *xevent, GdkEvent *event, gpointer user_data);
static void status_icon_activate(GtkStatusIcon *status_icon, gpointer user_data);
static void status_icon_popup(GtkStatusIcon *status_icon, guint button, guint active_time, gpointer user_data);
static void about_response_handle(GtkDialog *about_dialog, gint response_id, gpointer user_data);
static void about_activate(GtkToolButton *menu_item, gpointer user_data);
static void quit_activate(GtkWidget *widget, gpointer user_data);
static guint8 get_frequency(guint sense_count);
static void relatives_clear_all(GtkBuilder *gui_builder);
static void antonyms_load(GSList *antonyms, GtkBuilder *gui_builder);
static guint16 append_term(gchar *target, gchar *term, gboolean is_heading);
static void build_tree(GNode *node, GtkTreeStore *tree_store, GtkTreeIter *parent_iter);
static void add_nodes(GNode *tree, GtkTreeStore *tree_store, GtkTreeIter *node_parent, GSList **dupe_finder_list);
static void trees_load(GSList *properties, GtkBuilder *gui_builder, WNIRequestFlags id);
static void holo_mero_load(GSList *properties, GtkBuilder *gui_builder, WNIRequestFlags id);
static void list_relatives_load(GSList *properties, GtkBuilder *gui_builder, WNIRequestFlags id);
static void domains_load(GSList *properties, GtkBuilder *gui_builder);
static guint8 get_attribute_pos();
static void relatives_load(GtkBuilder *gui_builder, gboolean reset_tabs);
static gchar* wildmat_to_regex(const gchar *wildmat);
static gboolean set_regex_results(const gchar *wildmat_exp, GtkBuilder *gui_builder);
static gboolean is_wildmat_expr();
static gboolean handle_wildmat_expr(GtkBuilder *gui_builder, GtkTextBuffer *buffer);
static gboolean is_entry_unique(GtkTreeModel *query_list_store, GtkTreePath *path, GtkTreeIter *iter, gchar **lemma);
static gboolean update_history(GtkComboBox *combo_query, char *lemma);
static gboolean update_history_and_save(GtkComboBox *combo_query, char *lemma);
static void show_definitions(GtkBuilder *gui_builder, GtkTextBuffer *buffer);
static void show_suggestions(GtkBuilder *gui_builder, GtkTextBuffer *buffer, GtkTextIter *cur, GSList *suggestions);
static void construct_show_notification();
static void show_searching(GtkBuilder *gui_builder);
static void query_wni(gchar *search_str,
WNIRequestFlags lookup_type,
GSList **suggestions);
static void button_search_click(GtkButton *button, gpointer user_data);
static void combo_query_changed(GtkComboBox *combo_query, gpointer user_data);
static gboolean text_view_button_pressed(GtkWidget *widget, GdkEventButton *event, gpointer user_data);
static gboolean text_view_button_released(GtkWidget *widget, GdkEventButton *event, gpointer user_data);
static void text_view_selection_made(GtkWidget *widget, GtkSelectionData *sel_data, guint time, gpointer user_data);
static void expander_clicked(GtkExpander *expander, gpointer user_data);
static void query_list_updated(GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter, gpointer user_data);
static GtkTextMark *highlight_definition(guint8 id, guint16 sense, GtkTextView *text_view);
static void highlight_senses_from_synonyms(guint16 relative_index, GtkTextView *text_view);
static void highlight_senses_from_antonyms( guint8 category, guint16 relative_index, guint16 sub_level, GtkTextView *text_view);
static void highlight_senses_from_domain(guint8 category, guint16 relative_index, GtkTextView *text_view);
static void highlight_senses_from_relative_lists(WNIRequestFlags id, guint16 relative_index, GtkTextView *text_view);
static void relative_selection_changed(GtkTreeView *tree_view, gpointer user_data);
static void relative_row_activated(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data);
static void clear_history(GtkMenuItem *menu_item, GtkListStore *list_store_query);
static void save_history_to_file(GtkMenuItem *menu_item, gpointer user_data);
static void query_combo_popup(GtkEntry *query_entry_widget, GtkMenu *popup_menu, GtkListStore *list_store_query);
static void load_history(GtkListStore *list_store_query);
static void create_stores_renderers(GtkBuilder *gui_builder);
static void button_next_clicked(GtkToolButton *toolbutton, gpointer user_data);
static void button_prev_clicked(GtkToolButton *toolbutton, gpointer user_data);
static gboolean combo_query_scroll(GtkWidget *widget, GdkEventScroll *event, gpointer user_data);
static gboolean close_window(GtkAccelGroup *accel_group, GObject *acceleratable, guint keyval,
GdkModifierType modifier, GObject *user_data);
static void setup_toolbar(GtkBuilder *gui_builder);
static GtkMenu *create_popup_menu(GtkBuilder *gui_builder);
static void create_text_view_tags(GtkBuilder *gui_builder);
static void load_preference_hotkey(gboolean *first_run);
static gboolean load_preferences(GtkWindow *parent);
static gboolean update_history_in_file(const gchar *term);
static void save_preferences_to_file();
static void save_preferences();
static gboolean autocomplete_selected(GtkEntryCompletion *query_completion, GtkTreeModel *model, GtkTreeIter *iter, GtkButton *button);
static void show_loading(GtkBuilder *gui_builder);
typedef struct
{
GtkBuilder *gui_builder;
GtkListStore *completion_list;
gchar *index_file_contents;
gsize index_file_length;
gsize pos_in_file;
gchar *last_lemma;
}
WordnetTermsLoaderData;
static void attach_loaded_terms(WordnetTermsLoaderData *loader_data);
static gboolean terms_loader(WordnetTermsLoaderData *loader_data);
static gboolean wordnet_terms_load(WordnetTermsLoaderData *loader_data);
#ifdef X11_AVAILABLE
static void lookup_ignorable_modifiers(void);
#elif defined G_OS_WIN32
#ifndef MOD_NOREPEAT
#define MOD_NOREPEAT 0x4000
#endif
typedef struct
{
guint gdk_key;
BYTE vk_key;
} gdk_vk;
static gboolean try_convert_to_vk(guint gdk_key, BYTE *vk);
#endif //X11_AVAILABLE
gboolean grab_ungrab_with_ignorable_modifiers(GtkAccelKey *binding, gboolean grab);
static gboolean register_unregister_hotkey(gboolean first_run, gboolean setup_hotkey);
static void setup_settings_dialog(GtkBuilder *gui_builder);
static void set_settings_to_check_boxes(GtkBuilder *gui_builder);
static void get_settings_from_check_boxes(GtkBuilder *gui_builder,
gboolean *new_trayicon_show,
gboolean *new_launch_min,
gboolean *new_polysemy_show);
static void apply_and_save_settings(GtkBuilder *gui_builder);
static void revert_settings(GtkBuilder *gui_builder, GtkAccelKey *hotkey_backup);
static void show_settings_dialog(GtkToolButton *toolbutton, gpointer user_data);
static void destructor(GtkBuilder *gui_builder);
static void show_message_dlg(GtkWidget *parent_window, MessageResposeCode msg_code);
static gboolean window_visibility_toggled(GtkWidget *widget, GdkEventVisibility *event, gpointer user_data);
#ifdef X11_AVAILABLE
static int x_error_handler(Display *dpy, XErrorEvent *xevent)
{
if(BadAccess == xevent->error_code)
{
g_warning("Hotkey combo already occupied!\n");
}
else
g_error("X Server Error: %d\n", xevent->error_code);
return -1;
}
#endif // X11_AVAILABLE
static void show_window(GtkWindow *window)
{
#ifdef X11_AVAILABLE
tomboy_window_present_hardcore(window);
#else
gtk_window_present(window);
#endif
}
/*
This will be called for both notify check pop-up menu and notify tool bar button
Hence the first argument is GObject. Since both have the propery "active" as its
current state, this common function will suffice for notification toggling.
*/
static void notification_toggled(GObject *obj, gpointer user_data)
{
GtkBuilder *gui_builder = GTK_BUILDER(user_data);
GtkToolbar *toolbar = GTK_TOOLBAR(gtk_builder_get_object(gui_builder, TOOLBAR));
GtkToolItem *toolbar_notify = gtk_toolbar_get_nth_item(toolbar, notify_toolbar_index);
gboolean prev_state_of_notification = notifier_enabled; // flag to prevent showing hotkey-not-set alert twice
g_object_get(obj, "active", ¬ifier_enabled, NULL);
g_object_set(G_OBJECT(toolbar_notify), "active", notifier_enabled, NULL);
gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(toolbar_notify), notifier_enabled ? GTK_STOCK_YES : GTK_STOCK_NO);
gtk_check_menu_item_set_active(menu_notify, notifier_enabled);
save_preferences();
/* if not hotkey is set; then there's no point in enabling notifications
intimate this to the user */
if(!hotkey_set && notifier_enabled && prev_state_of_notification != notifier_enabled)
show_message_dlg(NULL, MSG_HOTKEY_NOTSET);
}
static void mode_toggled(GtkToggleToolButton *toggle_button, gpointer user_data)
{
GtkBuilder *gui_builder = GTK_BUILDER(user_data);
advanced_mode = gtk_toggle_tool_button_get_active(toggle_button);
if(last_search_successful)
{
G_MESSAGE("Re-requesting with changed mode: %d\n", advanced_mode);
wni_request_nyms(last_search, &results, WORDNET_INTERFACE_ALL, advanced_mode);
relatives_clear_all(gui_builder);
relatives_load(gui_builder, FALSE);
}
save_preferences();
}
static void trayicon_menu_toggled(GtkMenuItem *menu, GtkBuilder *gui_builder)
{
GtkStatusIcon *status_icon = GTK_STATUS_ICON(gtk_builder_get_object(gui_builder, STATUS_ICON));
show_trayicon = FALSE;
gtk_status_icon_set_visible(status_icon, show_trayicon);
save_preferences();
}
static gchar* strip_invalid_edges(gchar *selection)
{
guint i = 0;
gboolean alphanum_encounterd = FALSE;
if(selection)
{
while(selection[i] != '\0')
{
if(!g_ascii_isalnum(selection[i]) && selection[i] != '-' && selection[i] != '_' &&
selection[i] != ' ' && selection[i] != '\'' && selection[i] != '.')
{
if(alphanum_encounterd)
{
selection[i] = '\0';
break;
}
else
{
selection[i] = ' ';
}
}
else
{
// before setting the alphanum_encountered, make sure the current char isn't other valid ones
if(!alphanum_encounterd && selection[i] != '-' && selection[i] != '_' && selection[i] != ' ' &&
selection[i] != '\'' && selection[i] != '.')
alphanum_encounterd = TRUE;
}
i++;
}
}
return selection;
}
static gchar* strip_non_alpha_num(const gchar *str)
{
gchar *stripped_str = g_strstrip(g_strdup(str));
guint16 i = 0, j = 0;
gchar ch;
while((ch = str[i++]))
{
if(g_ascii_isalnum(ch))
stripped_str[j++] = ch;
}
stripped_str[j] = '\0';
return stripped_str;
}
static gboolean are_same_after_strip(const gchar *strA, const gchar *strB)
{
gchar *strAStripped = strip_non_alpha_num(strA);
gchar *strBStripped = strip_non_alpha_num(strB);
// NOT op since 0 means strings match as per strcmp
gboolean matches = !g_strcmp0(strAStripped, strBStripped);
g_free(strAStripped); strAStripped = NULL;
g_free(strBStripped); strBStripped = NULL;
return matches;
}
#ifdef G_OS_WIN32
static inline gboolean is_alt_pressed()
{
return (0 != (0x8000 & GetAsyncKeyState(VK_MENU)));
}
static inline void print_last_error()
{
g_warning("Error getting selection (Win32): %ld", GetLastError());
}
static gchar* win32_capture_selection()
{
GUITHREADINFO guiThread = {sizeof(GUITHREADINFO)};
gchar *selection = NULL;
INPUT input[KEY_COUNT] = {{INPUT_KEYBOARD}, {INPUT_KEYBOARD}, {INPUT_KEYBOARD}, {INPUT_KEYBOARD}};
const WORD inputKey[2] = {VK_CONTROL, VK_INSERT};
HGLOBAL hglb = NULL;
LPSTR charArrayFromClipboard = NULL;
if(GetGUIThreadInfo(0, &guiThread))
{
if(is_alt_pressed())
{
input[0].ki.wVk = VK_MENU;
input[0].ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(1, input, sizeof(INPUT));
input[0].ki.dwFlags = 0;
G_MESSAGE("Virtually released ALT!\n");
}
input[0].ki.wVk = input[2].ki.wVk = inputKey[0];
input[1].ki.wVk = input[3].ki.wVk = inputKey[1];
input[2].ki.dwFlags = (input[3].ki.dwFlags |= KEYEVENTF_KEYUP);
SendInput(KEY_COUNT, input, sizeof(INPUT));
Sleep(200);
if(OpenClipboard(NULL))
{
if((hglb = GetClipboardData(CF_TEXT)))
{
if((charArrayFromClipboard = (LPSTR) GlobalLock(hglb)))
{
selection = g_strdup(charArrayFromClipboard);
GlobalUnlock(hglb);
}
else
print_last_error();
}
else
print_last_error();
CloseClipboard();
}
else
print_last_error();
}
else
print_last_error();
return selection;
}
static gboolean link_launch(GtkAboutDialog *about_dialog, gchar *uri, gpointer user_data)
{
return ((int)ShellExecute(hMainWindow, "open", uri, NULL, NULL, SW_SHOWNORMAL) > 32);
}
#endif
static void notification_lookup(gchar *selection, GtkComboBox *combo_query)
{
gchar *stripped_lookup = g_strstrip(strip_invalid_edges(selection));
// make sure this isn't a zero length string; valid but zero length
if(stripped_lookup[0])
{
gboolean new_search = TRUE;
if(last_search_successful)
new_search = (g_ascii_strcasecmp(last_search, stripped_lookup) != 0);
if(new_search)
query_wni(stripped_lookup, WORDNET_INTERFACE_NONE, NULL);
if(results)
{
construct_show_notification();
gchar *lemma = ((WNIDefinitionItem*)((WNIOverview*)((WNINym*)results->data)->data)->definitions_list->data)->lemma;
update_history_and_save(combo_query, lemma);
g_stpcpy(last_search, stripped_lookup);
last_search_successful = TRUE;
}
else
{
gboolean update_successful = notify_notification_update(mod_notifier,
stripped_lookup,
STR_STATUS_QUERY_FAILED,
"gtk-dialog-warning");
if(!update_successful || (FALSE == notify_notification_show(mod_notifier, NULL)))
{
g_warning("Failed to display notification!\n");
}
last_search_successful = FALSE;
}
}
last_lookup_a_notification = TRUE;
}
static GdkFilterReturn hotkey_pressed(GdkXEvent *xevent, GdkEvent *event, gpointer user_data)
{
gchar *selection = NULL;
GtkBuilder *gui_builder = GTK_BUILDER(user_data);
GtkWindow *window = GTK_WINDOW(gtk_builder_get_object(gui_builder, WINDOW_MAIN));
#ifdef X11_AVAILABLE
XEvent *xe = (XEvent*) xevent;
// 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
// Try this by removing below if and try to print debug text
if(xe->type == KeyPress)
{
#elif defined G_OS_WIN32
PMSG msg = (PMSG) xevent;
if(WM_HOTKEY == msg->message)
{
#endif
GtkComboBox *combo_query = GTK_COMBO_BOX(gtk_builder_get_object(gui_builder, COMBO_QUERY));
#ifdef X11_AVAILABLE
hotkey_processing = TRUE;
last_hotkey_time = xe->xkey.time;
// get the clipboard text and strip off any invalid characters
selection = gtk_clipboard_wait_for_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY));
//strip_invalid_edges(selection);
#elif defined G_OS_WIN32
selection = win32_capture_selection();
#endif
if(selection)
{
// setting the text is needed for both notification and normal lookup
// since on unobscuring the window, a proper lookup needs to be done
GtkEntry *text_entry_query = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo_query)));
gtk_entry_set_text(text_entry_query, selection);
//gtk_editable_set_position(GTK_EDITABLE(text_entry_query), -1);
if(gtk_widget_get_visible(GTK_WIDGET(window)) || !notifier_enabled)
{
GtkButton *button_search = GTK_BUTTON(gtk_builder_get_object(gui_builder, BUTTON_SEARCH));
gtk_button_clicked(button_search);
}
// if notifier_enabled and notifier mod is present
else if(mod_notifier)
{
notification_lookup(selection, combo_query);
}
g_free(selection);
selection = NULL;
}
else
{
// see if in case notify is selected, should we popup or we should just notify "No selection made!"
show_window(window);
gtk_widget_grab_focus(GTK_WIDGET(combo_query));
}
#ifdef X11_AVAILABLE
hotkey_processing = FALSE;
#endif
return GDK_FILTER_REMOVE;
}
#ifdef G_OS_WIN32
else if(WM_ARTHA_RELAUNCH == msg->message)
{
show_window(window);
return GDK_FILTER_REMOVE;
}
#endif
return GDK_FILTER_CONTINUE;
}
static void status_icon_activate(GtkStatusIcon *status_icon, gpointer user_data)
{
GtkBuilder *gui_builder = GTK_BUILDER(user_data);
GtkWindow *window = GTK_WINDOW(gtk_builder_get_object(gui_builder, WINDOW_MAIN));
GtkComboBox *combo_query = NULL;
if(gtk_widget_get_visible(GTK_WIDGET(window)))
gtk_widget_hide(GTK_WIDGET(window));
else
{
/* close notifications, if any */
if(mod_notifier)
notify_notification_close(mod_notifier, NULL);
show_window(window);
combo_query = GTK_COMBO_BOX(gtk_builder_get_object(gui_builder, COMBO_QUERY));
gtk_widget_grab_focus(GTK_WIDGET(combo_query));
}
}
static void status_icon_popup(GtkStatusIcon *status_icon, guint button, guint active_time, gpointer user_data)
{
GtkMenu *menu = GTK_MENU(user_data);
gtk_menu_popup(menu, NULL, NULL, gtk_status_icon_position_menu, status_icon, button, active_time);
}
static void about_response_handle(GtkDialog *about_dialog, gint response_id, gpointer user_data)
{
switch(response_id)
{
case ARTHA_RESPONSE_REPORT_BUG:
{
#ifndef G_OS_WIN32
GError *err = NULL;
if(!gtk_show_uri(NULL, STR_BUG_WEBPAGE, GDK_CURRENT_TIME, &err))
{
GtkDialog *err_msg = GTK_DIALOG(gtk_message_dialog_new(
GTK_WINDOW(about_dialog),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_CLOSE,
"Error opening %s\n\n%s",
STR_BUG_WEBPAGE,
err->message));
gtk_dialog_run(err_msg);
gtk_widget_destroy(GTK_WIDGET(err_msg));
g_clear_error(&err);
}
#else
link_launch(GTK_ABOUT_DIALOG(about_dialog), STR_BUG_WEBPAGE, NULL);
#endif
break;
}
default:
g_warning("About Dialog: Unhandled response_id: %d!\n", response_id);
break;
}
gtk_widget_destroy(GTK_WIDGET(about_dialog));
}
static void about_activate(GtkToolButton *menu_item, gpointer user_data)
{
GtkWidget *about_dialog = gtk_about_dialog_new();
g_object_set(G_OBJECT(about_dialog), "license", STR_LICENCE, "copyright", STR_COPYRIGHT,
"comments", STR_ABOUT, "authors", strv_authors, "version", PACKAGE_VERSION,
"wrap-license", TRUE,
"website-label", STR_WEBSITE_LABEL,
"website", PACKAGE_URL, NULL);
gtk_dialog_add_button(GTK_DIALOG(about_dialog), STR_REPORT_BUG, ARTHA_RESPONSE_REPORT_BUG);
g_signal_connect(about_dialog, "response", G_CALLBACK(about_response_handle), NULL);
#ifdef G_OS_WIN32
g_signal_connect(about_dialog, "activate-link", G_CALLBACK(link_launch), NULL);
#endif
gtk_dialog_run(GTK_DIALOG(about_dialog));
}
static void quit_activate(GtkWidget *widget, gpointer user_data)
{
G_DEBUG("Destroy called!\n");
gtk_main_quit();
}
static guint8 get_frequency(guint sense_count)
{
guint8 frequency = 0;
if(sense_count == 0) frequency = 0;
if(sense_count == 1) frequency = 1;
if(sense_count == 2) frequency = 2;
if(sense_count >= 3 && sense_count <= 4) frequency = 3;
if(sense_count >= 5 && sense_count <= 8) frequency = 4;
if(sense_count >= 9 && sense_count <= 16) frequency = 5;
if(sense_count >= 17 && sense_count <= 32) frequency = 6;
if(sense_count > 32 ) frequency = 7;
return frequency;
}
static void relatives_clear_all(GtkBuilder *gui_builder)
{
guint8 i = 0;
GtkTreeView *temp_tree_view = NULL;
GtkTreeStore *temp_tree_store = NULL;
GtkWidget *relative_tab = NULL;
for(i = 0; i < TOTAL_RELATIVES; i++)
{
temp_tree_view = GTK_TREE_VIEW(gtk_builder_get_object(gui_builder, relative_tree[i]));
relative_tab = gtk_widget_get_parent(GTK_WIDGET(temp_tree_view));
if(gtk_widget_get_visible(GTK_WIDGET(relative_tab)))
{
temp_tree_store = GTK_TREE_STORE(gtk_tree_view_get_model(temp_tree_view));
if(temp_tree_store)
gtk_tree_store_clear(temp_tree_store);
}
else
gtk_widget_show(relative_tab);
}
}
static void antonyms_load(GSList *antonyms, GtkBuilder *gui_builder)
{
WNIAntonymItem *temp_antonym_item = NULL;
GSList *implications = NULL;
GtkTreeView *tree_antonyms = GTK_TREE_VIEW(gtk_builder_get_object(gui_builder, relative_tree[TREE_ANTONYMS]));
GtkTreeStore *antonyms_tree_store = GTK_TREE_STORE(gtk_tree_view_get_model(tree_antonyms));
GtkTreeIter iter = {0}, sub_iter = {0}, direct_iter = {0}, indirect_iter = {0};
GtkTreePath *expand_path = NULL;
gboolean direct_deleted = FALSE, has_indirect = FALSE;
g_assert(antonyms_tree_store);
if(advanced_mode)
{
gtk_tree_store_append(antonyms_tree_store, &direct_iter, NULL);
gtk_tree_store_set(antonyms_tree_store, &direct_iter, 0, STR_ANTONYM_HEADER_DIRECT, 1, PANGO_WEIGHT_SEMIBOLD, 2, NULL, -1);
gtk_tree_store_append(antonyms_tree_store, &indirect_iter, NULL);
gtk_tree_store_set(antonyms_tree_store, &indirect_iter, 0, STR_ANTONYM_HEADER_INDIRECT, 1, PANGO_WEIGHT_SEMIBOLD, 2, NULL, -1);
}
while(antonyms)
{
temp_antonym_item = antonyms->data;
if(temp_antonym_item)
{
if(DIRECT_ANT == temp_antonym_item->relation)
{
if(advanced_mode)
gtk_tree_store_append(antonyms_tree_store, &iter, &direct_iter);
else
gtk_tree_store_append(antonyms_tree_store, &iter, NULL);
gtk_tree_store_set(antonyms_tree_store, &iter, 0, temp_antonym_item->term, 1, PANGO_WEIGHT_NORMAL, 2, NULL, -1);
}
else
{
iter = indirect_iter;
has_indirect = TRUE;
}
implications = temp_antonym_item->implications;
while(implications)
{
gtk_tree_store_append(antonyms_tree_store, &sub_iter, &iter);
gtk_tree_store_set(antonyms_tree_store, &sub_iter, 0, ((WNIImplication*)implications->data)->term,
1, PANGO_WEIGHT_NORMAL, 2, (DIRECT_ANT == temp_antonym_item->relation)?NULL:temp_antonym_item->term, -1);
implications = g_slist_next(implications);
}
}
antonyms = g_slist_next(antonyms);
}
if(advanced_mode)
{
// if only Direct is present, then Indirect will be deleted, only indirect_iter becomes invalid
// if only Indirect is present, then Direct will be deleted, and the indirect delete won't work, because the indirect_iter will get invalid
// if both are present, none deleted, no iters become invalid
// if both are not present, we won't be here in the first place ;)
// 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
if((direct_deleted = !gtk_tree_model_iter_has_child(GTK_TREE_MODEL(antonyms_tree_store), &direct_iter)))
gtk_tree_store_remove(antonyms_tree_store, &direct_iter);
if(!gtk_tree_model_iter_has_child(GTK_TREE_MODEL(antonyms_tree_store), &indirect_iter))
gtk_tree_store_remove(antonyms_tree_store, &indirect_iter);
// expand one level
expand_path = gtk_tree_path_new_from_indices(0, -1);
// if direct list was deleted, then this is the indirect list that we are dealing, so expand, else don't
gtk_tree_view_expand_row(tree_antonyms, expand_path, direct_deleted);
gtk_tree_path_free(expand_path);
// if there is a list in this path "1", then it should be indirect
expand_path = gtk_tree_path_new_from_indices(1, -1);
gtk_tree_view_expand_row(tree_antonyms, expand_path, TRUE);
gtk_tree_path_free(expand_path);
expand_path = NULL;
}
gtk_tree_view_set_headers_visible(tree_antonyms, has_indirect);
}
static guint16 append_term(gchar *target, gchar *term, gboolean is_heading)
{
guint16 i = 0;
while(term[i] != '\0')
{
*target = term[i++];
target++;
}
*target = (is_heading)? ':' : ',';
++target;
*target = ' ';
return (i + 2);
}
static void build_tree(GNode *node, GtkTreeStore *tree_store, GtkTreeIter *parent_iter)
{
GNode *child = NULL;
GSList *temp_list = NULL;
WNIImplication *imp = NULL;
GtkTreeIter tree_iter = {0};
guint32 i = 0;
gchar terms[MAX_CONCAT_STR] = "";
if((child = g_node_first_child(node)))
do
{
i = 0;
terms[0] = '\0';
temp_list = ((WNITreeList*)child->data)->word_list;
while(temp_list)
{
imp = (WNIImplication*) temp_list->data;
i += append_term(&terms[i], imp->term, FALSE);
temp_list = g_slist_next(temp_list);
}
terms[i - 2] = '\0';
gtk_tree_store_append(tree_store, &tree_iter, parent_iter);
gtk_tree_store_set(tree_store, &tree_iter, 0, terms, 1, PANGO_WEIGHT_NORMAL, -1);
build_tree(child, tree_store, &tree_iter);
} while((child = g_node_next_sibling(child)));
}
static void add_nodes(GNode *tree, GtkTreeStore *tree_store, GtkTreeIter *node_parent, GSList **dupe_finder_list)
{
WNIImplication *imp = NULL;
gchar terms[MAX_CONCAT_STR] = "";
GtkTreeIter iter = {0};
gboolean is_dupe = FALSE;
guint16 i = 0;
GSList *term_list = ((WNITreeList*)tree->data)->word_list;
if(advanced_mode)
{
while(term_list)
{
imp = (WNIImplication*) term_list->data;
i += append_term(&terms[i], imp->term, FALSE);
term_list = g_slist_next(term_list);
}
terms[i - 2] = '\0';
gtk_tree_store_append(tree_store, &iter, node_parent);
gtk_tree_store_set(tree_store, &iter, 0, terms, 1, PANGO_WEIGHT_NORMAL, -1);
build_tree(tree, tree_store, &iter);
}
else
{
while(term_list)
{
imp = (WNIImplication*) term_list->data;
is_dupe = FALSE;
/* if dupe check was asked by the caller, then check else continue with append */
if(NULL != dupe_finder_list)
{
if(NULL == g_slist_find_custom(*dupe_finder_list, imp->term, (GCompareFunc) &g_strcmp0))
*dupe_finder_list = g_slist_append(*dupe_finder_list, imp->term);
else
is_dupe = TRUE;
}
if(!is_dupe)
{
gtk_tree_store_append(tree_store, &iter, node_parent);
gtk_tree_store_set(tree_store, &iter, 0, imp->term, 1, PANGO_WEIGHT_NORMAL, -1);
}
term_list = g_slist_next(term_list);
}
}
}
static void trees_load(GSList *properties, GtkBuilder *gui_builder, WNIRequestFlags id)
{
GNode *tree = NULL;
GtkTreeView *tree_view = NULL;
GtkTreeStore *tree_store = NULL;
GSList *dupe_finder_list = NULL;
guint8 i = (WORDNET_INTERFACE_HYPERNYMS == id) ? TREE_HYPERNYMS : ((WORDNET_INTERFACE_HYPONYMS == id) ? TREE_HYPONYMS : TREE_PERTAINYMS);
tree_view = GTK_TREE_VIEW(gtk_builder_get_object(gui_builder, relative_tree[i]));
tree_store = GTK_TREE_STORE(gtk_tree_view_get_model(tree_view));
g_assert(tree_store);
while(properties)
{
if(properties->data)
tree = g_node_first_child((GNode*)properties->data);
while(tree)
{
add_nodes(tree, tree_store, NULL, &dupe_finder_list);
tree = g_node_next_sibling(tree);
}
properties = g_slist_next(properties);
}
g_slist_free(dupe_finder_list);
dupe_finder_list = NULL;
}
static void holo_mero_load(GSList *properties, GtkBuilder *gui_builder, WNIRequestFlags id)
{
GNode *tree = NULL;
GtkTreeIter type_iter[HOLO_MERO_COUNT] = {{0}}, *relative_parent = NULL;
GSList *dupe_finder_list[HOLO_MERO_COUNT] = {NULL};
guint8 i = (WORDNET_INTERFACE_MERONYMS == id) ? TREE_MERONYMS : TREE_HOLONYMS;
GtkTreePath *head_level = NULL;
GtkTreeView *tree_view = GTK_TREE_VIEW(gtk_builder_get_object(gui_builder, relative_tree[i]));
GtkTreeStore *tree_store = GTK_TREE_STORE(gtk_tree_view_get_model(tree_view));
g_assert(tree_store);
for(guint8 j = 0; j < HOLO_MERO_COUNT; ++j)
{
gtk_tree_store_append(tree_store, &type_iter[j], NULL);
gtk_tree_store_set(tree_store, &type_iter[j], 0, holo_mero_types[i - TREE_HOLONYMS][j], 1, PANGO_WEIGHT_SEMIBOLD, -1);
}
while(properties)
{
if(properties->data) tree = g_node_first_child((GNode*)properties->data);
while(tree)
{
i = (((WNITreeList*)tree->data)->type) - ((WORDNET_INTERFACE_HOLONYMS == id) ? ISMEMBERPTR : HASMEMBERPTR);
relative_parent = (i < HOLO_MERO_COUNT) ? &type_iter[i] : NULL;
add_nodes(tree, tree_store, relative_parent, &dupe_finder_list[i]);
tree = g_node_next_sibling(tree);
}
properties = g_slist_next(properties);
}
for(i = 0; i < HOLO_MERO_COUNT; i++)
{
// if a node has child then expand it, else remove it
if(gtk_tree_model_iter_has_child(GTK_TREE_MODEL(tree_store), &type_iter[i]))
{
head_level = gtk_tree_model_get_path(GTK_TREE_MODEL(tree_store), &type_iter[i]);
gtk_tree_view_expand_row(tree_view, head_level, FALSE);
gtk_tree_path_free(head_level);
head_level = NULL;
}
else
{
gtk_tree_store_remove(tree_store, &type_iter[i]);
}
g_slist_free(dupe_finder_list[i]);
dupe_finder_list[i] = NULL;
}
}
static void list_relatives_load(GSList *properties, GtkBuilder *gui_builder, WNIRequestFlags id)
{
guint16 i = 0;
guint text_weight = PANGO_WEIGHT_NORMAL;
WNIPropertyItem *temp_property = NULL;
GtkTreeView *tree_view = NULL;
GtkTreeStore *tree_store = NULL;
GtkTreeIter iter = {0};
for(i = 0; 1 != id; (id = id >> 1), i++);
tree_view = GTK_TREE_VIEW(gtk_builder_get_object(gui_builder, relative_tree[i]));
tree_store = GTK_TREE_STORE(gtk_tree_view_get_model(tree_view));
g_assert(tree_store);
while(properties)
{
temp_property = (WNIPropertyItem*) properties->data;
if(temp_property)
{
gtk_tree_store_append(tree_store, &iter, NULL);
text_weight = PANGO_WEIGHT_NORMAL;
// check if synonym has more than one mapping
// we try to get the 2nd term (I.e. index 1) instead of checking the length; parsing the whole list is insane
if(NULL != g_slist_nth(temp_property->mapping, 1))
text_weight = PANGO_WEIGHT_BOLD;
gtk_tree_store_set(tree_store, &iter, 0, temp_property->term, 1, text_weight, -1);
}
properties = g_slist_next(properties);
}
}
static void domains_load(GSList *properties, GtkBuilder *gui_builder)
{
guint8 i = 0;
WNIClassItem *class_item = NULL;
GtkTreeView *tree_class = GTK_TREE_VIEW(gtk_builder_get_object(gui_builder, relative_tree[TREE_DOMAIN]));
GtkTreeStore *class_tree_store = GTK_TREE_STORE(gtk_tree_view_get_model(tree_class));
GtkTreeIter iter = {0}, class_iter[DOMAINS_COUNT] = {{0}};
g_assert(class_tree_store);
for(i = 0; i < DOMAINS_COUNT; i++)
{
gtk_tree_store_append(class_tree_store, &class_iter[i], NULL);
gtk_tree_store_set(class_tree_store, &class_iter[i], 0, domain_types[i], 1, PANGO_WEIGHT_SEMIBOLD, -1);
}
while(properties)
{
class_item = (WNIClassItem*) properties->data;
if(class_item)
{
gtk_tree_store_append(class_tree_store, &iter, &class_iter[class_item->type - CLASSIF_CATEGORY]);
gtk_tree_store_set(class_tree_store, &iter, 0, class_item->term, 1, PANGO_WEIGHT_NORMAL, -1);
}
properties = g_slist_next(properties);
}
for(i = 0; i < DOMAINS_COUNT; i++)
{
if(!gtk_tree_model_iter_has_child(GTK_TREE_MODEL(class_tree_store), &class_iter[i]))
gtk_tree_store_remove(class_tree_store, &class_iter[i]);
}
gtk_tree_view_expand_all(tree_class);
}
static guint8 get_attribute_pos()
{
GSList *temp_results = results, *def_list = NULL;
WNINym *temp_nym = NULL;
WNIPropertyItem *prop_item = NULL;
WNIPropertyMapping *prop_mapping = NULL;
WNIDefinitionItem *def_item = NULL;
while(temp_results)
{
temp_nym = (WNINym*) temp_results->data;
if(WORDNET_INTERFACE_ATTRIBUTES == temp_nym->id)
{
prop_item = (WNIPropertyItem*) (((WNIProperties*) temp_nym->data)->properties_list)->data;
prop_mapping = (WNIPropertyMapping*) prop_item->mapping->data;
break;
}
temp_results = g_slist_next(temp_results);
}
temp_results = results;
while(temp_results)
{
temp_nym = (WNINym*) temp_results->data;
if(WORDNET_INTERFACE_OVERVIEW == temp_nym->id)
{
def_list = ((WNIOverview*) temp_nym->data)->definitions_list;
while(def_list)
{
if(def_list)
{
def_item = (WNIDefinitionItem*) def_list->data;
if(def_item->id == prop_mapping->id)
{
return def_item->pos;
}
}
def_list = g_slist_next(def_list);
}
break;
}
temp_results = g_slist_next(temp_results);
}
return 0; // control should never reach here
}
static void relatives_load(GtkBuilder *gui_builder, gboolean reset_tabs)
{
GSList *temp_results = results;
WNINym *temp_nym = NULL;
guint8 i = 0;
gboolean store_empty = TRUE, page_set = FALSE;
GtkTreeView *temp_tree_view = NULL;
GtkTreeStore *temp_tree_store = NULL;
GtkWidget *relative_tab = NULL, *expander = NULL;
GtkTreeIter temp_iter = {0};
GtkLabel *tab_label = NULL;
// instead of invalidate rect redraw, we just toggle visibility
expander = GTK_WIDGET(gtk_builder_get_object(gui_builder, EXPANDER));
gtk_widget_hide(expander);
while(temp_results)
{
temp_nym = (WNINym*) temp_results->data;
switch(temp_nym->id)
{
case WORDNET_INTERFACE_OVERVIEW:
{
list_relatives_load(((WNIOverview*) temp_nym->data)->synonyms_list, gui_builder, temp_nym->id);
break;
}
case WORDNET_INTERFACE_ANTONYMS:
{
antonyms_load(((WNIProperties*) temp_nym->data)->properties_list, gui_builder);
break;
}
case WORDNET_INTERFACE_ATTRIBUTES:
case WORDNET_INTERFACE_DERIVATIONS:
case WORDNET_INTERFACE_CAUSES:
case WORDNET_INTERFACE_ENTAILS:
case WORDNET_INTERFACE_SIMILAR:
{
list_relatives_load(((WNIProperties*) temp_nym->data)->properties_list, gui_builder, temp_nym->id);
break;
}
case WORDNET_INTERFACE_PERTAINYMS:
case WORDNET_INTERFACE_HYPERNYMS:
case WORDNET_INTERFACE_HYPONYMS:
{
trees_load(((WNIProperties*) temp_nym->data)->properties_list, gui_builder, temp_nym->id);
break;
}
case WORDNET_INTERFACE_HOLONYMS:
case WORDNET_INTERFACE_MERONYMS:
{
holo_mero_load(((WNIProperties*) temp_nym->data)->properties_list, gui_builder, temp_nym->id);
break;
}
case WORDNET_INTERFACE_CLASS:
{
domains_load(((WNIProperties*) temp_nym->data)->properties_list, gui_builder);
break;
}
default:
break;
}
temp_results = g_slist_next(temp_results);
i++;
}
// set tab visibility
for(i = 0; i < TOTAL_RELATIVES; i++)
{
temp_tree_view = GTK_TREE_VIEW(gtk_builder_get_object(gui_builder, relative_tree[i]));
relative_tab = gtk_widget_get_parent(GTK_WIDGET(temp_tree_view));
temp_tree_store = GTK_TREE_STORE(gtk_tree_view_get_model(temp_tree_view));
if(temp_tree_store)
{
store_empty = !gtk_tree_model_get_iter_first(GTK_TREE_MODEL(temp_tree_store), &temp_iter);
}
if(store_empty || NULL == temp_tree_store)
gtk_widget_hide(relative_tab);
else if(reset_tabs && !page_set)
{
gtk_notebook_set_current_page(GTK_NOTEBOOK(gtk_builder_get_object(gui_builder, NOTEBOOK)), i);
page_set = TRUE;
}
// see if the term has attributes in first place (check visibility for that)
// if available, set the label based on the POS
if(gtk_widget_get_visible(GTK_WIDGET(relative_tab)) && TREE_ATTRIBUTES == i)
{
tab_label = GTK_LABEL(gtk_builder_get_object(gui_builder, LABEL_ATTRIBUTES));
if(get_attribute_pos() == ADJ)
gtk_label_set_text(tab_label, LABEL_TEXT_ATTRIBUTE_OF);
else
gtk_label_set_text(tab_label, LABEL_TEXT_ATTRIBUTES);
}
}
// instead of invalidate rect redraw, we just toggle visibility
gtk_widget_show(expander);
// if the expander was contracted automatically, then expand it back again
if(!gtk_expander_get_expanded(GTK_EXPANDER(expander)) && auto_contract)
gtk_expander_set_expanded(GTK_EXPANDER(expander), TRUE);
}
static gchar* wildmat_to_regex(const gchar *wildmat)
{
guint16 i = 0;
gchar ch = 0;
GString *regex = g_string_new("^");
/* Conversion Rules are simple at this point.
1. To get the proper term from start to end, make sure the beginning and end of the regex is ^ and $ respectively
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
so in regex make it as 'a.*b', which will make * applied to . (dot means any char. except newline in regex)
3. A dot/period in regex acts as a Joker (?) in wildmat, so make a direct conv.
4. Other chars go in as such, its the same in both for stuff like [r|m|s]{m,n} or [a-m]+, etc. */
while((ch = wildmat[i++]) != '\0')
{
if(ch == '*')
regex = g_string_append(regex, ".*");
else if(ch == '?')
regex = g_string_append_c(regex, '.');
else
regex = g_string_append_c(regex, ch);
}
regex = g_string_append_c(regex, '$');
regex = g_string_append_c(regex, '\0');
return g_string_free(regex, FALSE);
}
static gboolean set_regex_results(const gchar *wildmat_exp, GtkBuilder *gui_builder)
{
gchar *regex_pattern = NULL, *lemma = NULL;
GRegex *regex = NULL;
GMatchInfo *match_info = NULL;
guint32 count = 0;
GtkTextView *text_view = NULL;
GtkTextBuffer *text_buffer = NULL;
GtkTextIter cur = {0};
GtkStatusbar *status_bar = GTK_STATUSBAR(gtk_builder_get_object(gui_builder, STATUSBAR));
gchar status_msg[MAX_STATUS_MSG] = "";
gboolean results_set = FALSE;
// convert the wilmat expr. to PERL regex
if(wildmat_exp)
regex_pattern = wildmat_to_regex(wildmat_exp);
if(regex_pattern)
{
text_view = GTK_TEXT_VIEW(gtk_builder_get_object(gui_builder, TEXT_VIEW_DEFINITIONS));
text_buffer = gtk_text_view_get_buffer(text_view);
// clear text and get the iter
gtk_text_buffer_set_text(text_buffer, "", -1);
gtk_text_buffer_get_start_iter(text_buffer, &cur);
// set heading that Regex mode is now in action
gtk_text_buffer_insert_with_tags_by_name(text_buffer, &cur, STR_REGEX_DETECTED, -1, TAG_LEMMA, NULL);
gtk_text_buffer_insert(text_buffer, &cur, NEW_LINE, -1);
// compile a GRegex
regex = g_regex_new(regex_pattern, G_REGEX_MULTILINE|G_REGEX_CASELESS|G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, G_REGEX_MATCH_NOTEMPTY, NULL);
if(regex)
{
// do the actual regex matching and get the count
g_regex_match(regex, wordnet_terms->str, G_REGEX_MATCH_NOTEMPTY, &match_info);
count = g_match_info_get_match_count(match_info);
// make a header for the search results to follow
if(count > 0)
{
gtk_text_buffer_insert_with_tags_by_name(text_buffer, &cur, STR_SUGGEST_MATCHES, -1, TAG_MATCH, NULL);
}
// initialize count to 0 for the actual count to be displayed in the status bar
count = 0;
// insert the matches one by one
while(g_match_info_matches(match_info))
{
lemma = g_match_info_fetch(match_info, 0);
gtk_text_buffer_insert(text_buffer, &cur, NEW_LINE, -1);
gtk_text_buffer_insert_with_tags_by_name(text_buffer, &cur, lemma, -1, TAG_SUGGESTION, NULL);
g_free(lemma);
lemma = NULL;
g_match_info_next (match_info, NULL);
count++;
}
// free all the initialised data
g_match_info_free(match_info);
match_info = NULL;
g_regex_unref(regex);
// set the status bar accordingly with the count
gtk_statusbar_pop(status_bar, status_msg_context_id);
g_snprintf(status_msg, MAX_STATUS_MSG, STR_STATUS_REGEX, count, count > 0?STR_STATUS_LOOKUP_HINT:"");
status_msg_context_id = gtk_statusbar_get_context_id(status_bar, STATUS_DESC_REGEX_RESULTS);
gtk_statusbar_push(status_bar, status_msg_context_id, status_msg);
}
// if regex was invalid (it would be NULL)
// or if the expr. fetched no results, set regex failed message
if(NULL == regex || 0 == count)
{
gtk_text_buffer_insert_with_tags_by_name(text_buffer, &cur, STR_REGEX_FAILED, -1, TAG_SUGGESTION, NULL);
}
else
results_set = TRUE;
g_free(regex_pattern);
regex_pattern = NULL;
}
return results_set;
}
static gboolean is_wildmat_expr(const gchar *expr)
{
guint16 i = 0;
gchar ch = 0;
if(expr)
{
// if the passed expr. has any of the char below deem it as a wildmat expr.
while((ch = expr[i++]) != '\0')
{
if(ch == '*' || ch == '?' || ch == '[' || ch == ']' || ch =='{' || ch == '}' || ch == '+')
return TRUE;
}
}
return FALSE;
}
static gboolean handle_wildmat_expr(GtkBuilder *gui_builder, GtkTextBuffer *buffer)
{
GtkWindow *window = GTK_WINDOW(gtk_builder_get_object(gui_builder, WINDOW_MAIN));
GtkExpander *expander = GTK_EXPANDER(gtk_builder_get_object(gui_builder, EXPANDER));
GtkStatusbar *status_bar = GTK_STATUSBAR(gtk_builder_get_object(gui_builder, STATUSBAR));
GtkComboBox *combo_query = GTK_COMBO_BOX(gtk_builder_get_object(gui_builder, COMBO_QUERY));
GtkEntry *query_entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo_query)));
gchar status_msg[MAX_STATUS_MSG] = "";
gboolean results_set = FALSE;
// Check if the fed string is a wildmat expr.
// If true, call the regex mod. to set the results and return
gchar *regex_text = g_strstrip(g_strdup(gtk_entry_get_text(query_entry)));
if(is_wildmat_expr(regex_text))
{
// clear previously set relatives
// clear search successful flag; without this when the prev. looked up
// word is again dbl clicked from the results, it won't jump to the word
relatives_clear_all(gui_builder);
last_search_successful = FALSE;
// contract the expander, since relatives aren't required here
if(gtk_expander_get_expanded(expander))
{
gtk_expander_set_expanded(expander, FALSE);
auto_contract = TRUE;
}
gtk_statusbar_pop(status_bar, status_msg_context_id);
if(wordnet_terms)
{
// set the status bar to inform that the search is on going
g_snprintf(status_msg, MAX_STATUS_MSG, STR_STATUS_SEARCHING);
status_msg_context_id = gtk_statusbar_get_context_id(status_bar, STATUS_DESC_REGEX_SEARCHING);
gtk_statusbar_push(status_bar, status_msg_context_id, status_msg);
// if visible, update the statusbar; the window will usually go on a freeze
// without updating the statusbar, until GDK is idle
// this call forces GDK to first redraw the statusbar and proceed
if(gtk_widget_get_visible(GTK_WIDGET(window)))
gdk_window_process_updates(((GtkWidget*)status_bar)->window, FALSE);
/* if the results were set rightly, then add it to the search history */
if(set_regex_results(regex_text, gui_builder))
update_history(combo_query, regex_text);
}
else
{
GtkTextIter cur = {0};
gchar *lemma = NULL;
// report that the search index (index.sense) is missing
g_snprintf(status_msg, MAX_STATUS_MSG, STR_STATUS_REGEX_FILE_MISSING);
status_msg_context_id = gtk_statusbar_get_context_id(status_bar, STATUS_DESC_REGEX_ERROR);
gtk_statusbar_push(status_bar, status_msg_context_id, status_msg);
gtk_text_buffer_set_text(buffer, "", -1);
gtk_text_buffer_get_end_iter(buffer, &cur);
lemma = g_strdup_printf(STR_REGEX_FILE_MISSING, SetSearchdir());
if(lemma)
{
gtk_text_buffer_insert_with_tags_by_name(buffer, &cur, lemma, -1, TAG_POS, NULL);
g_free(lemma);
lemma = NULL;
}
}
results_set = TRUE;
}
g_free(regex_text);
regex_text = NULL;
return results_set;
}
static gboolean is_entry_unique(GtkTreeModel *query_list_store, GtkTreePath *path, GtkTreeIter *iter, gchar **lemma)
{
gboolean duplicate = FALSE;
gchar *str_list_item = NULL;
gtk_tree_model_get(query_list_store, iter, 0, &str_list_item, -1);
duplicate = (0 == g_strcmp0(*lemma, str_list_item));
g_free(str_list_item);
str_list_item = NULL;
if(duplicate)
{
*lemma = NULL;
}
return duplicate;
}
static gboolean update_history(GtkComboBox *combo_query, char *lemma)
{
GtkListStore *query_list_store = GTK_LIST_STORE(gtk_combo_box_get_model(combo_query));
gboolean inserted = FALSE;
if(!query_list_store)
g_error("Unable to get query combo box's model!");
gtk_tree_model_foreach(GTK_TREE_MODEL(query_list_store), (GtkTreeModelForeachFunc) is_entry_unique, (gpointer) &lemma);
if(lemma)
{
GtkTreeIter query_list_iter = {0};
gtk_list_store_prepend(query_list_store, &query_list_iter);
gtk_list_store_set(query_list_store, &query_list_iter, 0, lemma, -1);
inserted = TRUE;
}
return inserted;
}
static gboolean update_history_and_save(GtkComboBox *combo_query, char *lemma)
{
gboolean saved = FALSE;
if(update_history(combo_query, lemma))
{
static gboolean update_history_in_file_fail_notifed = FALSE;
if(update_history_in_file(lemma))
{
saved = TRUE;
}
else if(!update_history_in_file_fail_notifed)
{
GtkDialog *err_msg = GTK_DIALOG(gtk_message_dialog_new(NULL,
0,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_CLOSE,
STR_HISTORY_FILE_UPDATE_FAILED));
gtk_dialog_run(err_msg);
gtk_widget_destroy(GTK_WIDGET(err_msg));
update_history_in_file_fail_notifed = TRUE;
}
}
return saved;
}
static void show_definitions(GtkBuilder *gui_builder, GtkTextBuffer *buffer)
{
g_assert(results);
GSList *def_list = ((WNIOverview*)((WNINym*)results->data)->data)->definitions_list;
WNIDefinitionItem *def_item = NULL;
GSList *definitions_list = NULL, *example = NULL;
GtkTextMark *freq_marker = NULL;
GtkTextIter cur = {0};
glong count = 0;
WNIDefinition *defn = NULL;
guint16 total_results = 0;
guint8 total_pos = 0;
gchar str_count[MAX_SENSE_DIGITS] = "", status_msg[MAX_STATUS_MSG] = "";
GtkStatusbar *status_bar = GTK_STATUSBAR(gtk_builder_get_object(gui_builder, STATUSBAR));
GtkComboBox *combo_query = GTK_COMBO_BOX(gtk_builder_get_object(gui_builder, COMBO_QUERY));
while(def_list)
{
if(def_list->data)
{
def_item = (WNIDefinitionItem*) def_list->data;
definitions_list = def_item->definitions;
gtk_text_buffer_get_end_iter(buffer, &cur);
gtk_text_buffer_insert_with_tags_by_name(buffer, &cur, def_item->lemma, -1, TAG_LEMMA, NULL);
gtk_text_buffer_insert(buffer, &cur, " ~ ", -1);
gtk_text_buffer_insert_with_tags_by_name(buffer, &cur, partnames[def_item->pos], -1, TAG_POS, NULL);
freq_marker = gtk_text_buffer_create_mark(buffer, NULL, &cur, TRUE);
gtk_text_buffer_insert_with_tags_by_name(buffer, &cur, NEW_LINE, -1, TAG_POS, NULL);
count = 1;
while(definitions_list)
{
defn = (WNIDefinition*) definitions_list->data;
total_results++;
g_snprintf(str_count, MAX_SENSE_DIGITS, "%3d", total_results);
gtk_text_buffer_create_mark(buffer, str_count, &cur, TRUE);
g_snprintf(str_count, MAX_SENSE_DIGITS, "%2d. ", (int)count);
gtk_text_buffer_insert_with_tags_by_name(buffer, &cur, str_count, -1, TAG_COUNTER, NULL);
gtk_text_buffer_insert(buffer, &cur, defn->definition, -1);
example = defn->examples;
definitions_list = g_slist_next(definitions_list);
if(example || definitions_list) gtk_text_buffer_insert(buffer, &cur, NEW_LINE, -1);
while(example)
{
gtk_text_buffer_insert_with_tags_by_name(buffer, &cur, (gchar*) example->data, -1, TAG_EXAMPLE, NULL);
example = g_slist_next(example);
// for each example come to appear in a new line enabled this line and
// comment the next two buffer inserts of "; " and new line
//if(example || definitions_list) gtk_text_buffer_insert(buffer, &cur, NEW_LINE, -1);
if(example)
gtk_text_buffer_insert(buffer, &cur, "; ", -1);
else if(definitions_list)
gtk_text_buffer_insert(buffer, &cur, NEW_LINE, -1);
}
count++;
}
if(show_polysemy)
{
gtk_text_buffer_get_iter_at_mark(buffer, &cur, freq_marker);
gtk_text_buffer_insert(buffer, &cur, " ", -1);
gtk_text_buffer_insert_with_tags_by_name(buffer,
&cur,
familiarity[get_frequency(count - 1)],
-1,
familiarity[get_frequency(count - 1)],
NULL);
}
}
def_list = g_slist_next(def_list);
if(def_list)
{
gtk_text_buffer_get_end_iter(buffer, &cur);
gtk_text_buffer_insert(buffer, &cur, NEW_LINE, -1);
}
total_pos++;
}
// end of setting definitions
/* scroll to the text view top to show the first definition
gtk_text_buffer_get_start_iter(buffer, &cur);
gtk_text_view_scroll_to_iter(text_view, &cur, 0.0, FALSE, 0.0, 0.0); */
// set status
gtk_statusbar_pop(status_bar, status_msg_context_id);
g_snprintf(status_msg, MAX_STATUS_MSG, STR_STATUS_QUERY_SUCCESS, total_results, total_pos);
status_msg_context_id = gtk_statusbar_get_context_id(status_bar, STATUS_DESC_SEARCH_SUCCESS);
gtk_statusbar_push(status_bar, status_msg_context_id, status_msg);
// manage history
update_history_and_save(combo_query,
((WNIDefinitionItem*)((WNIOverview*)((WNINym*)results->data)->data)->definitions_list->data)->lemma);
}
static void show_suggestions(GtkBuilder *gui_builder, GtkTextBuffer *buffer, GtkTextIter *cur, GSList *suggestions)
{
GtkExpander *expander = GTK_EXPANDER(gtk_builder_get_object(gui_builder, EXPANDER));
gtk_text_buffer_insert(buffer, cur, NEW_LINE, -1);
gtk_text_buffer_insert_with_tags_by_name(buffer, cur, STR_SUGGEST_MATCHES, -1, TAG_MATCH, NULL);
while(suggestions)
{
gtk_text_buffer_insert(buffer, cur, NEW_LINE, -1);
gtk_text_buffer_insert_with_tags_by_name(buffer,
cur,
suggestions->data,
-1,
TAG_SUGGESTION,
NULL);
suggestions = g_slist_next(suggestions);
}
// contract the expander, since relatives aren't required here
if(gtk_expander_get_expanded(expander))
{
gtk_expander_set_expanded(expander, FALSE);
auto_contract = TRUE;
}
}
static void construct_show_notification()
{
const gchar *lemma = ((WNIDefinitionItem*)((WNIOverview*)((WNINym*)results->data)->data)->definitions_list->data)->lemma;
gchar *definition =
g_strconcat(partnames[((WNIDefinitionItem*)((WNIOverview*)((WNINym*)results->data)->data)->definitions_list->data)->pos],
". ",
((WNIDefinition*)((WNIDefinitionItem*)((WNIOverview*)((WNINym*)results->data)->data)->definitions_list->data)->definitions->data)->definition,
NULL);
// close any previous updates
notify_notification_close(mod_notifier, NULL);
if(definition)
{
gboolean update_successful = notify_notification_update(mod_notifier, lemma, definition, "gtk-dialog-info");
if(!update_successful || (FALSE == notify_notification_show(mod_notifier, NULL)))
{
g_warning("Failed to display notification\n");
}
g_free(definition);
definition = NULL;
}
}
static void show_searching(GtkBuilder *gui_builder)
{
gchar status_msg[MAX_STATUS_MSG] = "";
GtkWindow *window = GTK_WINDOW(gtk_builder_get_object(gui_builder, WINDOW_MAIN));
GtkStatusbar *status_bar = GTK_STATUSBAR(gtk_builder_get_object(gui_builder, STATUSBAR));
// set the status bar and update the window
gtk_statusbar_pop(status_bar, status_msg_context_id);
g_snprintf(status_msg, MAX_STATUS_MSG, STR_STATUS_SEARCHING);
status_msg_context_id = gtk_statusbar_get_context_id(status_bar, STATUS_DESC_REGEX_SEARCHING);
gtk_statusbar_push(status_bar, status_msg_context_id, status_msg);
// if visible, repaint the statusbar after setting the status message, for it to get reflected
if(gtk_widget_get_visible(GTK_WIDGET(window)))
gdk_window_process_updates(((GtkWidget*)status_bar)->window, FALSE);
}
static void query_wni(gchar *search_str,
WNIRequestFlags lookup_type,
GSList **suggestions)
{
G_MESSAGE("'%s' requested from WNI!\n", search_str);
wni_request_nyms(search_str, &results, lookup_type, advanced_mode);
// if the search failed and suggestions are available, check if the prime
// suggestion is the same as the search string
if(!results && mod_suggest)
{
GSList *temp_suggestions = suggestions_get(search_str);
if(temp_suggestions && are_same_after_strip(search_str, temp_suggestions->data))
{
wni_request_nyms(temp_suggestions->data, &results, lookup_type, advanced_mode);
}
// if the caller wanted the suggestions and the suggestions lookup succeeded
// then don't free the results, but pass it on
else if(suggestions)
{
*suggestions = temp_suggestions;
}
// if not passing on, free
if(!suggestions || (*suggestions != temp_suggestions))
{
g_slist_free_full(temp_suggestions, g_free);
}
}
}
static void button_search_click(GtkButton *button, gpointer user_data)
{
GtkBuilder *gui_builder = GTK_BUILDER(user_data);
GtkComboBox *combo_query = GTK_COMBO_BOX(gtk_builder_get_object(gui_builder, COMBO_QUERY));
GtkEntry *combo_entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo_query)));
GtkTextView *text_view = GTK_TEXT_VIEW(gtk_builder_get_object(gui_builder, TEXT_VIEW_DEFINITIONS));
GtkTextBuffer *buffer = gtk_text_view_get_buffer(text_view);
gchar *search_str = NULL;
// if it was a regex, then results are now furnished, so return
if(handle_wildmat_expr(gui_builder, buffer))
return;
// from here on normal lookup starts
search_str = g_strdup(gtk_entry_get_text(combo_entry));
search_str = g_strstrip(strip_invalid_edges(search_str));
// make sure this isn't a 0 length string
if(search_str && search_str[0])
{
gboolean new_search = TRUE;
GSList *suggestions = NULL;
G_MESSAGE("'%s' queried!\n", search_str);
if(last_search_successful)
new_search = (g_ascii_strcasecmp(last_search, search_str) != 0);
// check if this isn't the previous search query
if(new_search)
{
show_searching(gui_builder);
query_wni(search_str, WORDNET_INTERFACE_ALL, &suggestions);
// clear prior text in definitons text view & relatives
gtk_text_buffer_set_text(buffer, "", -1);
relatives_clear_all(gui_builder);
// set definitions
if(results)
{
G_MESSAGE("Results successful!\n");
show_definitions(gui_builder, buffer);
relatives_load(gui_builder, TRUE);
g_stpcpy(last_search, search_str);
last_search_successful = TRUE;
}
else
{
GtkStatusbar *status_bar = GTK_STATUSBAR(gtk_builder_get_object(gui_builder, STATUSBAR));
GtkTextIter cur = {0};
gtk_text_buffer_set_text(buffer, "", -1);
gtk_text_buffer_get_start_iter(buffer, &cur);
gtk_text_buffer_insert_with_tags_by_name(buffer, &cur, STR_QUERY_FAILED, -1, TAG_POS, NULL);
if(suggestions)
show_suggestions(gui_builder, buffer, &cur, suggestions);
/* status bar should be updated */
gtk_statusbar_pop(status_bar, status_msg_context_id);
status_msg_context_id = gtk_statusbar_get_context_id(status_bar, STATUS_DESC_SEARCH_FAILURE);
gtk_statusbar_push(status_bar, status_msg_context_id, STR_STATUS_QUERY_FAILED);
last_search_successful = FALSE;
}
if(suggestions)
{
g_slist_free_full(suggestions, g_free);
suggestions = NULL;
}
GtkWindow *window = GTK_WINDOW(gtk_builder_get_object(gui_builder, WINDOW_MAIN));
show_window(window);
}
gtk_editable_select_region(GTK_EDITABLE(combo_entry), 0, -1);
gtk_widget_grab_focus(GTK_WIDGET(combo_query));
}
if(search_str)
{
g_free(search_str);
search_str = NULL;
}
last_lookup_a_notification = FALSE;
}
static void combo_query_changed(GtkComboBox *combo_query, gpointer user_data)
{
GtkBuilder *gui_builder = GTK_BUILDER(user_data);
GtkButton *button_search = GTK_BUTTON(gtk_builder_get_object(gui_builder, BUTTON_SEARCH));
GtkToolbar *tool_bar = GTK_TOOLBAR(gtk_builder_get_object(gui_builder, TOOLBAR));
GtkToolItem *toolbar_prev = NULL, *toolbar_next = NULL;
GtkTreeModel *tree_model = NULL;
gint selected_item = gtk_combo_box_get_active(combo_query);
gint total_items = 0;
if(selected_item != -1)
{
gtk_button_clicked(button_search);
tree_model = gtk_combo_box_get_model(combo_query);
total_items = gtk_tree_model_iter_n_children(tree_model, NULL);
if(total_items > 1)
{
toolbar_next = gtk_toolbar_get_nth_item(tool_bar, 1);
toolbar_prev = gtk_toolbar_get_nth_item(tool_bar, 0);
if(0 == selected_item)
{
gtk_widget_set_sensitive(GTK_WIDGET(toolbar_prev), TRUE);
gtk_widget_set_sensitive(GTK_WIDGET(toolbar_next), FALSE);
}
else if(total_items == selected_item + 1)
{
gtk_widget_set_sensitive(GTK_WIDGET(toolbar_prev), FALSE);
gtk_widget_set_sensitive(GTK_WIDGET(toolbar_next), TRUE);
}
else
{
gtk_widget_set_sensitive(GTK_WIDGET(toolbar_next), TRUE);
gtk_widget_set_sensitive(GTK_WIDGET(toolbar_prev), TRUE);
}
}
}
}
static gboolean text_view_button_pressed(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
{
if(GDK_2BUTTON_PRESS == event->type)
was_double_click = TRUE;
return FALSE;
}
static gboolean text_view_button_released(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
{
GtkBuilder *gui_builder = GTK_BUILDER(user_data);
GtkTextView *defn_text_view = GTK_TEXT_VIEW(gtk_builder_get_object(gui_builder, TEXT_VIEW_DEFINITIONS));
GtkTextBuffer *defn_text_buffer = gtk_text_view_get_buffer(defn_text_view);
GtkTextMark *insert_mark = NULL, *selection_mark = NULL;
GtkTextIter ins_iter = {0}, sel_iter = {0};
gchar *trial_text = NULL;
guint8 i = 0;
gboolean iter_move_success = FALSE;
// set the selection if it was a double click or a CTRL + mouse release
if(was_double_click || (GDK_CONTROL_MASK & event->state))
{
// if it was a double click, try selecting diff. combinations
// for the sake of compound lemmas like 'work out', 'in a nutshell', etc.
if(was_double_click)
{
was_double_click = FALSE;
if(gtk_text_buffer_get_has_selection(defn_text_buffer))
{
// get the 'selection_bound' and 'insert' markers
selection_mark = gtk_text_buffer_get_selection_bound(defn_text_buffer);
insert_mark = gtk_text_buffer_get_mark(defn_text_buffer, "insert");
for(i = 0; ((i < 6) && (!iter_move_success)); i++)
{
// on every iteration of the loop, set the iters to the actual sel. marks
gtk_text_buffer_get_iter_at_mark(defn_text_buffer, &ins_iter, insert_mark);
gtk_text_buffer_get_iter_at_mark(defn_text_buffer, &sel_iter, selection_mark);
switch(i)
{
case 0:
// try selecting two words after the actual sel.
if(!gtk_text_iter_forward_visible_word_ends(&sel_iter, 2))
gtk_text_iter_forward_to_end(&sel_iter);
iter_move_success = TRUE;
break;
case 1:
// try selecting two words before the actual sel.
if(gtk_text_iter_backward_visible_word_starts(&ins_iter, 2))
iter_move_success = TRUE;
break;
case 2:
// try selecting one word before and after the actual sel.
if((gtk_text_iter_forward_visible_word_end(&sel_iter)) && (gtk_text_iter_backward_visible_word_start(&ins_iter)))
iter_move_success = TRUE;
break;
case 3:
// try selecting one word next to the actual sel.
if(!gtk_text_iter_forward_visible_word_end(&sel_iter))
gtk_text_iter_forward_to_end(&sel_iter);
iter_move_success = TRUE;
break;
case 4:
// try selecting one word before the actual sel.
if(gtk_text_iter_backward_visible_word_start(&ins_iter))
iter_move_success = TRUE;
break;
case 5:
// fallback to just the actual selection
iter_move_success = TRUE;
break;
}
if(iter_move_success)
{
// obtain the text for the set iters
trial_text = gtk_text_buffer_get_text(defn_text_buffer, &ins_iter, &sel_iter, FALSE);
// search the trial selection in WordNet, on a successful lookup iter_move_success is set
iter_move_success = wni_request_nyms(trial_text, NULL, (WNIRequestFlags) 0, FALSE);
// free the obtained text
g_free(trial_text);
trial_text = NULL;
}
}
// set the 'insert' and 'selection_bound' markers respectivly to the newly placed iters
gtk_text_buffer_move_mark(defn_text_buffer, insert_mark, &ins_iter);
gtk_text_buffer_move_mark(defn_text_buffer, selection_mark, &sel_iter);
}
}
// request the "TARGETS" target for the primary selection
gtk_selection_convert(widget, GDK_SELECTION_PRIMARY, GDK_TARGET_STRING, event->time);
}
return FALSE;
}
static void text_view_selection_made(GtkWidget *widget, GtkSelectionData *sel_data, guint time, gpointer user_data)
{
GtkBuilder *gui_builder = GTK_BUILDER(user_data);
GtkComboBox *combo_query = NULL;
GtkEntry *txtQuery = NULL;
GtkButton *button_search = NULL;
gchar *selection = (gchar*) gtk_selection_data_get_text(sel_data);
if(selection)
{
combo_query = GTK_COMBO_BOX(gtk_builder_get_object(gui_builder, COMBO_QUERY));
txtQuery = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo_query)));
button_search = GTK_BUTTON(gtk_builder_get_object(gui_builder, BUTTON_SEARCH));
if(0 != g_strcmp0(gtk_entry_get_text(txtQuery), selection))
{
gtk_entry_set_text(txtQuery, selection);
//gtk_editable_set_position(GTK_EDITABLE(txtQuery), -1);
}
g_free(selection);
selection = NULL;
gtk_button_clicked(button_search);
}
}
static void expander_clicked(GtkExpander *expander, gpointer user_data)
{
GtkBuilder *gui_builder = GTK_BUILDER(user_data);
GtkPaned *vpaned = GTK_PANED(gtk_builder_get_object(gui_builder, VPANE));
gtk_paned_set_position(vpaned, -1); // unset the pane's position so that it goes to the original state
// if the user manually expands/contracts it, reset the auto flag
auto_contract = FALSE;
}
static void query_list_updated(GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter, gpointer user_data)
{
GtkBuilder *gui_builder = GTK_BUILDER(user_data);
GtkToolbar *tool_bar = GTK_TOOLBAR(gtk_builder_get_object(gui_builder, TOOLBAR));
GtkToolItem *toolbar_prev = gtk_toolbar_get_nth_item(tool_bar, 0);
GtkToolItem *toolbar_next = gtk_toolbar_get_nth_item(tool_bar, 1);
GtkTreeIter temp_iter = {0};
if(gtk_tree_model_iter_nth_child(tree_model, &temp_iter, NULL, 1))
{
gtk_widget_set_sensitive(GTK_WIDGET(toolbar_prev), TRUE);
gtk_widget_set_sensitive(GTK_WIDGET(toolbar_next), FALSE);
}
}
static GtkTextMark* highlight_definition(guint8 id, guint16 sense, GtkTextView *text_view)
{
guint16 defn_no = 0;
GSList *wni_results = results, *definitions_list = NULL;
GtkTextBuffer *buffer = NULL;
GtkTextMark *mark = NULL;
GtkTextIter start = {0}, end = {0};
gchar str_index[MAX_SENSE_DIGITS] = "";
while(wni_results)
{
if(WORDNET_INTERFACE_OVERVIEW == ((WNINym*)wni_results->data)->id)
{
definitions_list = ((WNIOverview*)((WNINym*)wni_results->data)->data)->definitions_list;
while(definitions_list)
{
if(id == ((WNIDefinitionItem*)definitions_list->data)->id)
{
defn_no += sense + 1;
buffer = gtk_text_view_get_buffer(text_view);
g_snprintf(str_index, MAX_SENSE_DIGITS, "%3d", defn_no);
mark = gtk_text_buffer_get_mark(buffer, str_index);
if(mark)
{
gtk_text_buffer_get_iter_at_mark(buffer, &start, mark);
gtk_text_buffer_get_iter_at_offset(buffer, &end, gtk_text_iter_get_offset(&start) + 3);
gtk_text_buffer_apply_tag_by_name(buffer, TAG_HIGHLIGHT, &start, &end);
}
break;
}
else
defn_no += ((WNIDefinitionItem*)definitions_list->data)->count;
definitions_list = g_slist_next(definitions_list);
}
break;
}
wni_results = g_slist_next(wni_results);
}
return mark;
}
static void highlight_senses_from_synonyms(guint16 relative_index, GtkTextView *text_view)
{
GSList *wni_results = results;
GSList *synonyms_list = NULL;
GSList *synonym_mapping = NULL;
GtkTextMark *highlighted_mark = NULL, *scroll_mark = NULL;
GtkTextBuffer *buffer = NULL;
GtkTextIter iter = {0};
guint16 last_offset = 0, current_offset = 0;
while(wni_results)
{
if(WORDNET_INTERFACE_OVERVIEW == ((WNINym*)wni_results->data)->id)
{
synonyms_list = ((WNIOverview*) ((WNINym*)wni_results->data)->data)->synonyms_list;
for(; relative_index; relative_index--, synonyms_list = g_slist_next(synonyms_list));
synonym_mapping = ((WNIPropertyItem*) synonyms_list->data)->mapping;
buffer = gtk_text_view_get_buffer(text_view);
gtk_text_buffer_get_end_iter(buffer, &iter);
// store the last line number
last_offset = gtk_text_iter_get_line(&iter);
while(synonym_mapping)
{
highlighted_mark = highlight_definition(((WNISynonymMapping*) synonym_mapping->data)->id, ((WNISynonymMapping*) synonym_mapping->data)->sense, text_view);
gtk_text_buffer_get_iter_at_mark(buffer, &iter, highlighted_mark);
// see if this mark is at a higher line
current_offset = gtk_text_iter_get_line(&iter);
if(current_offset <= last_offset)
{
last_offset = current_offset;
scroll_mark = highlighted_mark;
}
synonym_mapping = g_slist_next(synonym_mapping);
}
gtk_text_view_scroll_to_mark(text_view, scroll_mark, 0.0, TRUE, 0, 0);
break;
}
wni_results = g_slist_next(wni_results);
}
}
static void highlight_senses_from_antonyms( guint8 category, guint16 relative_index, guint16 sub_level, GtkTextView *text_view)
{
GSList *wni_results = results;
GSList *antonyms_list = NULL;
GSList *antonym_mapping = NULL;
WNIAntonymItem *antonym_item = NULL;
guint16 direct_count = 0, indirect_count = 0, count = 0;
GtkTextBuffer *buffer = NULL;
GtkTextMark *scroll_mark = NULL, *highlighted_mark = NULL;
GtkTextIter iter = {0}, end = {0};
while(wni_results)
{
if(WORDNET_INTERFACE_ANTONYMS == ((WNINym*)wni_results->data)->id)
{
antonyms_list = ((WNIProperties*) ((WNINym*) wni_results->data)->data)->properties_list;
while(antonyms_list)
{
antonym_item = (WNIAntonymItem*) antonyms_list->data;
if(antonym_item->relation <= DIRECT_ANT)
direct_count++;
else
{
count = g_slist_length(antonym_item->implications);
indirect_count += count;
if(indirect_count > relative_index)
indirect_count = relative_index + 1;
}
if((category <= DIRECT_ANT && direct_count == relative_index + 1) ||
(category == INDIRECT_ANT && indirect_count == relative_index + 1))
{
antonym_mapping = antonym_item->mapping;
buffer = gtk_text_view_get_buffer(text_view);
gtk_text_buffer_get_end_iter(buffer, &end);
// store the last line number
direct_count = gtk_text_iter_get_line(&end);
while(antonym_mapping)
{
if(sub_level)
{
count += ((WNIAntonymMapping*) antonym_mapping->data)->related_word_count;
if(sub_level > count)
{
antonym_mapping = g_slist_next(antonym_mapping);
continue;
}
}
highlighted_mark = highlight_definition(((WNIAntonymMapping*) antonym_mapping->data)->id,
((WNIAntonymMapping*) antonym_mapping->data)->sense, text_view);
gtk_text_buffer_get_iter_at_mark(buffer, &iter, highlighted_mark);
// see if this mark is at a higher line
indirect_count = gtk_text_iter_get_line(&iter);
if(indirect_count <= direct_count)
{
direct_count = indirect_count;
scroll_mark = highlighted_mark;
}
antonym_mapping = g_slist_next(antonym_mapping);
if(sub_level)
{
if(sub_level <= count) break;
}
}
gtk_text_view_scroll_to_mark(text_view, scroll_mark, 0.0, TRUE, 0, 0);
break;
}
antonyms_list = g_slist_next(antonyms_list);
}
break;
}
wni_results = g_slist_next(wni_results);
}
}
static void highlight_senses_from_domain(guint8 category, guint16 relative_index, GtkTextView *text_view)
{
GSList *wni_results = results;
GSList *class_list = NULL;
WNIClassItem *class_item = NULL;
guint16 count = 0;
while(wni_results)
{
if(WORDNET_INTERFACE_CLASS == ((WNINym*) wni_results->data)->id)
{
class_list = ((WNIProperties*) ((WNINym*) wni_results->data)->data)->properties_list;
while(class_list)
{
class_item = (WNIClassItem*)class_list->data;
if(category == class_item->type - CLASSIF_CATEGORY)
count++;
if(count == relative_index + 1)
{
gtk_text_view_scroll_to_mark(text_view, highlight_definition(class_item->id, class_item->sense, text_view), 0.0, TRUE, 0, 0);
break;
}
class_list = g_slist_next(class_list);
}
break;
}
wni_results = g_slist_next(wni_results);
}
}
static void highlight_senses_from_relative_lists(WNIRequestFlags id, guint16 relative_index, GtkTextView *text_view)
{
GSList *wni_results = results;
GSList *properties_list = NULL;
GSList *property_mapping = NULL;
WNIPropertyMapping *mapping = NULL;
GtkTextMark *highlighted_mark = NULL, *scroll_mark = NULL;
GtkTextBuffer *buffer = NULL;
GtkTextIter iter = {0};
guint16 last_offset = 0, current_offset = 0;
while(wni_results)
{
if(id == ((WNINym*)wni_results->data)->id)
{
properties_list = ((WNIProperties*)((WNINym*)wni_results->data)->data)->properties_list;
for(; relative_index; relative_index--, properties_list = g_slist_next(properties_list));
property_mapping = ((WNIPropertyItem*) properties_list->data)->mapping;
buffer = gtk_text_view_get_buffer(text_view);
gtk_text_buffer_get_end_iter(buffer, &iter);
last_offset = gtk_text_iter_get_line(&iter);
while(property_mapping)
{
mapping = (WNIPropertyMapping*) property_mapping->data;
highlighted_mark = highlight_definition(mapping->id, mapping->sense, text_view);
gtk_text_buffer_get_iter_at_mark(buffer, &iter, highlighted_mark);
// see if this mark is at a higher line
current_offset = gtk_text_iter_get_line(&iter);
if(current_offset <= last_offset)
{
last_offset = current_offset;
scroll_mark = highlighted_mark;
}
property_mapping = g_slist_next(property_mapping);
}
gtk_text_view_scroll_to_mark(text_view, scroll_mark, 0.0, TRUE, 0, 0);
break;
}
wni_results = g_slist_next(wni_results);
}
}
static void relative_selection_changed(GtkTreeView *tree_view, gpointer user_data)
{
GtkTreeIter iter = {0};
GtkTreeModel *tree_store = NULL;
gchar *term = NULL, *str_path = NULL, *str_category = NULL, *str_demark = NULL;
gchar str_root[3] = "";
GtkBuilder *gui_builder = GTK_BUILDER(user_data);
GtkNotebook *notebook = GTK_NOTEBOOK(gtk_builder_get_object(gui_builder, NOTEBOOK));
GtkTextView *text_view = GTK_TEXT_VIEW(gtk_builder_get_object(gui_builder, TEXT_VIEW_DEFINITIONS));
GtkTextBuffer *buffer = NULL;
GtkTextIter start = {0}, end = {0};
guint16 sub_level = 0;
if(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(tree_view), &tree_store, &iter))
{
gtk_tree_model_get(tree_store, &iter, 0, &term, -1);
str_path = gtk_tree_model_get_string_from_iter(tree_store, &iter);
buffer = gtk_text_view_get_buffer(text_view);
gtk_text_buffer_get_start_iter(buffer, &start);
gtk_text_buffer_get_end_iter(buffer, &end);
gtk_text_buffer_remove_tag_by_name(buffer, TAG_HIGHLIGHT, &start, &end);
if(term && str_path)
{
switch(gtk_notebook_get_current_page(notebook))
{
case TREE_SYNONYMS:
highlight_senses_from_synonyms((guint16) g_ascii_strtoull(str_path, NULL, 10), text_view);
break;
case TREE_ANTONYMS:
// to make sure no highlighting is done for categories, check for ':' in the path
// Categories (Direct/Indirect) won't have a parent, hence only one char. will be
// there in the path; if not, a word has got selected
if(advanced_mode && (str_demark = g_strstr_len(str_path, -1, ":")) != NULL)
{
str_demark++;
str_demark = g_strstr_len(str_demark, -1, ":");
if(str_demark)
{
str_demark++;
sub_level = (guint16) g_ascii_strtoull(str_demark, NULL, 10) + 1;
}
gtk_tree_model_get_iter_first(tree_store, &iter);
gtk_tree_model_get(tree_store, &iter, 0, &str_category, -1);
if(str_category)
{
if(0 != g_strcmp0(str_category, STR_ANTONYM_HEADER_DIRECT)) str_path[0] = '1';
g_free(str_category);
str_category = NULL;
}
strip_invalid_edges(&str_path[2]);
/* if its a word, parent 0 means Direct and 1 Indirect */
highlight_senses_from_antonyms((0 == str_path[0] - '0') ? DIRECT_ANT : INDIRECT_ANT,
(guint16) g_ascii_strtoull(&str_path[2], NULL, 10),
sub_level,
text_view);
}
else if(!advanced_mode)
// if its simple mode, set category to 0
highlight_senses_from_antonyms(0, (guint16) g_ascii_strtoull(str_path, NULL, 10), sub_level, text_view);
break;
case TREE_DOMAIN:
if((str_demark = g_strstr_len(str_path, -1, ":")) != NULL)
{
g_snprintf(str_root, 3, "%c", str_path[0]);
gtk_tree_model_get_iter_from_string(tree_store, &iter, str_root);
gtk_tree_model_get(tree_store, &iter, 0, &str_category, -1);
for(sub_level = 0; (0 != g_strcmp0(domain_types[sub_level], str_category)); sub_level++);
g_free(str_category);
str_category = NULL;
str_demark++;
highlight_senses_from_domain((guint8) sub_level, (guint16) g_ascii_strtoull(str_demark, NULL, 10), text_view);
}
break;
case TREE_PERTAINYMS:
case TREE_HYPERNYMS:
case TREE_HYPONYMS:
case TREE_HOLONYMS:
case TREE_MERONYMS:
//highlight_senses_from_relative_trees(1 << gtk_notebook_get_current_page(notebook), g_ascii_strtoull(str_path, NULL, 10), text_view);
break;
default:
highlight_senses_from_relative_lists(1 << gtk_notebook_get_current_page(notebook), (guint16) g_ascii_strtoull(str_path, NULL, 10), text_view);
break;
}
}
g_free(term);
g_free(str_path);
term = str_path = NULL;
}
}
static void relative_row_activated(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data)
{
gchar *sel_term = NULL;
GtkTreeModel *tree_store = NULL;
GtkTreeIter iter = {0};
GtkComboBox *combo_query = NULL;
GtkEntry *txtQuery = NULL;
GtkButton *button_search = NULL;
GtkBuilder *gui_builder = GTK_BUILDER(user_data);
if(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(tree_view), &tree_store, &iter))
{
gtk_tree_model_get(tree_store, &iter, 0, &sel_term, -1);
if(sel_term)
{
combo_query = GTK_COMBO_BOX(gtk_builder_get_object(gui_builder, COMBO_QUERY));
txtQuery = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo_query)));
button_search = GTK_BUTTON(gtk_builder_get_object(gui_builder, BUTTON_SEARCH));
gtk_entry_set_text(txtQuery, sel_term);
gtk_button_clicked(button_search);
g_free(sel_term);
sel_term = NULL;
}
}
}
static void clear_history(GtkMenuItem *menu_item, GtkListStore *list_store_query)
{
gchar *hist_file_path = NULL;
GFile *history_file = NULL;
gtk_list_store_clear(list_store_query);
hist_file_path = g_strconcat(g_get_user_config_dir(), G_DIR_SEPARATOR_S, PACKAGE_TARNAME, HISTORY_FILE_EXT, NULL);
history_file = g_file_new_for_path(hist_file_path);
g_free(hist_file_path);
hist_file_path = NULL;
if(g_file_query_exists(history_file, NULL))
{
g_file_delete(history_file, NULL, NULL);
}
g_object_unref(history_file);
history_file = NULL;
}
static void save_history_to_file(GtkMenuItem *menu_item, gpointer user_data)
{
GtkFileChooser *dialog = GTK_FILE_CHOOSER(gtk_file_chooser_dialog_new("Save As",
NULL,
GTK_FILE_CHOOSER_ACTION_SAVE,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_SAVE, GTK_RESPONSE_OK,
NULL));
gchar *dest_filename = NULL;
gint response = 0;
gtk_file_chooser_set_do_overwrite_confirmation(dialog, TRUE);
gtk_file_chooser_set_local_only(dialog, TRUE);
gtk_file_chooser_set_create_folders(dialog, TRUE);
response = gtk_dialog_run(GTK_DIALOG(dialog));
dest_filename = gtk_file_chooser_get_filename(dialog);
gtk_widget_destroy(GTK_WIDGET(dialog));
dialog = NULL;
if((GTK_RESPONSE_OK == response) && dest_filename)
{
GFile *history_file = NULL;
gchar *hist_file_path = g_strconcat(g_get_user_config_dir(), G_DIR_SEPARATOR_S, PACKAGE_TARNAME, HISTORY_FILE_EXT, NULL);
if(!hist_file_path)
{
g_free(dest_filename);
dest_filename = NULL;
g_error("Failed creating path to load history");
}
history_file = g_file_new_for_path(hist_file_path);
g_free(hist_file_path);
hist_file_path = NULL;
if(g_file_query_exists(history_file, NULL))
{
GFile *dest_file = g_file_new_for_path(dest_filename);
GError *err = NULL;
GtkWidget *save_complete_dialog = NULL;
if(g_file_copy(history_file, dest_file, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, &err))
{
save_complete_dialog = gtk_message_dialog_new(NULL,
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_INFO,
GTK_BUTTONS_OK,
STR_HISTORY_FILE_SAVE_SUCCESS);
g_object_set(save_complete_dialog, "title", STR_HISTORY_FILE_SAVE_SUCCESS_TITLE, NULL);
gtk_dialog_run(GTK_DIALOG(save_complete_dialog));
}
else
{
save_complete_dialog = gtk_message_dialog_new(NULL,
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_WARNING,
GTK_BUTTONS_OK,
STR_HISTORY_FILE_SAVE_FAILED,
err->message);
g_object_set(save_complete_dialog, "title", STR_HISTORY_FILE_SAVE_FAILURE_TITLE, NULL);
gtk_dialog_run(GTK_DIALOG(save_complete_dialog));
g_clear_error(&err);
}
gtk_widget_destroy(save_complete_dialog);
g_object_unref(dest_file);
dest_file = NULL;
}
else
{
GtkWidget *save_failed_msgbox = gtk_message_dialog_new(NULL,
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_WARNING,
GTK_BUTTONS_OK,
STR_HISTORY_FILE_SAVE_FAILED,
STR_HISTORY_MISSING);
g_object_set(save_failed_msgbox, "title", STR_HISTORY_FILE_SAVE_FAILURE_TITLE, NULL);
gtk_dialog_run(GTK_DIALOG(save_failed_msgbox));
gtk_widget_destroy(save_failed_msgbox);
save_failed_msgbox = NULL;
}
g_object_unref(history_file);
history_file = NULL;
}
if(dest_filename)
{
g_free(dest_filename);
dest_filename = NULL;
}
}
static void query_combo_popup(GtkEntry *query_entry_widget, GtkMenu *popup_menu, GtkListStore *list_store_query)
{
GtkWidget *menu_item = NULL;
GtkTreeIter iter = {0};
gboolean menu_item_enabled = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(list_store_query), &iter);
menu_item = GTK_WIDGET(gtk_separator_menu_item_new());
gtk_menu_shell_prepend(GTK_MENU_SHELL(popup_menu), menu_item);
gtk_widget_show(menu_item);
menu_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_SAVE, NULL);
gtk_menu_item_set_label(GTK_MENU_ITEM(menu_item), "_Save History");
gtk_widget_set_sensitive(menu_item, menu_item_enabled);
g_signal_connect(menu_item, "activate", G_CALLBACK(save_history_to_file), NULL);
gtk_menu_shell_prepend(GTK_MENU_SHELL(popup_menu), menu_item);
gtk_widget_show(menu_item);
menu_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_CLEAR, NULL);
gtk_menu_item_set_label(GTK_MENU_ITEM(menu_item), "Cl_ear History");
gtk_widget_set_sensitive(menu_item, menu_item_enabled);
g_signal_connect(menu_item, "activate", G_CALLBACK(clear_history), list_store_query);
gtk_menu_shell_prepend(GTK_MENU_SHELL(popup_menu), menu_item);
gtk_widget_show(menu_item);
}
static void load_history(GtkListStore *list_store_query)
{
gchar *hist_file_path = g_strconcat(g_get_user_config_dir(), G_DIR_SEPARATOR_S, PACKAGE_TARNAME, HISTORY_FILE_EXT, NULL);
if(!hist_file_path)
{
g_warning("Failed creating path to load history");
return;
}
FILE *hist_file = fopen(hist_file_path, "r");
g_free(hist_file_path);
hist_file_path = NULL;
if(hist_file)
{
gchar lookup[MAX_LEMMA_LEN] = "";
while(fgets(lookup, MAX_LEMMA_LEN, hist_file))
{
size_t lookup_len = strlen(lookup);
// some char(s) and '\n' is minimum
if(lookup_len > 1 && lookup[lookup_len - 1] == '\n')
{
// remove the new line character
lookup[lookup_len - 1] = '\0';
GtkTreeIter iter = {0};
gtk_list_store_prepend(list_store_query, &iter);
gtk_list_store_set(list_store_query, &iter, 0, lookup, -1);
}
}
fclose(hist_file);
}
}
static void create_stores_renderers(GtkBuilder *gui_builder)
{
guint8 i = 0;
GtkComboBox *combo_query = NULL;
GtkListStore *list_store_query = NULL;
GtkTreeView *tree_view = NULL;
GtkTreeStore *tree_store = NULL;
GtkCellRenderer *tree_renderer = NULL;
const gchar *col_name = NULL;
GtkEntry *query_entry = NULL;
// combo box data store
combo_query = GTK_COMBO_BOX(gtk_builder_get_object(gui_builder, COMBO_QUERY));
list_store_query = gtk_list_store_new(1, G_TYPE_STRING);
load_history(list_store_query);
// popup for saving & clearing history
query_entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo_query)));
g_signal_connect(query_entry, "populate-popup", G_CALLBACK(query_combo_popup), list_store_query);
g_signal_connect(GTK_TREE_MODEL(list_store_query), "row-inserted", G_CALLBACK(query_list_updated), gui_builder);
gtk_combo_box_set_model(combo_query, GTK_TREE_MODEL(list_store_query));
gtk_combo_box_set_entry_text_column(combo_query, 0);
g_object_unref(list_store_query);
list_store_query = NULL;
for(i = 0; i < TOTAL_RELATIVES; i++)
{
tree_view = GTK_TREE_VIEW(gtk_builder_get_object(gui_builder, relative_tree[i]));
tree_renderer = gtk_cell_renderer_text_new();
col_name = relative_tree[i];
col_name = col_name + 4; // skip the first 4 chars "tree" in relative_tree string array
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree_view), -1, col_name, tree_renderer, "text", 0, "weight", 1, NULL);
if(i == TREE_ANTONYMS)
{
gtk_tree_view_insert_column_with_attributes(tree_view, -1, STR_ANTONYM_HEADER_INDIRECT_VIA, tree_renderer, "text", 2, NULL);
tree_store = gtk_tree_store_new(3, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_STRING);
}
else
tree_store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_UINT);
gtk_tree_view_set_model(tree_view, GTK_TREE_MODEL(tree_store));
g_object_unref(tree_store);
tree_store = NULL;
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), FALSE);
g_signal_connect(tree_view, "cursor-changed", G_CALLBACK(relative_selection_changed), gui_builder);
g_signal_connect(tree_view, "row-activated", G_CALLBACK(relative_row_activated), gui_builder);
}
}
static void button_next_clicked(GtkToolButton *toolbutton, gpointer user_data)
{
GtkBuilder *gui_builder = GTK_BUILDER(user_data);
GtkComboBox *combo_query = GTK_COMBO_BOX(gtk_builder_get_object(gui_builder, COMBO_QUERY));
gint16 active_item = gtk_combo_box_get_active(combo_query);
GtkTreeModel *tree_model = NULL;
if(-1 == active_item)
{
tree_model = gtk_combo_box_get_model(combo_query);
active_item = gtk_tree_model_iter_n_children(tree_model, NULL);
}
gtk_combo_box_set_active(combo_query, active_item - 1);
}
static void button_prev_clicked(GtkToolButton *toolbutton, gpointer user_data)
{
GtkBuilder *gui_builder = GTK_BUILDER(user_data);
GtkComboBox *combo_query = GTK_COMBO_BOX(gtk_builder_get_object(gui_builder, COMBO_QUERY));
gint16 active_item = gtk_combo_box_get_active(combo_query);
// in case if the search was successful and the index is 0 (current word)
// then prev. should move it 1st element, so set active_item = 0, which + 1 will make index as 1
// else if search had failed earlier, then prev. should take it to the 0th element
// so leave it as -1; if it is something lesser than -1, make it -1
if(-1 == active_item && last_search_successful)
{
active_item = 0;
}
else if(active_item < -1)
{
active_item = -1;
}
gtk_combo_box_set_active(combo_query, active_item + 1);
}
static gboolean combo_query_scroll(GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
{
gint current_index = 0;
if((GDK_SCROLL_UP == event->direction) || (GDK_SCROLL_DOWN == event->direction))
{
GtkComboBox *combo_box = GTK_COMBO_BOX(widget);
GtkTreeModel *model = gtk_combo_box_get_model(combo_box);
gint history_count = gtk_tree_model_iter_n_children(model, NULL);
current_index = gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
if(-1 == current_index && history_count > 0) current_index = 0;
if(GDK_SCROLL_DOWN == event->direction)
{
current_index++;
if(history_count <= current_index) current_index = 0;
}
else
{
current_index--;
if(current_index < 0) current_index = history_count - 1;
}
gtk_combo_box_set_active(GTK_COMBO_BOX(widget), current_index);
return TRUE;
}
return FALSE;
}
static gboolean close_window(GtkAccelGroup *accel_group,
GObject *acceleratable,
guint keyval,
GdkModifierType modifier,
GObject *user_data)
{
GtkBuilder *gui_builder = GTK_BUILDER(user_data);
GtkWidget *main_window = GTK_WIDGET(gtk_builder_get_object(gui_builder, WINDOW_MAIN));
gboolean deleted = FALSE;
g_signal_emit_by_name(main_window, "delete-event", G_TYPE_BOOLEAN, &deleted);
return TRUE;
}
static void setup_toolbar(GtkBuilder *gui_builder)
{
GtkToolbar *toolbar = NULL;
GtkToolItem *toolbar_item = NULL;
GtkAccelGroup *accel_group = NULL;
GClosure *close_window_closure = NULL;
GClosure *close_window_closure_dummy = NULL;
// toolbar code starts here
toolbar = GTK_TOOLBAR(gtk_builder_get_object(gui_builder, TOOLBAR));
toolbar_item = gtk_tool_button_new_from_stock(GTK_STOCK_GO_BACK);
gtk_tool_button_set_use_underline(GTK_TOOL_BUTTON(toolbar_item), TRUE);
gtk_tool_button_set_label(GTK_TOOL_BUTTON(toolbar_item), STR_TOOLITEM_PREV);
gtk_tool_item_set_tooltip_text(toolbar_item, TOOLITEM_TOOLTIP_PREV);
gtk_widget_set_sensitive(GTK_WIDGET(toolbar_item), FALSE);
g_signal_connect(toolbar_item, "clicked", G_CALLBACK(button_prev_clicked), gui_builder);
gtk_toolbar_insert(toolbar, toolbar_item, -1);
toolbar_item = gtk_tool_button_new_from_stock(GTK_STOCK_GO_FORWARD);
gtk_tool_button_set_use_underline(GTK_TOOL_BUTTON(toolbar_item), TRUE);
gtk_tool_button_set_label(GTK_TOOL_BUTTON(toolbar_item), STR_TOOLITEM_NEXT);
gtk_tool_item_set_tooltip_text(toolbar_item, TOOLITEM_TOOLTIP_NEXT);
gtk_widget_set_sensitive(GTK_WIDGET(toolbar_item), FALSE);
g_signal_connect(toolbar_item, "clicked", G_CALLBACK(button_next_clicked), gui_builder);
gtk_toolbar_insert(toolbar, toolbar_item, -1);
toolbar_item = gtk_separator_tool_item_new();
gtk_toolbar_insert(toolbar, toolbar_item, -1);
toolbar_item = gtk_toggle_tool_button_new_from_stock(GTK_STOCK_DIALOG_INFO);
gtk_tool_button_set_use_underline(GTK_TOOL_BUTTON(toolbar_item), TRUE);
gtk_tool_button_set_label(GTK_TOOL_BUTTON(toolbar_item), STR_TOOLITEM_MODE);
gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(toolbar_item), advanced_mode);
gtk_tool_item_set_tooltip_text(toolbar_item, TOOLITEM_TOOLTIP_MODE);
g_signal_connect(toolbar_item, "toggled", G_CALLBACK(mode_toggled), gui_builder);
gtk_toolbar_insert(toolbar, toolbar_item, -1);
toolbar_item = gtk_tool_button_new_from_stock(GTK_STOCK_PREFERENCES);
gtk_tool_button_set_label(GTK_TOOL_BUTTON(toolbar_item), STR_TOOLITEM_OPTIONS);
gtk_tool_button_set_use_underline(GTK_TOOL_BUTTON(toolbar_item), TRUE);
gtk_tool_item_set_tooltip_text(toolbar_item, TOOLITEM_TOOLTIP_OPTIONS);
g_signal_connect(toolbar_item, "clicked", G_CALLBACK(show_settings_dialog), gui_builder);
gtk_toolbar_insert(toolbar, toolbar_item, -1);
/* if mod notify is present */
if(mod_notifier)
{
toolbar_item = gtk_toggle_tool_button_new_from_stock(notifier_enabled ? GTK_STOCK_YES : GTK_STOCK_NO);
gtk_tool_button_set_use_underline(GTK_TOOL_BUTTON(toolbar_item), TRUE);
gtk_tool_button_set_label(GTK_TOOL_BUTTON(toolbar_item), STR_TOOLITEM_NOTIFY);
gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(toolbar_item), notifier_enabled);
gtk_tool_item_set_tooltip_text(toolbar_item, TOOLITEM_TOOLTIP_NOTIFY);
g_signal_connect(toolbar_item, "toggled", G_CALLBACK(notification_toggled), gui_builder);
gtk_toolbar_insert(toolbar, toolbar_item, -1);
notify_toolbar_index = gtk_toolbar_get_item_index(toolbar, toolbar_item);
}
toolbar_item = gtk_separator_tool_item_new();
gtk_toolbar_insert(toolbar, toolbar_item, -1);
toolbar_item = gtk_tool_button_new_from_stock(GTK_STOCK_ABOUT);
gtk_tool_button_set_use_underline(GTK_TOOL_BUTTON(toolbar_item), TRUE);
gtk_tool_button_set_label(GTK_TOOL_BUTTON(toolbar_item), STR_TOOLITEM_ABOUT);
gtk_tool_item_set_tooltip_text(toolbar_item, TOOLITEM_TOOLTIP_ABOUT);
g_signal_connect(toolbar_item, "clicked", G_CALLBACK(about_activate), NULL);
gtk_toolbar_insert(toolbar, toolbar_item, -1);
toolbar_item = gtk_tool_button_new_from_stock(GTK_STOCK_QUIT);
gtk_tool_button_set_use_underline(GTK_TOOL_BUTTON(toolbar_item), TRUE);
gtk_tool_button_set_label(GTK_TOOL_BUTTON(toolbar_item), STR_TOOLITEM_QUIT);
gtk_tool_item_set_tooltip_text(toolbar_item, TOOLITEM_TOOLTIP_QUIT);
g_signal_connect(toolbar_item, "clicked", G_CALLBACK(quit_activate), NULL);
gtk_toolbar_insert(toolbar, toolbar_item, -1);
// add accelerators
GtkWindow *window = GTK_WINDOW(gtk_builder_get_object(gui_builder, WINDOW_MAIN));
accel_group = gtk_accel_group_new();
gtk_window_add_accel_group(window, accel_group);
gtk_widget_add_accelerator(GTK_WIDGET(toolbar_item),
"clicked",
accel_group,
GDK_q,
GDK_CONTROL_MASK,
0);
close_window_closure = g_cclosure_new_object(G_CALLBACK(close_window), G_OBJECT(gui_builder));
/* due to GLib implementation's limitation, a dummy closure needs to be created
refer: gtk_accel_group_connect documentation */
close_window_closure_dummy = g_cclosure_new_object(G_CALLBACK(close_window), G_OBJECT(gui_builder));
gtk_accel_group_connect(accel_group, GDK_w, GDK_CONTROL_MASK, 0, close_window_closure);
gtk_accel_group_connect(accel_group, GDK_Escape, 0, 0, close_window_closure_dummy);
gtk_widget_show_all(GTK_WIDGET(toolbar));
}
static GtkMenu *create_popup_menu(GtkBuilder *gui_builder)
{
GtkMenu *menu = NULL;
GtkSeparatorMenuItem *menu_separator = NULL;
GtkImageMenuItem *menu_item = NULL;
// Initialize popup menu/sub menus
menu = GTK_MENU(gtk_menu_new());
/* if mod notify is present, setup a notifications menu */
if(mod_notifier)
{
menu_notify = GTK_CHECK_MENU_ITEM(gtk_check_menu_item_new_with_mnemonic(STR_TOOLITEM_NOTIFY));
// load the settings value
gtk_check_menu_item_set_active(menu_notify, notifier_enabled);
menu_separator = GTK_SEPARATOR_MENU_ITEM(gtk_separator_menu_item_new());
gtk_menu_shell_append(GTK_MENU_SHELL(menu), GTK_WIDGET(menu_notify));
gtk_menu_shell_append(GTK_MENU_SHELL(menu), GTK_WIDGET(menu_separator));
g_signal_connect(menu_notify, "toggled", G_CALLBACK(notification_toggled), gui_builder);
}
menu_item = GTK_IMAGE_MENU_ITEM(gtk_image_menu_item_new_from_stock(GTK_STOCK_CLOSE, NULL));
gtk_menu_item_set_use_underline(GTK_MENU_ITEM(menu_item), TRUE);
gtk_menu_item_set_label(GTK_MENU_ITEM(menu_item), "_Hide");
g_signal_connect(menu_item, "activate", G_CALLBACK(trayicon_menu_toggled), gui_builder);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), GTK_WIDGET(menu_item));
menu_item = GTK_IMAGE_MENU_ITEM(gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL));
g_signal_connect(menu_item, "activate", G_CALLBACK(quit_activate), NULL);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), GTK_WIDGET(menu_item));
gtk_widget_show_all(GTK_WIDGET(menu));
return menu;
}
static void create_text_view_tags(GtkBuilder *gui_builder)
{
guint8 i = 0;
GtkTextView *text_view = NULL;
GtkTextBuffer *buffer = NULL;
// create text view tags
text_view = GTK_TEXT_VIEW(gtk_builder_get_object(gui_builder, TEXT_VIEW_DEFINITIONS));
buffer = gtk_text_view_get_buffer(text_view);
gtk_text_buffer_create_tag(buffer, TAG_LEMMA, "weight", PANGO_WEIGHT_BOLD, NULL);
gtk_text_buffer_create_tag(buffer, TAG_POS, "foreground", "red", "style", PANGO_STYLE_ITALIC, NULL);
gtk_text_buffer_create_tag(buffer, TAG_COUNTER, "left_margin", 15, "weight", PANGO_WEIGHT_BOLD, "foreground", "gray", NULL);
gtk_text_buffer_create_tag(buffer, TAG_EXAMPLE, "foreground", "blue", "font", "Serif Italic 10", "left_margin", 45, NULL);
gtk_text_buffer_create_tag(buffer, TAG_MATCH, "foreground", "DarkGreen", "weight", PANGO_WEIGHT_SEMIBOLD, NULL);
gtk_text_buffer_create_tag(buffer, TAG_SUGGESTION, "foreground", "blue" , "left_margin", 25, NULL);
gtk_text_buffer_create_tag(buffer, TAG_HIGHLIGHT, "background", "black", "foreground", "white", NULL);
for(i = 0; i < FAMILIARITY_COUNT; i++)
{
gtk_text_buffer_create_tag(buffer, familiarity[i], "background", freq_colors[i],
"foreground", (i < 4)? "white" : "black", "font", "Monospace 9", NULL);
}
g_signal_connect(text_view, "button-press-event", G_CALLBACK(text_view_button_pressed), gui_builder);
g_signal_connect(text_view, "button-release-event", G_CALLBACK(text_view_button_released), gui_builder);
g_signal_connect(text_view, "selection-received", G_CALLBACK(text_view_selection_made), gui_builder);
}
static void load_preference_hotkey(gboolean *first_run)
{
GError *err = NULL;
gchar *version = g_key_file_get_string(config_file, GROUP_SETTINGS, KEY_VERSION, NULL);
if(version)
{
if(0 != g_strcmp0(version, PACKAGE_VERSION))
{
/* version number is split here. Major, Minor, Micro */
gchar **version_numbers = g_strsplit(version, ".", 3);
gchar **current_version_numbers = g_strsplit(PACKAGE_VERSION, ".", 3);
for(guint8 i = 0; (NULL != version_numbers[i] && NULL != current_version_numbers[i]); i++)
{
if(g_ascii_digit_value(version_numbers[i][0]) < g_ascii_digit_value(current_version_numbers[i][0]))
{
*first_run = TRUE;
break;
}
}
g_strfreev(version_numbers);
g_strfreev(current_version_numbers);
version_numbers = current_version_numbers = NULL;
}
g_free(version);
version = NULL;
}
/* zero can be returned in three cases:
i. Older version - no key set in conf (err will be set)
ii. New version with no key in conf file set (err will be set)
iii. New version & entries present (err will not be set) */
app_hotkey.accel_key = g_key_file_get_integer(config_file, GROUP_SETTINGS, KEY_ACCEL_KEY, &err);
app_hotkey.accel_mods = g_key_file_get_integer(config_file, GROUP_SETTINGS, KEY_ACCEL_MODS, NULL);
app_hotkey.accel_flags = g_key_file_get_integer(config_file, GROUP_SETTINGS, KEY_ACCEL_FLAGS, NULL);
if(err)
{
g_error_free(err);
err = NULL;
}
else if(*first_run) /* if no error was set, then the key is present, in that case */
*first_run = FALSE; /* a first run is not right, it's just a version above 1.0.1 where hotkeys are already set */
}
static gboolean load_preferences(GtkWindow *parent)
{
gboolean first_run = FALSE;
config_file = g_key_file_new();
if(!config_file)
{
g_error("Error creating GKeyFile to load/store preferences");
}
gchar *conf_file_path = g_strconcat(g_get_user_config_dir(), G_DIR_SEPARATOR_S, PACKAGE_TARNAME, CONF_FILE_EXT, NULL);
if(g_key_file_load_from_file(config_file, conf_file_path, G_KEY_FILE_KEEP_COMMENTS, NULL))
{
advanced_mode = g_key_file_get_boolean(config_file, GROUP_SETTINGS, KEY_MODE, NULL);
notifier_enabled = g_key_file_get_boolean(config_file, GROUP_SETTINGS, KEY_NOTIFICATIONS, NULL);
GError *err = NULL;
show_polysemy = g_key_file_get_boolean(config_file, GROUP_SETTINGS, KEY_POLYSEMY, &err);
if(err)
{
show_polysemy = TRUE;
g_clear_error(&err);
}
show_trayicon = g_key_file_get_boolean(config_file, GROUP_SETTINGS, KEY_TRAYICON, &err);
if(err)
{
show_trayicon = TRUE;
g_clear_error(&err);
}
load_preference_hotkey(&first_run);
launch_minimized = g_key_file_get_boolean(config_file, GROUP_SETTINGS, KEY_LAUNCH_HIDDEN, &err);
if(err)
{
launch_minimized = TRUE;
g_clear_error(&err);
}
}
else
first_run = TRUE;
if(conf_file_path)
{
g_free(conf_file_path);
conf_file_path = NULL;
}
/* The user might never close the app. and just shut down the system.
Conf file won't be written to disk in that case. Hence save the
conf file when its a first run with the defaults. This is done
by the main func. */
return first_run;
}
/* this function uses FILE instead of GFile to workaround
* a bug in GDataOutputStream, which doesn't convert '\n'
* (C's std. new file literal)
* to the target OS's line character; while std. FILE does */
static gboolean update_history_in_file(const gchar *term)
{
gboolean update_succeeded = FALSE;
gchar *hist_file_path = g_strconcat(g_get_user_config_dir(), G_DIR_SEPARATOR_S, PACKAGE_TARNAME, HISTORY_FILE_EXT, NULL);
if(!hist_file_path)
{
g_error("Failed creating path to save history");
}
FILE *hist_file = fopen(hist_file_path, "a+");
g_free(hist_file_path);
hist_file_path = NULL;
if(hist_file)
{
update_succeeded = (fprintf(hist_file, "%s\n", term) > 0);
fclose(hist_file);
hist_file = NULL;
}
return update_succeeded;
}
static void save_preferences_to_file()
{
gsize file_len = 0;
gchar *conf_file_path = g_strconcat(g_get_user_config_dir(), G_DIR_SEPARATOR_S, PACKAGE_TARNAME, CONF_FILE_EXT, NULL);
gchar *file_contents = g_key_file_to_data(config_file, &file_len, NULL);
GError *err = NULL;
if(!conf_file_path || !file_contents)
{
g_error("Error saving preferences to file");
}
if(!g_file_set_contents(conf_file_path, file_contents, file_len, &err))
{
g_warning("Failed to save preferences to %s\nError: %s\n", conf_file_path, err->message);
g_error_free(err);
err = NULL;
}
g_free(conf_file_path);
conf_file_path = NULL;
g_free(file_contents);
file_contents = NULL;
}
static void save_preferences()
{
g_key_file_set_comment(config_file, NULL, NULL, SETTINGS_COMMENT, NULL);
g_key_file_set_string(config_file, GROUP_SETTINGS, KEY_VERSION, PACKAGE_VERSION);
g_key_file_set_integer(config_file, GROUP_SETTINGS, KEY_ACCEL_KEY, app_hotkey.accel_key);
g_key_file_set_integer(config_file, GROUP_SETTINGS, KEY_ACCEL_MODS, app_hotkey.accel_mods);
g_key_file_set_integer(config_file, GROUP_SETTINGS, KEY_ACCEL_FLAGS, app_hotkey.accel_flags);
g_key_file_set_boolean(config_file, GROUP_SETTINGS, KEY_MODE, advanced_mode);
g_key_file_set_boolean(config_file, GROUP_SETTINGS, KEY_NOTIFICATIONS, notifier_enabled);
g_key_file_set_boolean(config_file, GROUP_SETTINGS, KEY_POLYSEMY, show_polysemy);
g_key_file_set_boolean(config_file, GROUP_SETTINGS, KEY_TRAYICON, show_trayicon);
g_key_file_set_boolean(config_file, GROUP_SETTINGS, KEY_LAUNCH_HIDDEN, launch_minimized);
save_preferences_to_file();
}
static gboolean autocomplete_selected(GtkEntryCompletion *query_completion, GtkTreeModel *model, GtkTreeIter *iter, GtkButton *button)
{
gchar *match = NULL;
gtk_tree_model_get(model, iter, 0, &match, -1);
if(match)
{
GtkEntry *query_entry = GTK_ENTRY(gtk_entry_completion_get_entry(query_completion));
gtk_entry_set_text(query_entry, match);
g_free(match);
match = NULL;
gtk_button_clicked(button);
return TRUE;
}
return FALSE;
}
static void show_loading(GtkBuilder *gui_builder)
{
gchar status_msg[MAX_STATUS_MSG] = "";
GtkWindow *window = GTK_WINDOW(gtk_builder_get_object(gui_builder, WINDOW_MAIN));
GtkStatusbar *status_bar = GTK_STATUSBAR(gtk_builder_get_object(gui_builder, STATUSBAR));
// set the loading message on status bar
gtk_statusbar_pop(status_bar, status_msg_context_id);
g_snprintf(status_msg, MAX_STATUS_MSG, STR_STATUS_INDEXING);
status_msg_context_id = gtk_statusbar_get_context_id(status_bar, STATUS_DESC_LOADING_INDEX);
gtk_statusbar_push(status_bar, status_msg_context_id, status_msg);
// if visible, repaint the statusbar after setting the status message, for it to get reflected
if(gtk_widget_get_visible(GTK_WIDGET(window)))
gdk_window_process_updates(((GtkWidget*)status_bar)->window, FALSE);
}
static void attach_loaded_terms(WordnetTermsLoaderData *loader_data)
{
g_free(loader_data->index_file_contents);
loader_data->index_file_contents = NULL;
loader_data->index_file_length = loader_data->pos_in_file = 0;
loader_data->last_lemma = NULL;
GtkEntryCompletion *query_entry_completion = gtk_entry_completion_new();
gtk_entry_completion_set_model(query_entry_completion, GTK_TREE_MODEL(loader_data->completion_list));
g_object_unref(loader_data->completion_list);
loader_data->completion_list = NULL;
gtk_entry_completion_set_text_column(query_entry_completion, 0);
gtk_entry_completion_set_minimum_key_length(query_entry_completion, 3);
GtkButton *query_button = GTK_BUTTON(gtk_builder_get_object(loader_data->gui_builder, BUTTON_SEARCH));
g_signal_connect(query_entry_completion, "match-selected", G_CALLBACK(autocomplete_selected), query_button);
GtkBin *query_combo = GTK_BIN(gtk_builder_get_object(loader_data->gui_builder, COMBO_QUERY));
GtkEntry *query_entry = GTK_ENTRY(gtk_bin_get_child(query_combo));
gtk_entry_set_completion(query_entry, query_entry_completion);
// clear the loading message from status bar
GtkStatusbar *status_bar = GTK_STATUSBAR(gtk_builder_get_object(loader_data->gui_builder, STATUSBAR));
gtk_statusbar_pop(status_bar, status_msg_context_id);
}
static gboolean terms_loader(WordnetTermsLoaderData *loader_data)
{
gboolean load_pending = TRUE, is_reading = TRUE;
gchar lemma[MAX_LEMMA_LEN] = "", ch = 0;
gsize i = 0;
while((ch = loader_data->index_file_contents[loader_data->pos_in_file++]) != '\n')
{
if(is_reading)
{
if(ch == '%')
{
is_reading = FALSE;
lemma[i++] = '\n';
lemma[i] = '\0';
if(g_strcmp0(lemma, loader_data->last_lemma) != 0)
{
g_string_append(wordnet_terms, lemma);
loader_data->last_lemma = wordnet_terms->str + wordnet_terms->len - i;
lemma[i - 1] = '\0';
GtkTreeIter iter = {0};
gtk_list_store_append(loader_data->completion_list, &iter);
gtk_list_store_set(loader_data->completion_list, &iter, 0, lemma, -1);
}
}
else
{
lemma[i++] = (ch == '_') ? ' ' : ch;
}
}
}
if(loader_data->pos_in_file == loader_data->index_file_length)
{
attach_loaded_terms(loader_data);
load_pending = FALSE;
}
return load_pending;
}
static gboolean wordnet_terms_load(WordnetTermsLoaderData *loader_data)
{
gboolean succeeded = FALSE;
gchar *index_file_path = g_strdup_printf(SENSEIDXFILE, SetSearchdir());
if(g_file_get_contents(index_file_path,
&loader_data->index_file_contents,
&loader_data->index_file_length,
NULL))
{
show_loading(loader_data->gui_builder);
wordnet_terms = g_string_new("");
loader_data->last_lemma = wordnet_terms->str;
loader_data->completion_list = gtk_list_store_new(1, G_TYPE_STRING);
g_idle_add((GSourceFunc) terms_loader, loader_data);
succeeded = TRUE;
}
g_free(index_file_path);
index_file_path = NULL;
return succeeded;
}
#ifdef X11_AVAILABLE
static void lookup_ignorable_modifiers ()
{
GdkKeymap *keymap = gdk_keymap_get_default();
/* caps_lock */
egg_keymap_resolve_virtual_modifiers (keymap, EGG_VIRTUAL_LOCK_MASK, &caps_lock_mask);
/* num_lock */
egg_keymap_resolve_virtual_modifiers (keymap, EGG_VIRTUAL_NUM_LOCK_MASK, &num_lock_mask);
/* scroll_lock */
egg_keymap_resolve_virtual_modifiers (keymap, EGG_VIRTUAL_SCROLL_LOCK_MASK, &scroll_lock_mask);
}
gboolean grab_ungrab_with_ignorable_modifiers (GtkAccelKey *binding, gboolean grab)
{
guint i = 0, actual_mods = 0;
Window rootwin = XDefaultRootWindow(dpy);
Bool x_error = False;
guint mod_masks [] =
{
0, /* modifier only */
num_lock_mask,
caps_lock_mask,
scroll_lock_mask,
num_lock_mask | caps_lock_mask,
num_lock_mask | scroll_lock_mask,
caps_lock_mask | scroll_lock_mask,
num_lock_mask | caps_lock_mask | scroll_lock_mask,
};
egg_keymap_resolve_virtual_modifiers (gdk_keymap_get_default(), (EggVirtualModifierType) binding->accel_mods, &actual_mods);
for(i = 0; (i < G_N_ELEMENTS (mod_masks) && (False == x_error)); i++)
{
if(grab)
{
XGrabKey (dpy,
XKeysymToKeycode(dpy, binding->accel_key),
actual_mods | mod_masks [i],
rootwin,
False,
GrabModeAsync,
GrabModeAsync);
}
else
{
XUngrabKey (dpy,
XKeysymToKeycode(dpy, binding->accel_key),
actual_mods | mod_masks [i],
rootwin);
}
}
return !x_error;
}
#elif defined G_OS_WIN32
static gboolean try_convert_to_vk(guint gdk_key, BYTE *vk)
{
gboolean converted = FALSE;
static const gdk_vk keys[] = {
{GDK_KEY_Insert, VK_INSERT},
{GDK_KEY_Delete, VK_DELETE},
{GDK_KEY_Select, VK_SELECT},
{GDK_KEY_Print, VK_PRINT},
{GDK_KEY_Execute, VK_EXECUTE},
{GDK_KEY_Page_Up, VK_PRIOR},
{GDK_KEY_Page_Down, VK_NEXT},
{GDK_KEY_End, VK_END},
{GDK_KEY_Clear, VK_CLEAR},
{GDK_KEY_Pause, VK_PAUSE},
{GDK_KEY_Cancel, VK_CANCEL},
{GDK_KEY_Menu, VK_MENU},
{GDK_KEY_3270_PrintScreen, VK_SNAPSHOT},
{GDK_KEY_Help, VK_HELP},
{GDK_KEY_Super_L, VK_LWIN},
{GDK_KEY_Super_R, VK_RWIN},
{GDK_KEY_Sleep, VK_SLEEP},
{GDK_KEY_Back, VK_BROWSER_BACK},
{GDK_KEY_Forward, VK_BROWSER_FORWARD},
{GDK_KEY_Refresh, VK_BROWSER_REFRESH},
{GDK_KEY_Stop, VK_BROWSER_STOP},
{GDK_KEY_Search, VK_BROWSER_SEARCH},
{GDK_KEY_Favorites, VK_BROWSER_FAVORITES},
{GDK_KEY_HomePage, VK_BROWSER_HOME},
{GDK_KEY_AudioMute, VK_VOLUME_MUTE},
{GDK_KEY_AudioLowerVolume, VK_VOLUME_DOWN},
{GDK_KEY_AudioRaiseVolume, VK_VOLUME_UP},
{GDK_KEY_AudioNext, VK_MEDIA_NEXT_TRACK},
{GDK_KEY_AudioPrev, VK_MEDIA_PREV_TRACK},
{GDK_KEY_AudioStop, VK_MEDIA_STOP},
{GDK_KEY_AudioPlay, VK_MEDIA_PLAY_PAUSE},
{GDK_KEY_Mail, VK_LAUNCH_MAIL},
{GDK_KEY_3270_Play, VK_PLAY}
};
// function keys
if((gdk_key >= GDK_KEY_F1) && (gdk_key <= GDK_KEY_F24))
{
*vk = VK_F1 + gdk_key - GDK_KEY_F1;
converted = TRUE;
}
// numpad 0 to 9
else if((gdk_key >= GDK_KEY_KP_0) && (gdk_key <= GDK_KEY_KP_9))
{
*vk = VK_NUMPAD0 + gdk_key - GDK_KEY_KP_0;
converted = TRUE;
}
// home & arrow keys
else if((gdk_key >= GDK_KEY_Home) && (gdk_key <= GDK_KEY_Down))
{
*vk = VK_HOME + gdk_key - GDK_KEY_Home;
converted = TRUE;
}
// numpad multiply to divide (includes separator)
else if((gdk_key >= GDK_KEY_KP_Multiply) && (gdk_key <= GDK_KEY_KP_Divide))
{
*vk = VK_MULTIPLY + gdk_key - GDK_KEY_KP_Multiply;
converted = TRUE;
}
else
{
guint8 i = 0;
for(; i < G_N_ELEMENTS(keys); ++i)
{
if(keys[i].gdk_key == gdk_key)
{
*vk = keys[i].vk_key;
converted = TRUE;
break;
}
}
}
return converted;
}
gboolean grab_ungrab_with_ignorable_modifiers (GtkAccelKey *binding, gboolean grab)
{
gboolean reg_success = FALSE;
if(grab)
{
SHORT key = 0;
BYTE vk_key = 0;
if(try_convert_to_vk(binding->accel_key, &vk_key) || ((key = VkKeyScan(binding->accel_key)) != -1))
{
if((key != -1) && (key != 0))
{
vk_key = LOBYTE(key);
}
UINT win_mods = 0;
win_mods |= ((binding->accel_mods & GDK_SHIFT_MASK) || (HIBYTE(key) & 1)) ? MOD_SHIFT : 0;
win_mods |= ((binding->accel_mods & GDK_CONTROL_MASK) || (HIBYTE(key) & 2)) ? MOD_CONTROL : 0;
win_mods |= ((binding->accel_mods & GDK_MOD1_MASK) || (HIBYTE(key) & 4)) ? MOD_ALT : 0;
win_mods |= (binding->accel_mods & GDK_SUPER_MASK) ? MOD_WIN : 0;
reg_success = RegisterHotKey(hMainWindow, 1, win_mods, vk_key);
}
}
else
{
reg_success = UnregisterHotKey(hMainWindow, 1);
}
return reg_success;
}
#endif // X11_AVAILABLE
static gboolean register_unregister_hotkey(gboolean first_run, gboolean setup_hotkey)
{
guint i = 0;
/* if it's first run and hotkey needs to be setup; try with the preset hotkey trials
else just grab or ungrab as per the last param passed */
if(first_run && setup_hotkey)
{
app_hotkey.accel_mods = GDK_CONTROL_MASK | GDK_MOD1_MASK;
for(i = 0; i < G_N_ELEMENTS(hotkey_trials); ++i)
{
app_hotkey.accel_key = hotkey_trials[i];
if(grab_ungrab_with_ignorable_modifiers(&app_hotkey, TRUE))
return TRUE;
}
/* when control reaches here, it means hotkey couldn't be set; but since
it's first run, save_pref will be called and whatever value is present
in app_hotkey (last key in hotkey_trials) will be saved; so 0 it */
app_hotkey.accel_key = app_hotkey.accel_mods = app_hotkey.accel_flags = 0;
return FALSE;
}
return(grab_ungrab_with_ignorable_modifiers(&app_hotkey, setup_hotkey));
}
static void setup_settings_dialog(GtkBuilder *gui_builder)
{
GtkLabel *hotkey_label = GTK_LABEL(gtk_builder_get_object(gui_builder, LABEL_HOTKEY));
GtkContainer *hbox = GTK_CONTAINER(gtk_builder_get_object(gui_builder, HBOX_HOTKEY));
GtkDialog *settings_dialog = GTK_DIALOG(gtk_builder_get_object(gui_builder, DIALOG_OPTIONS));
GtkWidget *hotkey_accel_cell = create_hotkey_editor();
gtk_container_add(hbox, hotkey_accel_cell);
gtk_label_set_mnemonic_widget(hotkey_label, hotkey_accel_cell);
g_signal_connect(GTK_WIDGET(settings_dialog), "response", G_CALLBACK(gtk_widget_hide), NULL);
set_settings_to_check_boxes(gui_builder);
}
static void set_settings_to_check_boxes(GtkBuilder *gui_builder)
{
GtkToggleButton *polysemy_show_btn = GTK_TOGGLE_BUTTON(gtk_builder_get_object(gui_builder, CHKBTN_POLYSEMY));
GtkToggleButton *status_show_btn = GTK_TOGGLE_BUTTON(gtk_builder_get_object(gui_builder, CHKBTN_STATUS));
GtkToggleButton *launch_min_btn = GTK_TOGGLE_BUTTON(gtk_builder_get_object(gui_builder, CHKBTN_LAUNCH_MIN));
// show_trayicon is the variable, while the checkbox is "hide status icon"
// hence the NOT op.; same goes for launch_minimized too
gtk_toggle_button_set_active(status_show_btn, !show_trayicon);
gtk_toggle_button_set_active(launch_min_btn, !launch_minimized);
gtk_toggle_button_set_active(polysemy_show_btn, show_polysemy);
}
static void get_settings_from_check_boxes(GtkBuilder *gui_builder,
gboolean *new_trayicon_show,
gboolean *new_launch_min,
gboolean *new_polysemy_show)
{
GtkToggleButton *polysemy_show_btn = GTK_TOGGLE_BUTTON(gtk_builder_get_object(gui_builder, CHKBTN_POLYSEMY));
GtkToggleButton *status_show_btn = GTK_TOGGLE_BUTTON(gtk_builder_get_object(gui_builder, CHKBTN_STATUS));
GtkToggleButton *launch_min_btn = GTK_TOGGLE_BUTTON(gtk_builder_get_object(gui_builder, CHKBTN_LAUNCH_MIN));
// show_trayicon is the variable, while the checkbox is "hide status icon"
// hence the NOT op.; same goes for launch_minimized
*new_trayicon_show = !gtk_toggle_button_get_active(status_show_btn);
*new_launch_min = !gtk_toggle_button_get_active(launch_min_btn);
*new_polysemy_show = gtk_toggle_button_get_active(polysemy_show_btn);
}
static void apply_and_save_settings(GtkBuilder *gui_builder)
{
GtkButton *search_button = GTK_BUTTON(gtk_builder_get_object(gui_builder, BUTTON_SEARCH));
gboolean new_show_trayicon = FALSE, new_show_polysemy = FALSE;
get_settings_from_check_boxes(gui_builder, &new_show_trayicon, &launch_minimized, &new_show_polysemy);
if(show_polysemy != new_show_polysemy)
{
show_polysemy = new_show_polysemy;
last_search_successful = FALSE;
gtk_button_clicked(search_button);
}
if(show_trayicon != new_show_trayicon)
{
GtkStatusIcon *status_icon = GTK_STATUS_ICON(gtk_builder_get_object(gui_builder, STATUS_ICON));
show_trayicon = new_show_trayicon;
gtk_status_icon_set_visible(status_icon, show_trayicon);
}
save_preferences();
}
/* flags when changed and reverted don't affect, since only Apply code makes permanent
* changes, while hotkey alone is an exception to this; when changed, it captures the
* hotkey changed to; hence when reverted, this alone should be undone and the old
* hotkey should be set again
*/
static void revert_settings(GtkBuilder *gui_builder, GtkAccelKey *hotkey_backup)
{
/* if prev. hotkey and app_hotkey are not the same and 'Apply' wasn't clicked
then revert hotkey before saving the preferences */
if(hotkey_backup->accel_key != app_hotkey.accel_key ||
hotkey_backup->accel_mods != app_hotkey.accel_mods ||
hotkey_backup->accel_flags != app_hotkey.accel_flags)
{
grab_ungrab_with_ignorable_modifiers(&app_hotkey, FALSE);
if(grab_ungrab_with_ignorable_modifiers(hotkey_backup, TRUE))
app_hotkey = *hotkey_backup;
else
{
show_message_dlg(NULL, MSG_HOTKEY_FAILED);
memset(&app_hotkey, 0, sizeof(GtkAccelKey));
}
}
}
static void show_settings_dialog(GtkToolButton *toolbutton, gpointer user_data)
{
GtkAccelKey hotkey_backup = app_hotkey;
GtkBuilder *gui_builder = GTK_BUILDER(user_data);
GtkDialog *settings_dialog = GTK_DIALOG(gtk_builder_get_object(gui_builder, DIALOG_OPTIONS));
gint hotkey_dialog_response = 0;
// load settings from variables to check boxes
/* update the old value on the controls
* this is done for a scenario where the user changes the form value and presses cancel
* in the next launch of the Options dialog, the old 'Cancel'ed form states shouldn't be there
*/
set_settings_to_check_boxes(gui_builder);
hotkey_dialog_response = gtk_dialog_run(settings_dialog);
if(GTK_RESPONSE_APPLY == hotkey_dialog_response)
{
apply_and_save_settings(gui_builder);
}
else
{
revert_settings(gui_builder, &hotkey_backup);
}
/* set hotkey flag */
hotkey_set = (gboolean) app_hotkey.accel_key;
}
static void destructor(GtkBuilder *gui_builder)
{
#ifdef G_OS_WIN32
GtkWidget *window = GTK_WIDGET(gtk_builder_get_object(gui_builder, WINDOW_MAIN));
#endif
if(config_file)
{
g_key_file_free(config_file);
config_file = NULL;
}
if(wordnet_terms)
{
g_string_free(wordnet_terms, TRUE);
wordnet_terms = NULL;
}
/* be responsible, give back the OS what you got from it :) */
if(results)
{
wni_free(&results);
results = NULL;
}
/* if hotkey is registered, unregister it */
if(hotkey_set)
{
// remove the preset event filter
#ifdef X11_AVAILABLE
gdk_window_remove_filter(gdk_get_default_root_window(), hotkey_pressed, gui_builder);
#elif defined G_OS_WIN32
gdk_window_remove_filter(window->window, hotkey_pressed, gui_builder);
#endif
register_unregister_hotkey(FALSE, FALSE);
hotkey_set = FALSE;
}
if(mod_suggest)
{
suggestions_uninit();
mod_suggest = FALSE;
}
if(mod_notifier)
{
mod_notify_uninit();
}
}
static void show_message_dlg(GtkWidget *parent_window, MessageResposeCode msg_code)
{
GtkWidget *msg_dialog = NULL;
GtkMessageType msg_type = GTK_MESSAGE_INFO;
gchar *str_body = NULL;
gchar *str_temp1 = NULL, *str_temp2 = NULL;
if(MSG_DB_LOAD_ERROR == msg_code)
{
str_body = STR_MSG_WN_ERROR;
msg_type = GTK_MESSAGE_ERROR;
}
else if(MSG_HOTKEY_SUCCEEDED_FIRST_RUN == msg_code)
{
str_temp1 = gtk_accelerator_get_label(app_hotkey.accel_key, app_hotkey.accel_mods);
str_temp2 = g_strdup_printf(STR_MSG_WELCOME_ARBITRARY_SUCCEEDED, str_temp1);
g_free(str_temp1); str_temp1 = NULL;
str_body = str_temp1 = g_strdup_printf("%s\n\n%s %s",
STR_MSG_WELCOME_HOTKEY_HEADER,
str_temp2,
STR_MSG_WELCOME_HOTKEY_FOOTER);
}
else if(MSG_HOTKEY_FAILED_FIRST_RUN == msg_code)
{
str_body = str_temp1 = g_strdup_printf("%s\n%s",
STR_MSG_WELCOME_HOTKEY_HEADER,
STR_MSG_WELCOME_HOTKEY_FOOTER);
}
else if(MSG_HOTKEY_FAILED == msg_code)
{
str_temp1 = gtk_accelerator_get_label(app_hotkey.accel_key, app_hotkey.accel_mods);
str_temp2 = g_strdup_printf(STR_MSG_WELCOME_HOTKEY_FAILED, str_temp1);
g_free(str_temp1); str_temp1 = NULL;
str_body = str_temp1 = g_strdup_printf("%s %s", str_temp2, STR_MSG_WELCOME_HOTKEY_FOOTER);
msg_type = GTK_MESSAGE_WARNING;
}
else if(MSG_HOTKEY_NOTSET == msg_code)
{
str_body = STR_MSG_HOTKEY_NOTSET;
msg_type = GTK_MESSAGE_WARNING;
}
msg_dialog = gtk_message_dialog_new_with_markup(parent_window ? GTK_WINDOW(parent_window) : NULL,
GTK_DIALOG_MODAL,
msg_type,
GTK_BUTTONS_OK,
str_body, NULL);
g_object_set(msg_dialog, "title", PACKAGE_NAME, NULL);
gtk_dialog_run(GTK_DIALOG(msg_dialog));
gtk_widget_destroy(msg_dialog);
if(str_temp1)
{
g_free(str_temp1);
str_temp1 = NULL;
}
if(str_temp2)
{
g_free(str_temp2);
str_temp2 = NULL;
}
}
static gboolean window_visibility_toggled(GtkWidget *widget, GdkEventVisibility *event, gpointer user_data)
{
/* when the window becomes fully visible, make sure a proper lookup is made
if the last lookup was a notification i.e. a light lookup */
if(GDK_VISIBILITY_UNOBSCURED == event->state && last_lookup_a_notification)
{
last_search_successful = FALSE;
gtk_button_clicked(GTK_BUTTON(user_data));
}
return FALSE;
}
int main(int argc, char *argv[])
{
GtkBuilder *gui_builder = NULL;
GtkWidget *window = NULL, *button_search = NULL, *combo_query = NULL, *combo_entry = NULL, *settings_dialog = NULL;
GtkMenu *popup_menu = NULL;
GtkExpander *expander = NULL;
GdkPixbuf *app_icon = NULL;
GError *err = NULL;
gboolean first_run = FALSE, hotkey_reg_failed = FALSE;
gchar *ui_file_path = NULL, *icon_file_path = NULL;
#ifdef G_OS_WIN32
/* logic for single instance apps. on Win32 */
HANDLE single_instance_mutex = CreateMutex(NULL, FALSE, TEXT(PACKAGE_NAME));
if(ERROR_ALREADY_EXISTS == GetLastError() || !single_instance_mutex)
{
hMainWindow = FindWindow(NULL, TEXT(STR_APP_TITLE));
if(hMainWindow)
PostMessage(hMainWindow, WM_ARTHA_RELAUNCH, 0, 0);
return 0;
}
#endif
g_set_application_name(PACKAGE_NAME);
if(gtk_init_check(&argc, &argv))
{
#ifdef DBUS_AVAILABLE
/* if we're not the first instance of artha, then quit */
if(FALSE == instance_handler_am_i_unique())
{
/* signal the primary instance of this invocation */
instance_handler_send_signal();
/* notify the desktop env. that the start up is complete */
gdk_notify_startup_complete();
return 0;
}
#endif
gui_builder = gtk_builder_new();
if(gui_builder)
{
ui_file_path = g_build_filename(APP_DIR, UI_FILE, NULL);
if(gtk_builder_add_from_file(gui_builder, ui_file_path, &err))
{
/* if WordNet's database files are not opened, open it */
if((OpenDB != 1) && (wninit() != 0))
show_message_dlg(NULL, MSG_DB_LOAD_ERROR);
window = GTK_WIDGET(gtk_builder_get_object(gui_builder, WINDOW_MAIN));
if(window)
{
GtkStatusIcon *status_icon = NULL;
#ifdef DBUS_AVAILABLE
/* if the control's here, then it's sure that we're the unique instance;
register for dbus signals from possible duplicate instances */
if(!instance_handler_register_signal(GTK_WINDOW(window)))
{
g_error("Unable to register for duplicate instance signals!\n");
}
#endif
/* try to load preferences and see if this is the first run */
first_run = load_preferences(GTK_WINDOW(window));
#ifdef X11_AVAILABLE
/* Most Important: do not use the XServer's XGetDisplay, you will have to do XNextEvent (blocking) to
get the event call, so get GDK's display; its X11 display equivalent */
dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default());
if(NULL == dpy)
{
g_error("Can't open Display %s!\n", gdk_display_get_name(gdk_display_get_default()));
return -1;
}
G_DEBUG("XDisplay Opened!\n");
XSynchronize(dpy, True); /* Without calling this you get the error call back dealyed, much delayed! */
XSetErrorHandler(x_error_handler); /* Set the error handler for setting the flag */
lookup_ignorable_modifiers();
/* Add a filter function to handle low level events, like X events.
For Param1 use gdk_get_default_root_window() instead of NULL or window, so that
only when hotkey combo is pressed will the filter func. be called, unlike
others, where it will be called for all events beforehand GTK handles */
gdk_window_add_filter(gdk_get_default_root_window(), hotkey_pressed, gui_builder);
#elif defined G_OS_WIN32
/* if the main window widget is not yet realised, then the underlying
GdkWindow will be a NULL member, so realise it and get the HWND */
if(!window->window)
gtk_widget_realize(window);
hMainWindow = (HWND) GDK_WINDOW_HWND(window->window);
gdk_window_add_filter(window->window, hotkey_pressed, gui_builder);
#endif // X11_AVAILABLE
if(app_hotkey.accel_key || first_run)
{
if(register_unregister_hotkey(first_run, TRUE))
{
hotkey_set = TRUE;
if(first_run)
{
show_message_dlg(window, MSG_HOTKEY_SUCCEEDED_FIRST_RUN);
}
}
else
{
if(first_run)
show_message_dlg(window, MSG_HOTKEY_FAILED_FIRST_RUN);
else
{
hotkey_reg_failed = TRUE;
show_message_dlg(window, MSG_HOTKEY_FAILED);
/* since the previously set hotkey is now not registerable, memzero
(i.e. disable) app_hotkey and save prefs */
app_hotkey.accel_key = app_hotkey.accel_mods = (GdkModifierType) (app_hotkey.accel_flags = 0);
}
}
}
/* save preferences here - after all setting based loads are done */
save_preferences();
setup_settings_dialog(gui_builder);
settings_dialog = GTK_WIDGET(gtk_builder_get_object(gui_builder, DIALOG_OPTIONS));
icon_file_path = g_build_filename(ICON_DIR, ICON_FILE, NULL);
if(g_file_test(icon_file_path, G_FILE_TEST_IS_REGULAR))
{
status_icon = GTK_STATUS_ICON(gtk_builder_get_object(gui_builder, STATUS_ICON));
gtk_status_icon_set_from_file(status_icon, icon_file_path);
gtk_status_icon_set_tooltip_text(status_icon, STR_APP_TITLE);
gtk_status_icon_set_visible(status_icon, show_trayicon);
}
else
{
g_warning("Error loading icon file!\n%s not found!\n", icon_file_path);
}
g_free(icon_file_path);
icon_file_path = NULL;
mod_notify_init();
setup_toolbar(gui_builder);
/* pop-up menu creation should be after assessing the availability of notifications
since if it is not available, the Notify menu option can be stripped
create pop-up menu */
popup_menu = create_popup_menu(gui_builder);
/* status icon connections made here since popup menu should be ready for this
* which wouldn't be ready when status_icon is inited */
if(status_icon)
{
g_signal_connect(status_icon, "activate", G_CALLBACK(status_icon_activate), gui_builder);
g_signal_connect(status_icon, "popup-menu", G_CALLBACK(status_icon_popup), GTK_WIDGET(popup_menu));
}
// using the status icon, app. icon is also set
g_object_get(status_icon, "pixbuf", &app_icon, NULL);
if(app_icon)
{
gtk_window_set_default_icon(app_icon);
g_object_unref(app_icon);
app_icon = NULL;
}
button_search = GTK_WIDGET(gtk_builder_get_object(gui_builder, BUTTON_SEARCH));
g_signal_connect(button_search, "clicked", G_CALLBACK(button_search_click), gui_builder);
combo_query = GTK_WIDGET(gtk_builder_get_object(gui_builder, COMBO_QUERY));
g_signal_connect(combo_query, "changed", G_CALLBACK(combo_query_changed), gui_builder);
/* get the GtkEntry in GtkComboBox and set activates-default to TRUE; so that
it pass the ENTER key signal to query button */
combo_entry = gtk_bin_get_child(GTK_BIN(combo_query));
g_object_set(combo_entry, "activates-default", TRUE, NULL);
create_text_view_tags(gui_builder);
/* do main window specific connects */
g_signal_connect(window, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
g_signal_connect(window, "visibility-notify-event", G_CALLBACK(window_visibility_toggled), button_search);
g_signal_connect(combo_query, "scroll-event", G_CALLBACK(combo_query_scroll), button_search);
expander = GTK_EXPANDER(gtk_builder_get_object(gui_builder, EXPANDER));
g_signal_connect(expander, "activate", G_CALLBACK(expander_clicked), gui_builder);
gtk_widget_grab_focus(GTK_WIDGET(combo_query));
G_DEBUG("GUI loaded successfully!\n");
create_stores_renderers(gui_builder);
mod_suggest = suggestions_init();
// show the window if it's a first run or a hotkey couldn't be set
// if the window is not shown, set notify the startup is complete
if(first_run || hotkey_reg_failed || !launch_minimized)
gtk_widget_show_all(window);
else
gdk_notify_startup_complete();
// index all wordnet terms from the index.sense onto memory
WordnetTermsLoaderData loader_data = { gui_builder };
wordnet_terms_load(&loader_data);
gtk_main();
/* on Win32 platform, icon stays even after app close, this is
a workaround to fix that */
gtk_status_icon_set_visible(status_icon, FALSE);
destructor(gui_builder);
/* GtkBuilder drops references to any held, except toplevel widgets */
gtk_widget_destroy(GTK_WIDGET(popup_menu));
gtk_widget_destroy(settings_dialog);
gtk_widget_destroy(window);
}
else
{
g_error("Error loading GUI!\n Corrupted data in UI file!\n");
}
}
else
{
/* shouldn't call g_error directly, since it's a terminating call;
so print the error, free the resources and then do it */
g_print("%s: ", err->message);
g_error_free(err);
err = NULL;
g_error("Error loading GUI from %s!\n", ui_file_path);
}
g_free(ui_file_path);
ui_file_path = NULL;
}
else
{
g_error("Error creating GtkBuilder!\n");
}
}
else
{
g_error("Error initializing GUI!\n");
}
#ifdef G_OS_WIN32
CloseHandle(single_instance_mutex);
#endif
return 0;
}