/* This file is part of GNUnet (C) 2011, 2012 Christian Grothoff (and other contributing authors) GNUnet 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, or (at your option) any later version. GNUnet 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 GNUnet; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /** * @file src/fs/gnunet-fs-gtk_main-window-namespace-dropdown.c * @author LRN * @brief event handlers for the namespace selection dropdown box in the main window */ #include "gnunet-fs-gtk_common.h" #include "gnunet-fs-gtk.h" #include "gnunet-fs-gtk_event-handler.h" /** * How long until we automatically hide the drop-down if the cursor is outside the bounds? */ #define AUTO_HIDE_TIMEOUT_MS 100 /** * ID of a timeout task which we schedule to close the drop-down automatically * if the mouse leaves the area for a while. 0 for no such task. * * FIXME-BUG-MINOR: this task is not cancelled if the main window is closed while * the drop-down is down. */ static guint namespace_selector_window_leave_timeout_source; /** * The mouse has re-entered the dropdown window. Stop the * timeout task that would hide the dropdown. * * @param widget the dropdown widget * @param event the mouse-enter event * @param user_data the context for the main window */ gboolean GNUNET_FS_GTK_search_namespace_dropdown_button_enter_notify_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data) { if (namespace_selector_window_leave_timeout_source > 0) g_source_remove (namespace_selector_window_leave_timeout_source); namespace_selector_window_leave_timeout_source = 0; return FALSE; } /** * Run when the timeout has expired. Hides the drop down window. * * @param user_data the toggle button which we will use to hide the dropdown * @return FALSE */ static gboolean namespace_selector_window_leave_timeout_cb (gpointer user_data) { GtkToggleButton *toggle_button = GTK_TOGGLE_BUTTON (user_data); /* This will eventually hide the namespace selector */ namespace_selector_window_leave_timeout_source = 0; gtk_toggle_button_set_active (toggle_button, FALSE); return FALSE; } /** * The cursor has left the window. Place a timeout to hide the * window. It will be cancelled if the cursor re-enters the namespace * selector window or the toggle button within 100ms * * @param user_data the context for the main window */ gboolean GNUNET_FS_GTK_search_namespace_selector_window_leave_notify_event_cb (GtkWidget * widget, GdkEvent * event, gpointer user_data) { struct GNUNET_GTK_MainWindowContext *main_ctx = user_data; if (namespace_selector_window_leave_timeout_source > 0) g_source_remove (namespace_selector_window_leave_timeout_source); namespace_selector_window_leave_timeout_source = g_timeout_add (AUTO_HIDE_TIMEOUT_MS, &namespace_selector_window_leave_timeout_cb, main_ctx->ns_dropdown_button); return FALSE; } /** * Given a tree view, find out which row is currently selected. * * @param tree a tree view instance * @return a reference to the currently selected row, or NULL for none */ static GtkTreeRowReference * get_selected_row_from_treeview (GtkTreeView * tree) { GtkTreeSelection *sel; GtkTreeModel *model; GtkTreePath *path; GtkTreeIter iter; GtkTreeRowReference *ref; sel = gtk_tree_view_get_selection (tree); if (! gtk_tree_selection_get_selected (sel, &model, &iter)) return NULL; path = gtk_tree_model_get_path (model, &iter); ref = gtk_tree_row_reference_new (model, path); gtk_tree_path_free (path); return ref; } /** * Changes were made to the selected entry in the tree view and the * user clicked to confirm. Hide the drop down and display the * selected entry as the new namespace label. * * @param main_ctx the context for the main window * @param tv the tree view that was updated */ static void commit_changes (struct GNUNET_GTK_MainWindowContext *main_ctx, GtkTreeView *tv) { GtkTreePath *treepath; gchar *value; if (NULL != main_ctx->selected_ns_row) gtk_tree_row_reference_free (main_ctx->selected_ns_row); main_ctx->selected_ns_row = get_selected_row_from_treeview (tv); treepath = gtk_tree_row_reference_get_path (main_ctx->selected_ns_row); if (GNUNET_GTK_get_tree_string (tv, treepath, 0, &value)) { gtk_label_set_text (main_ctx->search_ns_label, (NULL != value) ? value : ""); g_free (value); } if (GNUNET_GTK_get_tree_string (tv, treepath, 2, &value)) { gtk_entry_set_text (main_ctx->search_entry, (NULL != value) ? value : ""); g_free (value); } gtk_tree_path_free (treepath); /* hide the namespace selector */ gtk_toggle_button_set_active (main_ctx->ns_dropdown_button, FALSE); } /** * User pushed the button in the treeview. Get the selected entry * and remember it in the "pushed-rowreference" of the widget. * This way, we can use it when the button is released. * * @param widget the tree view widget * @param event the push event * @param user_data the context for the main window * @return FALSE */ gboolean GNUNET_FS_GTK_namespace_selector_treeview_button_press_event_cb (GtkWidget * widget, GdkEvent * event, gpointer user_data) { struct GNUNET_GTK_MainWindowContext *main_ctx = user_data; if (NULL != main_ctx->ns_selector_pushed_row) gtk_tree_row_reference_free (main_ctx->ns_selector_pushed_row); main_ctx->ns_selector_pushed_row = get_selected_row_from_treeview (GTK_TREE_VIEW (widget)); return FALSE; } /** * User released the button in the treeview. Get the selected entry * and update the cursor accordingly, but only if the user pushed the * button down and released it in the same row. * * @param widget the tree view widget * @param event the release event * @param user_data the context for the main window * @return FALSE */ gboolean GNUNET_FS_GTK_namespace_selector_treeview_button_release_event_cb (GtkWidget * widget, GdkEvent * event, gpointer user_data) { struct GNUNET_GTK_MainWindowContext *main_ctx = user_data; GtkTreeRowReference *ref; ref = get_selected_row_from_treeview (GTK_TREE_VIEW (widget)); if ( (NULL != ref) && (NULL != main_ctx->ns_selector_pushed_row)) { GtkTreePath *path_ref; GtkTreePath *path_old; path_ref = gtk_tree_row_reference_get_path (ref); path_old = gtk_tree_row_reference_get_path (main_ctx->ns_selector_pushed_row); if (0 == gtk_tree_path_compare (path_ref, path_old)) commit_changes (main_ctx, GTK_TREE_VIEW (widget)); if (path_ref) gtk_tree_path_free (path_ref); if (path_old) gtk_tree_path_free (path_old); } if (NULL != ref) gtk_tree_row_reference_free (ref); if (NULL != main_ctx->ns_selector_pushed_row) gtk_tree_row_reference_free (main_ctx->ns_selector_pushed_row); main_ctx->ns_selector_pushed_row = NULL; return FALSE; } /** * The toggle button that changes the visibility of the namespace dropdown * list was toggled. * * @param togglebutton the button that toggles the namespace dropdown list * @param user_data the contexxt for the main window */ void GNUNET_FS_GTK_search_namespace_dropdown_button_toggled_cb (GtkToggleButton * togglebutton, gpointer user_data) { struct GNUNET_GTK_MainWindowContext *main_ctx = user_data; gboolean active; GtkAllocation togglebutton_allocation; GdkWindow *main_window_gdk; gint mwg_x; gint mwg_y; gint tgb_x; gint tgb_y; gint popup_x; gint popup_y; g_object_get (G_OBJECT (togglebutton), "active", &active, NULL); if (! active) { gtk_widget_hide (main_ctx->ns_selector_window); gtk_widget_grab_focus (GTK_WIDGET (togglebutton)); return; } gtk_widget_get_allocation (GTK_WIDGET (togglebutton), &togglebutton_allocation); main_window_gdk = gtk_widget_get_window (GTK_WIDGET (togglebutton)); gdk_window_get_origin (main_window_gdk, &mwg_x, &mwg_y); /* show the window below the button */ tgb_x = mwg_x + togglebutton_allocation.x; tgb_y = mwg_y + togglebutton_allocation.y; popup_x = tgb_x; popup_y = tgb_y + togglebutton_allocation.height; gtk_window_move (GTK_WINDOW (main_ctx->ns_selector_window), popup_x, popup_y); gtk_widget_show_all (main_ctx->ns_selector_window); gtk_widget_grab_focus (GTK_WIDGET (main_ctx->ns_selector_treeview)); } /** * Add pseudonym data to tree store * * @param cls closure (the 'GtkListStore') * @param pseudonym hash code of public key of pseudonym * @param md meta data known about the pseudonym * @param rating the local rating of the pseudonym * @return GNUNET_OK to continue iteration, GNUNET_SYSERR to abort */ static int add_namespace_to_ts (void *cls, const GNUNET_HashCode * pseudonym, const char *name, const char *unique_name, const struct GNUNET_CONTAINER_MetaData *md, int rating) { GtkTreeStore *ts = cls; char *root; char *ns_name, *unique_ns_name; GNUNET_HashCode *nsid; char *description; int desc_is_a_dup; char *uris; char *emsg; struct GNUNET_FS_Uri *uri; GtkTreeIter iter; if (rating < 0) return GNUNET_OK; GNUNET_PSEUDONYM_get_info (GNUNET_FS_GTK_get_configuration (), pseudonym, NULL, NULL, &ns_name, NULL); unique_ns_name = GNUNET_PSEUDONYM_name_uniquify ( GNUNET_FS_GTK_get_configuration (), pseudonym, ns_name, NULL); GNUNET_free (ns_name); nsid = GNUNET_malloc (sizeof (GNUNET_HashCode)); *nsid = *pseudonym; root = NULL; uris = GNUNET_CONTAINER_meta_data_get_by_type (md, EXTRACTOR_METATYPE_URI); if (uris != NULL) { emsg = NULL; uri = GNUNET_FS_uri_parse (uris, &emsg); if (uri == NULL) GNUNET_free (emsg); root = GNUNET_FS_uri_sks_get_content_id (uri); GNUNET_FS_uri_destroy (uri); } description = GNUNET_FS_GTK_get_description_from_metadata (md, &desc_is_a_dup); gtk_tree_store_insert_with_values (ts, &iter, NULL, G_MAXINT, 0, unique_ns_name, 1, nsid, 2, root, 3, description, -1); GNUNET_free (unique_ns_name); GNUNET_free_non_null (root); GNUNET_free (description); return GNUNET_OK; } void GNUNET_GTK_main_window_refresh_ns_list (struct GNUNET_GTK_MainWindowContext *main_ctx) { GtkTreeIter iter; gint i; GtkTreePath *treepath; GNUNET_HashCode *key = NULL, *selected_ns_id; gboolean found = FALSE; gchar *value = NULL; if (NULL != main_ctx->selected_ns_row) { GtkTreeModel *model; treepath = gtk_tree_row_reference_get_path (main_ctx->selected_ns_row); model = gtk_tree_view_get_model (main_ctx->ns_selector_treeview); if (model) { if (gtk_tree_model_get_iter (model, &iter, treepath)) { gtk_tree_model_get (model, &iter, 1, &key, -1); } } gtk_tree_path_free (treepath); gtk_tree_row_reference_free (main_ctx->selected_ns_row); main_ctx->selected_ns_row = NULL; } selected_ns_id = NULL; if (key != NULL) { selected_ns_id = GNUNET_malloc (sizeof (GNUNET_HashCode)); memcpy (selected_ns_id, key, sizeof (GNUNET_HashCode)); } if (TRUE == gtk_tree_model_get_iter_first (GTK_TREE_MODEL ( main_ctx->search_ns_treestore), &iter)) { while (TRUE) { gtk_tree_model_get (GTK_TREE_MODEL (main_ctx->search_ns_treestore), &iter, 1, &key, -1); GNUNET_free_non_null (key); if (TRUE != gtk_tree_model_iter_next (GTK_TREE_MODEL ( main_ctx->search_ns_treestore), &iter)) break; } } gtk_tree_store_clear (main_ctx->search_ns_treestore); gtk_tree_store_insert_with_values (main_ctx->search_ns_treestore, &iter, NULL, G_MAXINT, 0, "Any", 1, NULL, 2, "", 3, "Do not search in any particular namespace", -1); if (GNUNET_YES == main_ctx->ns_callback_registered) GNUNET_PSEUDONYM_discovery_callback_unregister (add_namespace_to_ts, main_ctx->search_ns_treestore); GNUNET_PSEUDONYM_discovery_callback_register (main_ctx->cfg, add_namespace_to_ts, main_ctx->search_ns_treestore); main_ctx->ns_callback_registered = GNUNET_YES; if (TRUE == gtk_tree_model_get_iter_first (GTK_TREE_MODEL ( main_ctx->search_ns_treestore), &iter)) { while (TRUE) { gtk_tree_model_get (GTK_TREE_MODEL (main_ctx->search_ns_treestore), &iter, 0, &value, 1, &key, -1); if (selected_ns_id == NULL) found = TRUE; else if (key != NULL && memcmp (key, selected_ns_id, sizeof (GNUNET_HashCode)) == 0) found = TRUE; if (found || (TRUE != gtk_tree_model_iter_next (GTK_TREE_MODEL ( main_ctx->search_ns_treestore), &iter))) break; else g_free (value); } } if (!found) { gtk_tree_model_get_iter_first (GTK_TREE_MODEL ( main_ctx->search_ns_treestore), &iter); gtk_tree_model_get (GTK_TREE_MODEL (main_ctx->search_ns_treestore), &iter, 0, &value, 1, &key, -1); found = TRUE; } gtk_tree_selection_select_iter (gtk_tree_view_get_selection (main_ctx->ns_selector_treeview), &iter); if (value != NULL) gtk_label_set_text (main_ctx->search_ns_label, value); g_free(value); GNUNET_free_non_null (selected_ns_id); } /* end of gnunet-fs-gtk_main-window-namespace-dropdown.c */