/* This file is part of GNUnet. (C) 2005 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 "fs.h" #include "meta.h" #include /** * @brief linked list of pages in the search notebook */ typedef struct SL { struct SL * next; GtkWidget * treeview; GtkWidget * searchpage; GtkTreeModel * model; GtkWidget * anonymityButton; struct ECRS_URI * uri; } SearchList; static SearchList * head; static GtkListStore * summary; /** * Add an entry to the search tree. * * @param model the search model * @param pos the position to add the entry * @param uri the URI to add * @param meta metadata describing the URI */ void addEntryToSearchTree(GtkTreeStore * model, GtkTreeIter * pos, const struct ECRS_URI * uri, const struct ECRS_MetaData * meta) { char * name; char * mime; char * desc; unsigned char * thumb; size_t ts; GdkPixbuf * pixbuf; GdkPixbufLoader * loader; unsigned long long size; mime = ECRS_getFromMetaData(meta, EXTRACTOR_MIMETYPE); if (mime == NULL) mime = STRDUP(_("unknown")); desc = ECRS_getFirstFromMetaData(meta, EXTRACTOR_DESCRIPTION, EXTRACTOR_GENRE, EXTRACTOR_ALBUM, EXTRACTOR_COMMENT, EXTRACTOR_SUBJECT, EXTRACTOR_FORMAT, EXTRACTOR_SIZE, EXTRACTOR_KEYWORDS, -1); if (desc == NULL) desc = STRDUP(""); name = ECRS_getFirstFromMetaData(meta, EXTRACTOR_FILENAME, EXTRACTOR_TITLE, EXTRACTOR_ARTIST, EXTRACTOR_AUTHOR, EXTRACTOR_PUBLISHER, EXTRACTOR_CREATOR, EXTRACTOR_PRODUCER, EXTRACTOR_UNKNOWN, -1); if (name == NULL) name = STRDUP(_("no name given")); if (ECRS_isFileUri(uri)) { size = ECRS_fileSize(uri); } else { size = 0; } thumb = NULL; ts = ECRS_getThumbnailFromMetaData(meta, &thumb); if (ts != 0) { loader = gdk_pixbuf_loader_new(); gdk_pixbuf_loader_write(loader, (const guchar*) thumb, ts, NULL); pixbuf = gdk_pixbuf_loader_get_pixbuf(loader); gdk_pixbuf_loader_close(loader, NULL); g_object_ref(pixbuf); UNREF(loader); } else { pixbuf = NULL; } gtk_tree_store_set(model, pos, SEARCH_NAME, name, SEARCH_SIZE, size, SEARCH_MIME, mime, SEARCH_DESC, desc, SEARCH_PIXBUF, pixbuf, SEARCH_URI, ECRS_dupUri(uri), SEARCH_META, ECRS_dupMetaData(meta), SEARCH_INTERNAL, NULL, /* internal */ -1); FREE(mime); FREE(desc); FREE(name); FREENONNULL(thumb); } GtkWidget * getAnonymityButtonFromTM(GtkTreeModel * model) { SearchList * list; list = head; while (list != NULL) { if (list->model == model) return list->anonymityButton; list = list->next; } return NULL; } /** * 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 ECRS_URI * uri; struct ECRS_MetaData * meta; do { uri = NULL; meta = NULL; gtk_tree_model_get(tree, iter, SEARCH_URI, &uri, SEARCH_META, &meta, -1); if (uri != NULL) ECRS_freeUri(uri); if (meta != NULL) ECRS_freeMetaData(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)); } /** * The spin button giving the rating for a particular namespace * has been changed. Store the new rating for the namespace. */ void on_namespaceRatingSpinButton_changed(GtkWidget * dummy, GtkWidget * dummy2) { GtkWidget * spin; GtkWidget * ncbe; GtkTreeModel * model; GtkTreeIter iter; char * encStr; char * description; int rating; int newrating; spin = glade_xml_get_widget(getMainXML(), "namespaceRatingSpinButton"); ncbe = glade_xml_get_widget(getMainXML(), "searchNamespaceComboBoxEntry"); model = gtk_combo_box_get_model(GTK_COMBO_BOX(ncbe)); if (TRUE == gtk_combo_box_get_active_iter(GTK_COMBO_BOX(ncbe), &iter)) { gtk_tree_model_get(model, &iter, NS_SEARCH_DESCRIPTION, &description, NS_SEARCH_ENCNAME, &encStr, NS_SEARCH_RATING, &rating, -1); if ( (description != NULL) && (0 == strcmp(description, _("globally"))) ) { /* just to be sure */ gtk_widget_set_sensitive(spin, FALSE); } else { if (encStr != NULL) { newrating = gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)); rating = FSUI_rankNamespace(ctx, encStr, newrating - rating); if (rating != newrating) { /* concurrent modification? */ gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), rating); BREAK(); } gtk_list_store_set(GTK_LIST_STORE(model), &iter, NS_SEARCH_RATING, rating, -1); } } } else { /* FIXME: if enc2hash succeeds, we may want to keep this active */ gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), 0); gtk_widget_set_sensitive(spin, FALSE); } } /** * The namespace in the search window has changed. * Update the trust level (possibly changing sensitivity) * and set the search string to the root (if available). */ void on_searchNamespaceComboBoxEntry_changed(GtkWidget * dummy, GtkWidget * dummy2) { GtkWidget * keyword; GtkWidget * spin; GtkWidget * ncbe; GtkTreeModel * model; GtkTreeIter iter; int rating; char * encStr; char * descStr; HashCode512 ns; HashCode512 root; EncName enc; spin = glade_xml_get_widget(getMainXML(), "namespaceRatingSpinButton"); ncbe = glade_xml_get_widget(getMainXML(), "searchNamespaceComboBoxEntry"); model = gtk_combo_box_get_model(GTK_COMBO_BOX(ncbe)); if (TRUE == gtk_combo_box_get_active_iter(GTK_COMBO_BOX(ncbe), &iter)) { encStr = NULL; descStr = NULL; gtk_tree_model_get(model, &iter, NS_SEARCH_DESCRIPTION, &descStr, NS_SEARCH_ENCNAME, &encStr, NS_SEARCH_RATING, &rating, -1); if ( (descStr != NULL) && (0 == strcmp(descStr, _("globally"))) ) { gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), 0); gtk_widget_set_sensitive(spin, FALSE); } else if (encStr != NULL) { enc2hash(encStr, &ns); gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), rating); gtk_widget_set_sensitive(spin, TRUE); if (OK == FSUI_getNamespaceRoot(encStr, &root)) { hash2enc(&root, &enc); keyword = glade_xml_get_widget(getMainXML(), "fssearchKeywordComboBoxEntry"); gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(keyword))), (const gchar*) &enc); } } } else { gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), 0); gtk_widget_set_sensitive(spin, FALSE); } } void on_searchResults_destroy(GtkWidget * dummy, GtkWidget * treeview) { GtkTreeStore * tree; GtkTreeIter iter; tree = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(treeview))); if (! gtk_tree_model_get_iter_first(GTK_TREE_MODEL(tree), &iter)) return; /* tree empty */ freeIterSubtree(GTK_TREE_MODEL(tree), &iter); } /** * Add the given result to the model (search result * list). * @param info the information to add to the model * @param treeview the page from which to obtain the model * @param path the tree path that selects where to add * the information */ static void addSearchResultToModel(const ECRS_FileInfo * info, GtkWidget * treeview, GtkTreeRowReference * row) { GtkTreeStore * model; GtkTreeIter iter; GtkTreeIter parent; GtkTreeIter * pparent; GtkTreePath * path; if (! gtk_tree_row_reference_valid(row)) path = NULL; else path = gtk_tree_row_reference_get_path(row); model = GTK_TREE_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW(treeview))); if (path != NULL) { gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &parent, path); pparent = &parent; } else pparent = NULL; gtk_tree_store_insert(model, &iter, pparent, 0); addEntryToSearchTree(model, &iter, info->uri, info->meta); } /** * 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 path the tree path that selects where to add * the information, NULL for top-level */ void displaySearchResult(const ECRS_FileInfo * info, const struct ECRS_URI * uri, GtkTreeRowReference * row) { SearchList * list; struct ECRS_URI * euri; unsigned int count; GtkTreeIter iter; list = head; while (list != NULL) { if (ECRS_equalsUri(list->uri, uri)) break; list = list->next; } if (list == NULL) { BREAK(); return; } addSearchResultToModel(info, list->treeview, row); if (! gtk_tree_model_get_iter_first(GTK_TREE_MODEL(summary), &iter)) { BREAK(); return; } do { gtk_tree_model_get(GTK_TREE_MODEL(summary), &iter, SER_SUM_COUNT, &count, SER_SUM_URI, &euri, -1); if (ECRS_equalsUri(euri, uri)) { count++; gtk_list_store_set(GTK_LIST_STORE(summary), &iter, SER_SUM_COUNT, count, -1); return; } } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(summary), &iter)); BREAK(); } void on_closeSearchButton_clicked(GtkWidget * searchPage, GtkWidget * closeButton) { GtkWidget * notebook; int index; int i; struct ECRS_URI * uri; SearchList * list; SearchList * prev; GtkTreeIter iter; struct ECRS_URI * euri; list = head; prev = NULL; while (list != NULL) { if (list->searchpage == searchPage) break; prev = list; list = list->next; } if (list == NULL) return; uri = list->uri; FSUI_stopSearch(ctx, uri); if (prev == NULL) head = list->next; else prev->next = list->next; FREE(list); notebook = glade_xml_get_widget(getMainXML(), "downloadNotebook"); index = -1; for (i=gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook))-1;i>=0;i--) if (searchPage == gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), i)) index = i; if (index != -1) { gtk_notebook_remove_page(GTK_NOTEBOOK(notebook), index); } else { BREAK(); } if (! gtk_tree_model_get_iter_first(GTK_TREE_MODEL(summary), &iter)) { BREAK(); ECRS_freeUri(uri); return; } do { gtk_tree_model_get(GTK_TREE_MODEL(summary), &iter, SER_SUM_URI, &euri, -1); if (ECRS_equalsUri(euri, uri)) { gtk_list_store_remove(GTK_LIST_STORE(summary), &iter); ECRS_freeUri(euri); ECRS_freeUri(uri); return; } } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(summary), &iter)); ECRS_freeUri(uri); BREAK(); } static GtkWidget * makeResultFrame(GtkWidget ** treeview, GtkWidget ** anonSpin) { GtkWidget * window; GtkWidget * child; GtkWidget * resultList; GtkCellRenderer * renderer; GtkTreeStore * tree; GladeXML * searchXML; searchXML = glade_xml_new(getGladeFileName(), "searchResultsFrame", PACKAGE_NAME); connectGladeWithPlugins(searchXML); window = glade_xml_get_widget(searchXML, "searchResultsFrame"); resultList = glade_xml_get_widget(searchXML, "searchResults"); *anonSpin = glade_xml_get_widget(searchXML, "downloadAnonymitySpinButton"); if (treeview != NULL) (*treeview) = GTK_WIDGET(GTK_TREE_VIEW(resultList)); tree = gtk_tree_store_new(SEARCH_NUM, G_TYPE_STRING, /* name */ G_TYPE_UINT64, /* size */ G_TYPE_STRING, /* mime-type */ G_TYPE_STRING, /* meta-data (some) */ GDK_TYPE_PIXBUF, /* preview */ G_TYPE_POINTER, /* url */ G_TYPE_POINTER, /* meta */ G_TYPE_POINTER); /* internal: download info/NULL */ gtk_tree_view_set_model(GTK_TREE_VIEW(resultList), GTK_TREE_MODEL(tree)); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(resultList), -1, _("Name"), renderer, "text", SEARCH_NAME, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(resultList), -1, _("Size"), renderer, "text", SEARCH_SIZE, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(resultList), -1, _("Mime-type"), renderer, "text", SEARCH_MIME, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(resultList), -1, _("Meta-data"), renderer, "text", SEARCH_DESC, NULL); if (! testConfigurationString("GNUNET-GTK", "DISABLE-PREVIEWS", "YES")) { renderer = gtk_cell_renderer_pixbuf_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(resultList), -1, _("Preview"), renderer, "pixbuf", SEARCH_PIXBUF, NULL); } child = gtk_bin_get_child(GTK_BIN(window)); gtk_widget_ref(GTK_WIDGET(child)); gtk_container_remove(GTK_CONTAINER(window), child); gtk_widget_destroy(window); UNREF(searchXML); return child; } void on_fssearchbutton_clicked(gpointer dummy2, GtkWidget * searchButton) { GtkWidget * searchKeywordGtkCB; GtkWidget * searchNamespaceGtkCB; GtkWidget * notebook; GtkWidget * page; GtkWidget * label; GtkWidget * entry; GtkWidget * spin; GtkListStore * model; GtkTreeIter iter; struct ECRS_URI * uri; const char * ss; const char * ns; gint pages; gint i; char * tabtxt; SearchList * list; searchKeywordGtkCB = glade_xml_get_widget(getMainXML(), "fssearchKeywordComboBoxEntry"); entry = gtk_bin_get_child(GTK_BIN(searchKeywordGtkCB)); ss = gtk_entry_get_text(GTK_ENTRY(entry)); if (ss == NULL) { LOG(LOG_ERROR, "Need a keyword to search!\n"); return; } i = gtk_combo_box_get_active(GTK_COMBO_BOX(searchKeywordGtkCB)); if (i == -1) { model = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX(searchKeywordGtkCB))); gtk_list_store_prepend(model, &iter); gtk_list_store_set(model, &iter, 0, ss, -1); } searchNamespaceGtkCB = glade_xml_get_widget(getMainXML(), "searchNamespaceComboBoxEntry"); entry = gtk_bin_get_child(GTK_BIN(searchNamespaceGtkCB)); ns = gtk_entry_get_text(GTK_ENTRY(entry)); if ( (ns != NULL) && (0 == strcmp(ns, _("globally"))) ) ns = NULL; if (ns != NULL) { char * ustring; GNUNET_ASSERT(strlen(ns) > sizeof(EncName)); ns = &ns[strlen(ns) - sizeof(EncName) + 1]; ustring = MALLOC(strlen(ss) + sizeof(EncName) + strlen(ECRS_URI_PREFIX) + strlen(ECRS_SUBSPACE_INFIX) + 10); strcpy(ustring, ECRS_URI_PREFIX); strcat(ustring, ECRS_SUBSPACE_INFIX); strcat(ustring, ns); strcat(ustring, "/"); strcat(ustring, ss); uri = ECRS_stringToUri(ustring); if (uri == NULL) { LOG(LOG_ERROR, _("Failed to create namespace URI from `%s'.\n"), ustring); } FREE(ustring); } else { uri = FSUI_parseCharKeywordURI(ss); } if (uri == NULL) return; if (ns == NULL) { tabtxt = STRDUP(ss); } else { tabtxt = MALLOC(strlen(ss) + strlen(ns) + 2); SNPRINTF(tabtxt, strlen(ss) + strlen(ns) + 2, "%s/%s", ns, ss); } notebook = glade_xml_get_widget(getMainXML(), "downloadNotebook"); list = head; pages = gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook)); while (list != NULL) { if (ECRS_equalsUri(list->uri, uri)) { for (i=0;isearchpage) { gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), i); ECRS_freeUri(uri); FREE(tabtxt); return; } } BREAK(); } list = list->next; } list = MALLOC(sizeof(SearchList)); list->searchpage = makeResultFrame(&list->treeview, &spin); list->next = head; list->uri = uri; list->model = gtk_tree_view_get_model(GTK_TREE_VIEW(list->treeview)); list->anonymityButton = spin; head = list; gtk_list_store_append(summary, &iter); gtk_list_store_set(summary, &iter, SER_SUM_NAME, tabtxt, SER_SUM_COUNT, 0, SER_SUM_URI, ECRS_dupUri(uri), -1); label = gtk_label_new(tabtxt); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), list->searchpage, label); gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), pages); gtk_widget_show(notebook); FSUI_startSearch(ctx, getAnonymityLevel(getMainXML(), "searchAnonymitySelectionSpinButton"), uri); FREE(tabtxt); } static int addNamespace(void * arg, const char * namespaceName, const HashCode512 * namespaceId, const struct ECRS_MetaData * md, int rating) { GtkListStore * model = arg; GtkTreeIter iter; EncName enc; char * name; struct ECRS_MetaData * dmd; char * desc; size_t n; hash2enc(namespaceId, &enc); if (md == NULL) { dmd = NULL; desc = STRDUP(""); } else { dmd = ECRS_dupMetaData(md); desc = ECRS_getFirstFromMetaData(md, EXTRACTOR_DESCRIPTION, EXTRACTOR_TITLE, EXTRACTOR_AUTHOR, EXTRACTOR_GENRE, EXTRACTOR_SUBJECT, EXTRACTOR_CREATOR, EXTRACTOR_PRODUCER, EXTRACTOR_GROUP, EXTRACTOR_CREATED_FOR, EXTRACTOR_SUMMARY, EXTRACTOR_OWNER); if (desc == NULL) desc = STRDUP(""); } n = strlen(desc) + 64; name = MALLOC(n); SNPRINTF(name, n, "%s: %*.s", desc, 20, &enc); gtk_list_store_append(model, &iter); gtk_list_store_set(model, &iter, NS_SEARCH_DESCRIPTION, name, NS_SEARCH_ENCNAME, &enc, NS_SEARCH_METADATA, dmd, NS_SEARCH_RATING, rating, -1); FREE(name); return OK; } /** * cron job that periodically updates the model for the * namespace selection in the search vbox. */ static void updateNCBModelSafe(void * unused) { GtkWidget * searchNamespaceCB; GtkListStore * model; GtkTreeIter iter; model = gtk_list_store_new(NS_SEARCH_NUM, G_TYPE_STRING, /* what we show */ G_TYPE_STRING, /* EncName of namespace */ G_TYPE_POINTER, G_TYPE_INT); /* Meta-data about namespace */ gtk_list_store_append(model, &iter); gtk_list_store_set(model, &iter, NS_SEARCH_DESCRIPTION, _("globally"), NS_SEARCH_ENCNAME, NULL, NS_SEARCH_METADATA, NULL, NS_SEARCH_RATING, 0, -1); FSUI_listNamespaces(ctx, NO, &addNamespace, model); searchNamespaceCB = glade_xml_get_widget(getMainXML(), "searchNamespaceComboBoxEntry"); gtk_combo_box_set_model(GTK_COMBO_BOX(searchNamespaceCB), GTK_TREE_MODEL(model)); if (gtk_combo_box_entry_get_text_column(GTK_COMBO_BOX_ENTRY(searchNamespaceCB)) == -1) gtk_combo_box_entry_set_text_column(GTK_COMBO_BOX_ENTRY(searchNamespaceCB), 0); if (-1 == gtk_combo_box_get_active(GTK_COMBO_BOX(searchNamespaceCB))) gtk_combo_box_set_active(GTK_COMBO_BOX(searchNamespaceCB), 0); } static void updateNCBModel(void * dummy) { gtkSaveCall(&updateNCBModelSafe, NULL); } /** * Open a tab for the given search * (and display the results). */ int openTabForSearch(void * unused, const struct ECRS_URI * uri, unsigned int anonymityLevel, unsigned int resultCount, const ECRS_FileInfo * results) { SearchList * list; char * description; char * dhead; GtkWidget * label; GtkWidget * spin; GtkWidget * notebook; GtkTreeStore * model; GtkTreeIter iter; int i; description = ECRS_uriToString(uri); if (description == NULL) { BREAK(); return SYSERR; } GNUNET_ASSERT(strlen(description) >= strlen(ECRS_URI_PREFIX)); dhead = &description[strlen(ECRS_URI_PREFIX)]; if (0 == strncmp(dhead, ECRS_SEARCH_INFIX, strlen(ECRS_SEARCH_INFIX))) dhead = &dhead[strlen(ECRS_SEARCH_INFIX)]; else if (0 == strncmp(dhead, ECRS_SUBSPACE_INFIX, strlen(ECRS_SUBSPACE_INFIX))) dhead = &dhead[strlen(ECRS_SUBSPACE_INFIX)]; label = gtk_label_new(dhead); gtk_list_store_append(summary, &iter); gtk_list_store_set(summary, &iter, SER_SUM_NAME, dhead, SER_SUM_COUNT, resultCount, SER_SUM_URI, ECRS_dupUri(uri), -1); FREE(description); list = MALLOC(sizeof(SearchList)); list->uri = ECRS_dupUri(uri); list->next = head; list->searchpage = makeResultFrame(&list->treeview, &spin); list->anonymityButton = spin; model = GTK_TREE_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW(list->treeview))); list->model = GTK_TREE_MODEL(model); notebook = glade_xml_get_widget(getMainXML(), "downloadNotebook"); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), list->searchpage, label); gtk_widget_show(notebook); head = list; for (i=0;itreeview, NULL); } return OK; } void fs_search_start() { GtkWidget * searchCB; GtkListStore * model; GtkWidget * searchList; GtkCellRenderer * renderer; searchCB = glade_xml_get_widget(getMainXML(), "fssearchKeywordComboBoxEntry"); model = gtk_list_store_new(NS_SEARCH_NUM, G_TYPE_STRING, /* what we show */ G_TYPE_STRING, /* EncName of namespace */ G_TYPE_POINTER, G_TYPE_INT); /* Meta-data about namespace */ gtk_combo_box_set_model(GTK_COMBO_BOX(searchCB), GTK_TREE_MODEL(model)); gtk_combo_box_entry_set_text_column(GTK_COMBO_BOX_ENTRY(searchCB), NS_SEARCH_DESCRIPTION); addCronJob(&updateNCBModel, 0, 5 * cronMINUTES, NULL); searchList = glade_xml_get_widget(getMainXML(), "activeSearchesSummary"); summary = gtk_list_store_new(SER_SUM_NUM, G_TYPE_STRING, /* name */ G_TYPE_INT, /* # results */ G_TYPE_POINTER); /* internal: uri */ gtk_tree_view_set_model(GTK_TREE_VIEW(searchList), GTK_TREE_MODEL(summary)); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(searchList), -1, _("Query"), renderer, "text", SER_SUM_NAME, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(searchList), -1, _("Results"), renderer, "text", SER_SUM_COUNT, NULL); FSUI_listSearches(ctx, &openTabForSearch, NULL); } void fs_search_stop() { SearchList * list; GtkTreeIter iter; struct ECRS_URI * u; delCronJob(&updateNCBModel, 5 * cronMINUTES, NULL); while (head != NULL) { list = head; head = head->next; ECRS_freeUri(list->uri); #if 0 /* FIXME - memory leak. The following is still incorrect; it does not free entire tree (need to recurse!) */ if (gtk_tree_model_get_iter_first(head->model, &iter)) { struct ECRS_MetaData * m; do { gtk_tree_model_get(head->model, &iter, SEARCH_URI, &u, SEARCH_META, &m, -1); gtk_tree_store_set(GTK_TREE_STORE(head->model), &iter, SEARCH_URI, NULL, SEARCH_META, NULL, -1); if (u != NULL) ECRS_freeUri(u); if (m != NULL) ECRS_freeMetaData(m); } while (gtk_tree_model_iter_next(head->model, &iter)); } #endif FREE(list); } /* free URIs in summary */ if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(summary), &iter)) { do { gtk_tree_model_get(GTK_TREE_MODEL(summary), &iter, SER_SUM_URI, &u, -1); if (u != NULL) ECRS_freeUri(u); gtk_list_store_set(summary, &iter, SER_SUM_URI, NULL, -1); } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(summary), &iter)); } } /* end of search.c */