/* This file is part of GNUnet. (C) 2005, 2006, 2007, 2008 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/plugins/fs/search.c * @brief code for searching with gnunet-gtk * @author Christian Grothoff */ #include "platform.h" #include "gnunetgtk_common.h" #include "search.h" #include "status.h" #include "helper.h" #include "fs.h" #include "meta.h" #include #include #include #ifdef HAVE_GIO #include #endif /** * The user has edited the search entry. * Update search button status. */ static void on_fssearchSelectionChanged (gpointer signal, gpointer cls) { SearchList *list = cls; GtkTreeSelection *selection; GtkWidget *downloadButton; selection = gtk_tree_view_get_selection (list->treeview); downloadButton = glade_xml_get_widget (list->searchXML, "downloadButton"); gtk_widget_set_sensitive (downloadButton, gtk_tree_selection_count_selected_rows (selection) > 0); } /* **************** FSUI event handling ****************** */ /** * Update the number of results received for the given * search in the summary and in the label of the tab. */ static void updateSearchSummary (SearchList * searchContext) { GtkTreePath *path; GtkTreeIter iter; char *new_title; GtkLabel *label; path = gtk_tree_row_reference_get_path (searchContext->summaryViewRowReference); if (TRUE != gtk_tree_model_get_iter (GTK_TREE_MODEL (search_summary), &iter, path)) { GNUNET_GE_BREAK (ectx, 0); return; } gtk_tree_path_free (path); gtk_list_store_set (search_summary, &iter, SEARCH_SUMMARY_RESULT_COUNT, searchContext->resultsReceived, -1); /* update tab title with the number of results */ new_title = g_strdup_printf ("%.*s%s (%u)", 20, searchContext->searchString, strlen (searchContext->searchString) > 20 ? "..." : "", searchContext->resultsReceived); label = GTK_LABEL (glade_xml_get_widget (searchContext->labelXML, "searchTabLabel")); gtk_label_set (label, new_title); GNUNET_free (new_title); } static GdkPixbuf * make_ranking_pixbuf (int availability_rank, unsigned int availability_certainty, unsigned int applicability_rank, unsigned int kwords) { GdkPixbuf *pixbuf; guchar *pixels; guchar *pixel; int n_channels; int rowstride; unsigned int x; unsigned int y; #define P_HEIGHT 21 #define P_WIDTH 60 pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, /* alpha */ 8, /* bits per sample */ P_WIDTH, /* width */ P_HEIGHT /* height */ ); n_channels = gdk_pixbuf_get_n_channels (pixbuf); pixels = gdk_pixbuf_get_pixels (pixbuf); rowstride = gdk_pixbuf_get_rowstride (pixbuf); for (x = 0; x < P_WIDTH; x++) for (y = 0; y < P_HEIGHT; y++) { pixel = pixels + y * rowstride + x * n_channels; #define PX_RED 0 #define PX_GREEN 1 #define PX_BLUE 2 #define PX_ALPHA 3 pixel[PX_RED] = 0; pixel[PX_GREEN] = 0; pixel[PX_BLUE] = 0; pixel[PX_ALPHA] = 0; if (y < P_HEIGHT / 2) { /* applicability */ if (x * kwords < applicability_rank * P_WIDTH) { pixel[PX_RED] = 0; pixel[PX_GREEN] = 0; pixel[PX_BLUE] = 255; pixel[PX_ALPHA] = 255; } } else if ((y > P_HEIGHT / 2) && ((y - P_HEIGHT / 2) * GNUNET_FSUI_MAX_PROBES < availability_certainty * P_HEIGHT / 2)) { /* availability */ if (availability_rank < 0) { if ((x * GNUNET_FSUI_MAX_PROBES > (GNUNET_FSUI_MAX_PROBES + availability_rank) * P_WIDTH / 2) && (x <= P_WIDTH / 2)) { pixel[PX_RED] = 255; pixel[PX_GREEN] = 0; pixel[PX_BLUE] = 0; pixel[PX_ALPHA] = 255; } } else if (availability_rank > 0) { if ((x >= P_WIDTH / 2) && ((x - (P_WIDTH / 2)) * GNUNET_FSUI_MAX_PROBES < availability_rank * P_WIDTH / 2)) { pixel[PX_RED] = 0; pixel[PX_GREEN] = 255; pixel[PX_BLUE] = 0; pixel[PX_ALPHA] = 255; } } else { if (x == P_WIDTH / 2) { /* yellow */ pixel[PX_RED] = 255; pixel[PX_GREEN] = 255; pixel[PX_BLUE] = 0; pixel[PX_ALPHA] = 255; } } } } return pixbuf; } /** * Add the given search result to the search * tree at the specified position. */ void addEntryToSearchTree (SearchList * searchContext, DownloadList * downloadParent, const GNUNET_ECRS_FileInfo * info, GtkTreeIter * iter) { char *name; char *rawMime; char *mime; char *desc; unsigned long long size; char *size_h; GdkPixbuf *pixbuf; GdkPixbuf *rankbuf; #ifdef HAVE_GIO GdkPixbuf *icon = NULL; GIcon *gicon = NULL; const gchar **iconNames; int i = 0; #endif enum GNUNET_URITRACK_STATE state; state = GNUNET_URITRACK_get_state (ectx, cfg, info->uri); rawMime = getMimeTypeFromMetaData (info->meta); desc = getDescriptionFromMetaData (info->meta); name = getFileNameFromMetaData (info->meta); size = GNUNET_ECRS_uri_test_chk (info->uri) || GNUNET_ECRS_uri_test_loc (info->uri) ? GNUNET_ECRS_uri_get_file_size (info->uri) : 0; pixbuf = getThumbnailFromMetaData (info->meta); size_h = GNUNET_get_byte_size_as_fancy_string (size); rankbuf = make_ranking_pixbuf (0, 0, 1, GNUNET_ECRS_uri_get_keyword_count_from_ksk (searchContext->uri)); #ifdef HAVE_GIO if (0 == strcmp (rawMime, GNUNET_DIRECTORY_MIME)) { mime = GNUNET_strdup (_("Directory")); icon = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (), "folder", 16, 0, (GError **) NULL); } else { mime = g_content_type_get_description (rawMime); gicon = g_content_type_get_icon (rawMime); if (G_IS_THEMED_ICON (gicon)) { iconNames = (const gchar **) g_themed_icon_get_names (G_THEMED_ICON (gicon)); do { icon = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (), iconNames[i], 16, 0, (GError **) NULL); i++; } while ((icon == NULL) && iconNames[i]); } } #else mime = GNUNET_strdup(rawMime); #endif gtk_tree_store_set (searchContext->tree, iter, SEARCH_NAME, name, SEARCH_SIZE, size, SEARCH_HSIZE, size_h, SEARCH_MIME, mime, SEARCH_RAW_MIME, rawMime, SEARCH_DESC, desc, SEARCH_PIXBUF, pixbuf, SEARCH_URI, GNUNET_ECRS_uri_duplicate (info->uri), SEARCH_META, GNUNET_meta_data_duplicate (info->meta), SEARCH_CELL_BG_COLOR, getColorCode (state), SEARCH_CELL_FG_COLOR, "black", SEARCH_INTERNAL, searchContext, SEARCH_INTERNAL_PARENT, downloadParent, SEARCH_STATUS, getStatusName (state), SEARCH_APPLICABILITY_RANK, 1, SEARCH_RANK_SORT, (long long) 1, SEARCH_RANK_PIXBUF, rankbuf, #ifdef HAVE_GIO SEARCH_ICON, icon, #endif -1); g_object_unref (rankbuf); if (pixbuf != NULL) g_object_unref (pixbuf); #ifdef HAVE_GIO if (gicon != NULL) g_object_unref (gicon); if (icon != NULL) g_object_unref (icon); #endif GNUNET_free (size_h); GNUNET_free (name); GNUNET_free (desc); GNUNET_free (rawMime); GNUNET_free (mime); } /** * Add the given result to the model (search result * list). * * @param info the information to add to the model * @param uri the search URI * @param searchContext identifies the search page */ void fs_search_result_received (SearchList * searchContext, const GNUNET_ECRS_FileInfo * info, const struct GNUNET_ECRS_URI *uri) { GtkTreeStore *model; GtkTreeIter iter; enum GNUNET_URITRACK_STATE state; struct GNUNET_ECRS_URI *have; state = GNUNET_URITRACK_get_state (ectx, cfg, info->uri); if ((state & (GNUNET_URITRACK_INSERTED | GNUNET_URITRACK_INDEXED)) && (GNUNET_YES == GNUNET_GC_get_configuration_value_yesno (cfg, "GNUNET-GTK", "DISABLE-OWN", GNUNET_NO))) return; model = GTK_TREE_STORE (gtk_tree_view_get_model (searchContext->treeview)); /* Check that the entry does not already exist (for resume!) */ if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter)) { do { have = NULL; gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, SEARCH_URI, &have, -1); if ((have != NULL) && (GNUNET_ECRS_uri_test_equal (have, uri))) return; /* duplicate */ } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &iter)); } gtk_tree_store_append (model, &iter, NULL); addEntryToSearchTree (searchContext, NULL, info, &iter); searchContext->resultsReceived++; updateSearchSummary (searchContext); } /** * Update the applicability and availability rating * for the given search result. * * @param info the search result (and metadata) * @param availability_rank availability estimate * @param applicability_rank relevance */ void fs_search_update (SearchList * searchContext, const GNUNET_ECRS_FileInfo * info, int availability_rank, unsigned int availability_certainty, unsigned int applicability_rank) { GtkTreeStore *model; GtkTreeIter iter; struct GNUNET_ECRS_URI *have; GdkPixbuf *pixbuf; long long rank; unsigned int kwords; kwords = GNUNET_ECRS_uri_get_keyword_count_from_ksk (searchContext->uri); model = GTK_TREE_STORE (gtk_tree_view_get_model (searchContext->treeview)); /* find existing entry */ if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter)) { do { have = NULL; gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, SEARCH_URI, &have, -1); if ((have != NULL) && (GNUNET_ECRS_uri_test_equal (have, info->uri))) { /* gotcha, create pixbuf and rank info! */ rank = (int) applicability_rank + (int) (availability_rank * (int) availability_certainty * 65536); pixbuf = make_ranking_pixbuf (availability_rank, availability_certainty, applicability_rank, kwords); gtk_tree_store_set (searchContext->tree, &iter, SEARCH_AVAILABILITY_RANK, availability_rank, SEARCH_AVAILABILITY_CERTAINTY, availability_certainty, SEARCH_APPLICABILITY_RANK, applicability_rank, SEARCH_RANK_PIXBUF, pixbuf, SEARCH_RANK_SORT, rank, -1); g_object_unref (pixbuf); return; /* done! */ } } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &iter)); } /* not found!? */ GNUNET_GE_BREAK (NULL, 0); } static int on_search_display_metadata_activate (void *cls, GtkWidget * searchEntry) { SearchList *list = cls; GtkTreePath *path; GtkTreeIter iter; struct GNUNET_ECRS_URI *uri; struct GNUNET_MetaData *meta; char *str; GladeXML *xml; GtkWidget *dialog; path = NULL; if (FALSE == gtk_tree_view_get_path_at_pos (list->treeview, list->last_x, list->last_y, &path, NULL, NULL, NULL)) { /* nothing selected */ return FALSE; } if (FALSE == gtk_tree_model_get_iter (GTK_TREE_MODEL (list->tree), &iter, path)) { GNUNET_GE_BREAK (NULL, 0); gtk_tree_path_free (path); return FALSE; } gtk_tree_path_free (path); uri = NULL; meta = NULL; gtk_tree_model_get (GTK_TREE_MODEL (list->tree), &iter, SEARCH_URI, &uri, SEARCH_META, &meta, -1); str = GNUNET_ECRS_uri_to_string (uri); xml = glade_xml_new (GNUNET_GTK_get_glade_filename (), "metaDisplayDialog", PACKAGE_NAME); GNUNET_GTK_connect_glade_with_plugins (xml); dialog = glade_xml_get_widget (xml, "metaDisplayDialog"); gtk_window_set_title (GTK_WINDOW (dialog), str); createMetaDataListTreeView (xml, "metaDisplayDialogTreeView", NULL, meta); gtk_dialog_run (GTK_DIALOG (dialog)); GNUNET_free_non_null (str); gtk_widget_destroy (dialog); UNREF (xml); return FALSE; } static int on_search_copy_uri_activate (void *cls, GtkWidget * searchEntry) { SearchList *list = cls; GtkTreePath *path; GtkTreeIter iter; struct GNUNET_ECRS_URI *uri; char *str; GtkClipboard *clip; path = NULL; if (FALSE == gtk_tree_view_get_path_at_pos (list->treeview, list->last_x, list->last_y, &path, NULL, NULL, NULL)) { GNUNET_GE_BREAK (NULL, 0); return FALSE; } if (FALSE == gtk_tree_model_get_iter (GTK_TREE_MODEL (list->tree), &iter, path)) { GNUNET_GE_BREAK (NULL, 0); gtk_tree_path_free (path); return FALSE; } gtk_tree_path_free (path); uri = NULL; gtk_tree_model_get (GTK_TREE_MODEL (list->tree), &iter, SEARCH_URI, &uri, -1); str = GNUNET_ECRS_uri_to_string (uri); clip = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD); gtk_clipboard_set_text (clip, str, strlen (str)); GNUNET_free (str); return FALSE; } #ifndef MINGW static char * selectFile () { GladeXML *uploadXML; GtkFileChooser *dialog; char *ret; uploadXML = glade_xml_new (GNUNET_GTK_get_glade_filename (), "directorySaveDialog", PACKAGE_NAME); GNUNET_GTK_connect_glade_with_plugins (uploadXML); dialog = GTK_FILE_CHOOSER (glade_xml_get_widget (uploadXML, "directorySaveDialog")); if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_CANCEL) ret = gtk_file_chooser_get_filename (dialog); else ret = NULL; gtk_widget_destroy (GTK_WIDGET (dialog)); UNREF (uploadXML); return ret; } #else /* MINGW */ static char * selectFile () { return plibc_ChooseFile (_ ("Choose the name under which you want to save the search results."), OFN_SHAREAWARE); } #endif /* MINGW */ static int on_save_search_activate (void *cls, GtkWidget * searchEntry) { SearchList *list = cls; char *name; char *directory; unsigned long long dir_len; unsigned int fis_size; GNUNET_ECRS_FileInfo *fis; struct GNUNET_MetaData *meta; GtkTreeModel *model; GtkTreeIter iter; unsigned int pos; model = gtk_tree_view_get_model (list->treeview); if (TRUE != gtk_tree_model_get_iter_first (model, &iter)) { GNUNET_GTK_add_log_entry (_("No search results yet, cannot save!")); return FALSE; } name = selectFile (""); if (name == NULL) return FALSE; fis = NULL; fis_size = 0; GNUNET_array_grow (fis, fis_size, list->resultsReceived); pos = 0; do { if (pos == fis_size) GNUNET_array_grow (fis, fis_size, pos + 1); gtk_tree_model_get (model, &iter, SEARCH_URI, &fis[pos].uri, SEARCH_META, &fis[pos].meta, -1); pos++; } while (gtk_tree_model_iter_next (model, &iter)); meta = GNUNET_meta_data_create (); GNUNET_meta_data_insert (meta, EXTRACTOR_KEYWORDS, list->searchString); GNUNET_meta_data_insert (meta, EXTRACTOR_DESCRIPTION, _("Saved search results")); GNUNET_meta_data_insert (meta, EXTRACTOR_SOFTWARE, "gnunet-gtk"); if (GNUNET_OK != GNUNET_ECRS_directory_create (NULL, &directory, &dir_len, fis_size, fis, meta)) { GNUNET_GTK_add_log_entry (_("Internal error.")); GNUNET_GE_BREAK (NULL, 0); GNUNET_meta_data_destroy (meta); GNUNET_array_grow (fis, fis_size, 0); GNUNET_free (name); return FALSE; } GNUNET_meta_data_destroy (meta); GNUNET_array_grow (fis, fis_size, 0); if (GNUNET_OK != GNUNET_disk_file_write (NULL, name, directory, dir_len, "644")) { GNUNET_GTK_add_log_entry (_("Error writing file `%s'."), name); } GNUNET_free (directory); GNUNET_free (name); return FALSE; } static gint search_click_handler (void *cls, GdkEvent * event) { SearchList *list = cls; GtkMenu *menu; GtkWidget *entry; GdkEventButton *event_button; g_return_val_if_fail (event != NULL, FALSE); if (event->type != GDK_BUTTON_PRESS) return FALSE; event_button = (GdkEventButton *) event; if (event_button->button != 3) return FALSE; list->last_x = event_button->x; list->last_y = event_button->y; menu = GTK_MENU (gtk_menu_new ()); entry = gtk_menu_item_new_with_label (_("_Display metadata")); g_signal_connect_swapped (entry, "activate", G_CALLBACK (on_search_display_metadata_activate), list); gtk_label_set_use_underline (GTK_LABEL (gtk_bin_get_child (GTK_BIN (entry))), TRUE); gtk_widget_show (entry); gtk_menu_shell_append (GTK_MENU_SHELL (menu), entry); entry = gtk_menu_item_new_with_label (_("_Copy URI to Clipboard")); g_signal_connect_swapped (entry, "activate", G_CALLBACK (on_search_copy_uri_activate), list); gtk_label_set_use_underline (GTK_LABEL (gtk_bin_get_child (GTK_BIN (entry))), TRUE); gtk_widget_show (entry); gtk_menu_shell_append (GTK_MENU_SHELL (menu), entry); entry = gtk_menu_item_new_with_label (_("_Save results as directory")); g_signal_connect_swapped (entry, "activate", G_CALLBACK (on_save_search_activate), list); gtk_label_set_use_underline (GTK_LABEL (gtk_bin_get_child (GTK_BIN (entry))), TRUE); gtk_widget_show (entry); gtk_menu_shell_append (GTK_MENU_SHELL (menu), entry); gtk_menu_popup (menu, NULL, NULL, NULL, NULL, event_button->button, event_button->time); return TRUE; } /** * FSUI event: a search was started; create the * tab and add an entry to the summary. */ SearchList * fs_search_started (struct GNUNET_FSUI_SearchList * fsui_list, const struct GNUNET_ECRS_URI * uri, unsigned int anonymityLevel, unsigned int resultCount, const GNUNET_ECRS_FileInfo * results, GNUNET_FSUI_State state) { SearchList *list; gint pages; char *description; GtkTreeViewColumn *column; GtkCellRenderer *renderer; GtkNotebook *notebook; GtkTreePath *path; GtkTreeIter iter; int col; int i; /* check that search does not already exist with fsui_list == NULL; (and if so, hijack!) */ list = search_head; while (list != NULL) { if ((list->fsui_list == NULL) && (list->uri != NULL) && (GNUNET_ECRS_uri_test_equal (list->uri, uri))) { list->fsui_list = fsui_list; for (i = 0; i < resultCount; i++) fs_search_result_received (list, &results[i], uri); if (resultCount == 0) /* otherwise already done! */ updateSearchSummary (list); return list; } list = list->next; } /* build new entry */ if (GNUNET_ECRS_uri_test_ksk (uri)) description = GNUNET_ECRS_ksk_uri_to_human_readable_string (uri); else description = GNUNET_NS_sks_uri_to_human_readable_string (ectx, cfg, uri); if (description == NULL) { GNUNET_GE_BREAK (ectx, 0); return NULL; } list = GNUNET_malloc (sizeof (SearchList)); memset (list, 0, sizeof (SearchList)); list->searchString = description; list->uri = GNUNET_ECRS_uri_duplicate (uri); list->fsui_list = fsui_list; list->next = search_head; list->anonymityLevel = anonymityLevel; search_head = list; list->searchXML = glade_xml_new (GNUNET_GTK_get_glade_filename (), "searchResultsFrame", PACKAGE_NAME); GNUNET_GTK_connect_glade_with_plugins (list->searchXML); list->searchpage = GNUNET_GTK_extract_main_widget_from_window (list->searchXML, "searchResultsFrame"); /* setup tree view and renderers */ list->treeview = GTK_TREE_VIEW (glade_xml_get_widget (list->searchXML, "searchResults")); g_signal_connect_swapped (list->treeview, "button-press-event", G_CALLBACK (search_click_handler), list); list->tree = gtk_tree_store_new (SEARCH_NUM, G_TYPE_STRING, /* name */ G_TYPE_UINT64, /* size */ G_TYPE_STRING, /* human-readable size */ G_TYPE_STRING, /* mime-type */ G_TYPE_STRING, /* raw mime-type */ G_TYPE_STRING, /* meta-data (some) */ GDK_TYPE_PIXBUF, /* preview */ G_TYPE_POINTER, /* url */ G_TYPE_POINTER, /* meta */ G_TYPE_STRING, /* bg-color */ G_TYPE_STRING, /* fg-color */ G_TYPE_POINTER, /* internal: search list */ G_TYPE_POINTER, /* internal: download parent list */ G_TYPE_STRING, /* status */ G_TYPE_INT, /* availability rank */ G_TYPE_UINT, /* availability certainty */ G_TYPE_UINT, /* applicability rank */ GDK_TYPE_PIXBUF, /* ranking visualization */ G_TYPE_INT64 /* numeric sort */ #ifdef HAVE_GIO , GDK_TYPE_PIXBUF /* icon */ #endif ); gtk_tree_view_set_model (list->treeview, GTK_TREE_MODEL (list->tree)); gtk_tree_selection_set_mode (gtk_tree_view_get_selection (list->treeview), GTK_SELECTION_MULTIPLE); g_signal_connect_data (gtk_tree_view_get_selection (list->treeview), "changed", G_CALLBACK (&on_fssearchSelectionChanged), list, NULL, 0); renderer = gtk_cell_renderer_text_new (); col = gtk_tree_view_insert_column_with_attributes (list->treeview, -1, _("Status"), renderer, "text", SEARCH_STATUS, "cell-background", SEARCH_CELL_BG_COLOR, "foreground", SEARCH_CELL_FG_COLOR, NULL); column = gtk_tree_view_get_column (list->treeview, col - 1); gtk_tree_view_column_set_resizable (column, TRUE); gtk_tree_view_column_set_clickable (column, TRUE); gtk_tree_view_column_set_reorderable (column, TRUE); gtk_tree_view_column_set_sort_column_id (column, SEARCH_STATUS); gtk_tree_view_column_set_min_width (column, 0); renderer = gtk_cell_renderer_text_new (); col = gtk_tree_view_insert_column_with_attributes (list->treeview, -1, _("Name"), renderer, "text", SEARCH_NAME, NULL); g_object_set (G_OBJECT (renderer), "wrap-width", 45, "width-chars", 45, "ellipsize", PANGO_ELLIPSIZE_END, NULL); column = gtk_tree_view_get_column (list->treeview, col - 1); gtk_tree_view_column_set_resizable (column, TRUE); gtk_tree_view_column_set_clickable (column, TRUE); gtk_tree_view_column_set_reorderable (column, TRUE); gtk_tree_view_column_set_sort_column_id (column, SEARCH_NAME); renderer = gtk_cell_renderer_text_new (); g_object_set (renderer, "xalign", 1.00, NULL); col = gtk_tree_view_insert_column_with_attributes (list->treeview, -1, _("Size"), renderer, "text", SEARCH_HSIZE, NULL); column = gtk_tree_view_get_column (list->treeview, col - 1); gtk_tree_view_column_set_resizable (column, TRUE); gtk_tree_view_column_set_clickable (column, TRUE); gtk_tree_view_column_set_reorderable (column, TRUE); gtk_tree_view_column_set_sort_column_id (column, SEARCH_SIZE); #ifdef HAVE_GIO renderer = gtk_cell_renderer_pixbuf_new (); col = gtk_tree_view_insert_column_with_attributes (list->treeview, -1, _("Type"), renderer, "pixbuf", SEARCH_ICON, NULL); column = gtk_tree_view_get_column (list->treeview, col - 1); gtk_tree_view_column_set_resizable (column, TRUE); gtk_tree_view_column_set_reorderable (column, TRUE); gtk_tree_view_column_set_sort_column_id (column, SEARCH_RAW_MIME); gtk_tree_view_column_set_min_width (column, 20); #else renderer = gtk_cell_renderer_text_new (); col = gtk_tree_view_insert_column_with_attributes (list->treeview, -1, _("Type"), renderer, "text", SEARCH_MIME, NULL); g_object_set (G_OBJECT (renderer), "wrap-width", 30, "width-chars", 30, "ellipsize", PANGO_ELLIPSIZE_END, NULL); column = gtk_tree_view_get_column (list->treeview, col - 1); gtk_tree_view_column_set_resizable (column, TRUE); gtk_tree_view_column_set_clickable (column, TRUE); gtk_tree_view_column_set_reorderable (column, TRUE); gtk_tree_view_column_set_sort_column_id (column, SEARCH_RAW_MIME); #endif #if 0 /* colums for data visualized graphically */ renderer = gtk_cell_renderer_text_new (); gtk_tree_view_insert_column_with_attributes (list->treeview, -1, _("Availability"), renderer, "text", SEARCH_AVAILABILITY_RANK, NULL); renderer = gtk_cell_renderer_text_new (); gtk_tree_view_insert_column_with_attributes (list->treeview, -1, _("Certainty"), renderer, "text", SEARCH_AVAILABILITY_CERTAINTY, NULL); renderer = gtk_cell_renderer_text_new (); gtk_tree_view_insert_column_with_attributes (list->treeview, -1, _("Applicability"), renderer, "text", SEARCH_APPLICABILITY_RANK, NULL); renderer = gtk_cell_renderer_text_new (); gtk_tree_view_insert_column_with_attributes (list->treeview, -1, _("Sort"), renderer, "text", SEARCH_RANK_SORT, NULL); #endif renderer = gtk_cell_renderer_pixbuf_new (); col = gtk_tree_view_insert_column_with_attributes (list->treeview, -1, _("Ranking"), renderer, "pixbuf", SEARCH_RANK_PIXBUF, NULL); column = gtk_tree_view_get_column (list->treeview, col - 1); gtk_tree_view_column_set_resizable (column, FALSE); gtk_tree_view_column_set_clickable (column, TRUE); gtk_tree_view_column_set_reorderable (column, TRUE); gtk_tree_view_column_set_sort_column_id (column, SEARCH_RANK_SORT); if (GNUNET_YES != GNUNET_GC_get_configuration_value_yesno (cfg, "GNUNET-GTK", "DISABLE-PREVIEWS", GNUNET_NO)) { renderer = gtk_cell_renderer_pixbuf_new (); col = gtk_tree_view_insert_column_with_attributes (list->treeview, -1, _("Preview"), renderer, "pixbuf", SEARCH_PIXBUF, NULL); column = gtk_tree_view_get_column (list->treeview, col - 1); gtk_tree_view_column_set_resizable (column, TRUE); gtk_tree_view_column_set_reorderable (column, TRUE); gtk_tree_view_column_set_resizable (column, TRUE); } renderer = gtk_cell_renderer_text_new (); col = gtk_tree_view_insert_column_with_attributes (list->treeview, -1, _("Meta-data"), renderer, "text", SEARCH_DESC, NULL); column = gtk_tree_view_get_column (list->treeview, col - 1); g_object_set (G_OBJECT (renderer), "wrap-width", 60, "width-chars", 60, "wrap-mode", PANGO_WRAP_WORD_CHAR, "ellipsize", PANGO_ELLIPSIZE_END, "ellipsize-set", TRUE, NULL); gtk_tree_view_column_set_resizable (column, TRUE); gtk_tree_view_column_set_clickable (column, TRUE); gtk_tree_view_column_set_reorderable (column, TRUE); gtk_tree_view_column_set_sort_column_id (column, SEARCH_DESC); /* add entry in search summary */ gtk_list_store_append (search_summary, &iter); gtk_list_store_set (search_summary, &iter, SEARCH_SUMMARY_NAME, description, SEARCH_SUMMARY_RESULT_COUNT, 0, SEARCH_SUMMARY_INTERNAL, list, -1); path = gtk_tree_model_get_path (GTK_TREE_MODEL (search_summary), &iter); list->summaryViewRowReference = gtk_tree_row_reference_new (GTK_TREE_MODEL (search_summary), path); gtk_tree_path_free (path); /* load label */ list->labelXML = glade_xml_new (GNUNET_GTK_get_glade_filename (), "searchTabLabelWindow", PACKAGE_NAME); GNUNET_GTK_connect_glade_with_plugins (list->labelXML); list->tab_label = GNUNET_GTK_extract_main_widget_from_window (list->labelXML, "searchTabLabelWindow"); /* process existing results */ for (i = 0; i < resultCount; i++) fs_search_result_received (list, &results[i], uri); if (resultCount == 0) /* otherwise already done! */ updateSearchSummary (list); /* insert new page into search notebook */ notebook = GTK_NOTEBOOK (glade_xml_get_widget (GNUNET_GTK_get_main_glade_XML (), "downloadNotebook")); pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook)); gtk_notebook_append_page (notebook, list->searchpage, list->tab_label); gtk_notebook_set_current_page (notebook, pages); gtk_widget_show (GTK_WIDGET (notebook)); /* may have been hidden! */ return list; } /** * Recursively free the (internal) model data fields * (uri and meta) from the search tree model. */ static void freeIterSubtree (GtkTreeModel * tree, GtkTreeIter * iter) { GtkTreeIter child; struct GNUNET_ECRS_URI *uri; struct GNUNET_MetaData *meta; do { uri = NULL; meta = NULL; gtk_tree_model_get (tree, iter, SEARCH_URI, &uri, SEARCH_META, &meta, -1); if (uri != NULL) GNUNET_ECRS_uri_destroy (uri); if (meta != NULL) GNUNET_meta_data_destroy (meta); gtk_tree_store_set (GTK_TREE_STORE (tree), iter, SEARCH_URI, NULL, SEARCH_META, NULL, -1); if (gtk_tree_model_iter_children (tree, &child, iter)) freeIterSubtree (tree, &child); } while (gtk_tree_model_iter_next (tree, iter)); } /** * FSUI event: a search was aborted. * Update views accordingly. */ void fs_search_aborted (SearchList * list) { gtk_widget_show (glade_xml_get_widget (list->searchXML, "searchResumeButton")); gtk_widget_show (glade_xml_get_widget (list->searchXML, "searchPauseButton")); } void fs_search_paused (SearchList * list) { /* nothing to be done */ } void fs_search_restarted (SearchList * list) { /* nothing to be done */ } /** * FSUI event: a search was stopped. Remove the * respective tab and its entry in the summary. */ void fs_search_stopped (SearchList * list) { GtkNotebook *notebook; GtkTreeIter iter; SearchList *prev; DownloadList *downloads; GtkTreePath *path; int index; int i; /* remove from linked list */ if (search_head == list) { search_head = search_head->next; } else { prev = search_head; while (prev->next != list) prev = prev->next; prev->next = list->next; } /* remove links from download views */ downloads = download_head; while (downloads != NULL) { if (downloads->searchList == list) { gtk_tree_row_reference_free (downloads->searchViewRowReference); downloads->searchViewRowReference = NULL; downloads->searchList = NULL; } downloads = downloads->next; } /* remove page from notebook */ notebook = GTK_NOTEBOOK (glade_xml_get_widget (GNUNET_GTK_get_main_glade_XML (), "downloadNotebook")); index = -1; for (i = gtk_notebook_get_n_pages (notebook) - 1; i >= 0; i--) if (list->searchpage == gtk_notebook_get_nth_page (notebook, i)) index = i; GNUNET_GE_BREAK (ectx, index != -1); gtk_notebook_remove_page (notebook, index); /* recursively free search model */ if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list->tree), &iter)) freeIterSubtree (GTK_TREE_MODEL (list->tree), &iter); /* destroy entry in summary */ path = gtk_tree_row_reference_get_path (list->summaryViewRowReference); gtk_tree_model_get_iter (GTK_TREE_MODEL (search_summary), &iter, path); gtk_tree_path_free (path); gtk_list_store_remove (search_summary, &iter); /* free list state itself */ UNREF (list->searchXML); UNREF (list->labelXML); gtk_tree_row_reference_free (list->summaryViewRowReference); GNUNET_free (list->searchString); GNUNET_ECRS_uri_destroy (list->uri); GNUNET_free (list); } /* ****************** User event handling ************* */ /** * The user has edited the search entry. * Update search button status. */ void on_fssearchKeywordComboBoxEntry_changed_fs (gpointer dummy2, GtkWidget * searchEntry) { const char *searchString; GtkWidget *searchButton; searchString = getEntryLineValue (GNUNET_GTK_get_main_glade_XML (), "fssearchKeywordComboBoxEntry"); searchButton = glade_xml_get_widget (GNUNET_GTK_get_main_glade_XML (), "fssearchbutton"); gtk_widget_set_sensitive (searchButton, strlen (searchString) > 0); } typedef struct { unsigned int anonymity; struct GNUNET_ECRS_URI *uri; } FSSS; static void * search_start_helper (void *cls) { FSSS *fsss = cls; GNUNET_FSUI_search_start (ctx, fsss->anonymity, fsss->uri); return NULL; } /** * The user has clicked the "SEARCH" button. * Initiate a search. */ void on_fssearchbutton_clicked_fs (gpointer dummy2, GtkWidget * searchButton) { FSSS fsss; const char *searchString; gint pages; gint i; SearchList *list; GtkTreeIter iter; GtkComboBox *searchKeywordGtkCB; GtkWidget *searchNamespaceGtkCB; GtkNotebook *notebook; searchString = getEntryLineValue (GNUNET_GTK_get_main_glade_XML (), "fssearchKeywordComboBoxEntry"); if ((searchString == NULL) || (strlen (searchString) == 0)) { GNUNET_GE_LOG (ectx, GNUNET_GE_ERROR | GNUNET_GE_USER | GNUNET_GE_IMMEDIATE, _("Need a keyword to search!\n")); return; } /* add the keyword to the list of keywords that have been used so far */ searchKeywordGtkCB = GTK_COMBO_BOX (glade_xml_get_widget (GNUNET_GTK_get_main_glade_XML (), "fssearchKeywordComboBoxEntry")); i = gtk_combo_box_get_active (searchKeywordGtkCB); if (i == -1) { GtkListStore *model; model = GTK_LIST_STORE (gtk_combo_box_get_model (searchKeywordGtkCB)); gtk_list_store_prepend (model, &iter); gtk_list_store_set (model, &iter, 0, searchString, -1); } fsss.uri = NULL; /* check for namespace search */ searchNamespaceGtkCB = glade_xml_get_widget (GNUNET_GTK_get_main_glade_XML (), "searchNamespaceComboBoxEntry"); if (TRUE == gtk_combo_box_get_active_iter (GTK_COMBO_BOX (searchNamespaceGtkCB), &iter)) { char *descStr; char *nsName; nsName = NULL; descStr = NULL; gtk_tree_model_get (gtk_combo_box_get_model (GTK_COMBO_BOX (searchNamespaceGtkCB)), &iter, NS_SEARCH_DESCRIPTION, &descStr, NS_SEARCH_NAME, &nsName, -1); if ((descStr != NULL) && (0 == strcmp (descStr, ""))) { nsName = NULL; } else { if ((descStr == NULL) && (nsName != NULL)) descStr = GNUNET_strdup (nsName); } if (nsName != NULL) { char *ustring; GNUNET_EncName enc; GNUNET_HashCode nsid; GNUNET_GE_ASSERT(NULL, GNUNET_OK == GNUNET_pseudonym_name_to_id(ectx, cfg, nsName, &nsid)); GNUNET_hash_to_enc(&nsid, &enc); ustring = GNUNET_malloc (strlen (searchString) + sizeof (GNUNET_EncName) + strlen (GNUNET_ECRS_URI_PREFIX) + strlen (GNUNET_ECRS_SUBSPACE_INFIX) + 10); strcpy (ustring, GNUNET_ECRS_URI_PREFIX); strcat (ustring, GNUNET_ECRS_SUBSPACE_INFIX); strcat (ustring, (const char*)&enc); strcat (ustring, "/"); strcat (ustring, searchString); fsss.uri = GNUNET_ECRS_string_to_uri (ectx, ustring); if (fsss.uri == NULL) { GNUNET_GE_LOG (ectx, GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER, _("Failed to create namespace URI from `%s'.\n"), ustring); } GNUNET_free (ustring); } if (descStr != NULL) free (descStr); if (nsName != NULL) free (nsName); } if (fsss.uri == NULL) fsss.uri = GNUNET_ECRS_keyword_string_to_uri (ectx, searchString); if (fsss.uri == NULL) { GNUNET_GE_BREAK (ectx, 0); return; } /* check if search is already running */ notebook = GTK_NOTEBOOK (glade_xml_get_widget (GNUNET_GTK_get_main_glade_XML (), "downloadNotebook")); pages = gtk_notebook_get_n_pages (notebook); list = search_head; while (list != NULL) { if (GNUNET_ECRS_uri_test_equal (list->uri, fsss.uri)) { for (i = 0; i < pages; i++) { if (gtk_notebook_get_nth_page (notebook, i) == list->searchpage) { gtk_notebook_set_current_page (notebook, i); GNUNET_ECRS_uri_destroy (fsss.uri); return; } } GNUNET_GE_BREAK (ectx, 0); } list = list->next; } fsss.anonymity = getSpinButtonValue (GNUNET_GTK_get_main_glade_XML (), "searchAnonymitySelectionSpinButton"); GNUNET_GTK_run_with_save_calls (search_start_helper, &fsss); GNUNET_ECRS_uri_destroy (fsss.uri); } struct FCBC { int (*method) (struct GNUNET_FSUI_SearchList * list); struct GNUNET_FSUI_SearchList *argument; }; static void * fsui_callback (void *cls) { struct FCBC *fcbc = cls; fcbc->method (fcbc->argument); return NULL; } /** * This method is called when the user clicks on either * the "CLOSE" button (at the bottom of the search page) * or on the "CANCEL (X)" button in the TAB of the * search notebook. Note that "searchPage" can thus * either refer to the main page in the tab or to the * main entry of the tab label. */ void on_closeSearchButton_clicked_fs (GtkWidget * searchPage, GtkWidget * closeButton) { SearchList *list; struct FCBC fcbc; list = search_head; while (list != NULL) { if ((list->searchpage == searchPage) || (list->tab_label == searchPage)) break; list = list->next; } GNUNET_GE_ASSERT (ectx, list != NULL); if (list->fsui_list == NULL) { /* open directory or paused search; close directly */ fs_search_stopped (list); } else { /* actual search - close via FSUI */ fcbc.method = &GNUNET_FSUI_search_abort; fcbc.argument = list->fsui_list; GNUNET_GTK_run_with_save_calls (&fsui_callback, &fcbc); fcbc.method = &GNUNET_FSUI_search_stop; GNUNET_GTK_run_with_save_calls (&fsui_callback, &fcbc); } } /** * The abort button in the search summary was clicked. */ void on_searchPauseButton_clicked_fs (GtkWidget * searchPage, GtkWidget * pauseButton) { SearchList *list; struct FCBC fcbc; list = search_head; while (list != NULL) { if (list->searchpage == searchPage) break; list = list->next; } GNUNET_GE_ASSERT (ectx, list != NULL); gtk_widget_hide (pauseButton); gtk_widget_show (glade_xml_get_widget (list->searchXML, "searchResumeButton")); if (list->fsui_list != NULL) { fcbc.method = &GNUNET_FSUI_search_pause; fcbc.argument = list->fsui_list; GNUNET_GTK_run_with_save_calls (&fsui_callback, &fcbc); } } /** * The abort button in the search summary was clicked. */ void on_searchResumeButton_clicked_fs (GtkWidget * searchPage, GtkWidget * resumeButton) { SearchList *list; struct FCBC fcbc; list = search_head; while (list != NULL) { if (list->searchpage == searchPage) break; list = list->next; } GNUNET_GE_ASSERT (ectx, list != NULL); gtk_widget_hide (resumeButton); gtk_widget_show (glade_xml_get_widget (list->searchXML, "searchPauseButton")); if (list->fsui_list != NULL) { fcbc.method = &GNUNET_FSUI_search_restart; fcbc.argument = list->fsui_list; GNUNET_GTK_run_with_save_calls (&fsui_callback, &fcbc); } } /** * The abort button was clicked. Abort the search. */ void on_abortSearchButton_clicked_fs (GtkWidget * searchPage, GtkWidget * abortButton) { SearchList *list; struct FCBC fcbc; list = search_head; while (list != NULL) { if (list->searchpage == searchPage) break; list = list->next; } GNUNET_GE_ASSERT (ectx, list != NULL); gtk_widget_hide (abortButton); if (list->fsui_list != NULL) { fcbc.method = &GNUNET_FSUI_search_abort; fcbc.argument = list->fsui_list; GNUNET_GTK_run_with_save_calls (&fsui_callback, &fcbc); } } static void stopSearch (GtkTreeModel * model, GtkTreePath * path, GtkTreeIter * iter, gpointer unused) { SearchList *s; struct FCBC fcbc; s = NULL; gtk_tree_model_get (model, iter, SEARCH_SUMMARY_INTERNAL, &s, -1); if (s != NULL) { if (s->fsui_list == NULL) { /* open directory - close directly */ fs_search_stopped (s); } else { fcbc.method = &GNUNET_FSUI_search_abort; fcbc.argument = s->fsui_list; GNUNET_GTK_run_with_save_calls (&fsui_callback, &fcbc); fcbc.method = &GNUNET_FSUI_search_stop; GNUNET_GTK_run_with_save_calls (&fsui_callback, &fcbc); } } } /** * The stop button in the search summary was clicked. */ void on_closeSearchSummaryButton_clicked_fs (GtkWidget * treeview, GtkWidget * closeButton) { GtkTreeSelection *selection; selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)); GNUNET_GTK_tree_selection_selected_foreach (selection, &stopSearch, NULL); } static void abortSearch (GtkTreeModel * model, GtkTreePath * path, GtkTreeIter * iter, gpointer unused) { SearchList *s; struct FCBC fcbc; s = NULL; gtk_tree_model_get (model, iter, SEARCH_SUMMARY_INTERNAL, &s, -1); if ((s != NULL) && (s->fsui_list != NULL)) { fcbc.method = &GNUNET_FSUI_search_abort; fcbc.argument = s->fsui_list; GNUNET_GTK_run_with_save_calls (&fsui_callback, &fcbc); } } /** * The abort button in the search summary was clicked. */ void on_abortSearchSummaryButton_clicked_fs (GtkWidget * treeview, GtkWidget * closeButton) { GtkTreeSelection *selection; selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)); GNUNET_GTK_tree_selection_selected_foreach (selection, &abortSearch, NULL); } /* end of search.c */