aboutsummaryrefslogtreecommitdiff
path: root/src/fs/gnunet-fs-gtk_event-handler.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/fs/gnunet-fs-gtk_event-handler.c')
-rw-r--r--src/fs/gnunet-fs-gtk_event-handler.c2658
1 files changed, 2658 insertions, 0 deletions
diff --git a/src/fs/gnunet-fs-gtk_event-handler.c b/src/fs/gnunet-fs-gtk_event-handler.c
new file mode 100644
index 00000000..7c243b65
--- /dev/null
+++ b/src/fs/gnunet-fs-gtk_event-handler.c
@@ -0,0 +1,2658 @@
1/*
2 This file is part of GNUnet.
3 (C) 2010, 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file src/fs/gnunet-fs-gtk_event-handler.c
23 * @brief Main event handler for file-sharing
24 * @author Christian Grothoff
25 */
26#include "gnunet-fs-gtk.h"
27#include "gnunet-fs-gtk-download.h"
28#include "gnunet-fs-gtk-event_handler.h"
29#include <string.h>
30
31
32/**
33 * We have a single tab where we display publishing operations.
34 * So there is only one instance of this struct.
35 */
36struct PublishTab
37{
38
39 /**
40 * Frame for the tab.
41 */
42 GtkWidget *frame;
43
44 /**
45 * Associated builder.
46 */
47 GtkBuilder *builder;
48
49 /**
50 * Associated tree store.
51 */
52 GtkTreeStore *ts;
53};
54
55
56/**
57 * Information we keep for each file or directory being published.
58 * Used to quickly identify the tab and row of the operation; stored
59 * in the user-context of the FS library for the publish operation.
60 */
61struct PublishEntry
62{
63 /**
64 * Associated FS publish operation.
65 */
66 struct GNUNET_FS_PublishContext *pc;
67
68 /**
69 * Tab storing this entry.
70 */
71 struct PublishTab *tab;
72
73 /**
74 * Where in the tab is this entry?
75 */
76 GtkTreeRowReference *rr;
77
78 /**
79 * URI of the file (set after completion).
80 */
81 struct GNUNET_FS_Uri *uri;
82
83 /**
84 * Is this the top-level entry for the publish operation
85 * or sub-operation?
86 */
87 int is_top;
88};
89
90
91/**
92 * Information we keep for each search result entry in any search tab.
93 * An entry like this is also generated for downloads by-URI. Used to
94 * quickly identify the tab and row of the result; stored in the
95 * user-context of the FS library for the search result.
96 */
97struct SearchResult
98{
99 /**
100 * Where in the tab is this result?
101 */
102 GtkTreeRowReference *rr;
103
104 /**
105 * Tab storing this result.
106 */
107 struct SearchTab *tab;
108
109 /**
110 * Search result for top-level results and
111 * namespace-update results.
112 */
113 struct GNUNET_FS_SearchResult *result;
114
115 /**
116 * Associated download, or NULL for none.
117 */
118 struct DownloadEntry *download;
119};
120
121
122
123/**
124 * Head of linked list of tabs for searches.
125 */
126static struct SearchTab *search_tab_head;
127
128/**
129 * Tail of linked list of tabs for searches.
130 */
131static struct SearchTab *search_tab_tail;
132
133/**
134 * Special tab we use to for downloads-by-URIs and downloads
135 * where the search tab has been closed ("parent lost").
136 */
137static struct SearchTab *uri_tab;
138
139/**
140 * Special tab we use to store publishing operations.
141 */
142static struct PublishTab *publish_tab;
143
144/**
145 * Row reference for the current search context menu.
146 * FIXME-UNCLEAN: de-globalize?
147 */
148static GtkTreeRowReference *current_context_row_reference;
149
150/**
151 * Search tab used for the current search context menu.
152 * FIXME-UNCLEAN: de-globalize?
153 */
154static struct SearchTab *current_context_search_tab;
155
156
157
158/* ***************** Search event handling ****************** */
159
160/**
161 * This should get the default download directory (so that GNUnet
162 * won't offer the user to download files to the 'bin' subdirectory,
163 * or whatever is the cwd). Returns NULL on failure (such as
164 * non-existend directory). Should also preserve the last setting (so
165 * if the user saves files somewhere else, next time we default to
166 * somewhere else, at least until application restart, or maybe even
167 * between application restarts).
168 *
169 * Fills the 'buffer' up to 'size' bytes, returns a pointer to it.
170 */
171static char *
172get_default_download_directory (char *buffer, size_t size)
173{
174 /* FIXME-FEATURE: implement... */
175 return NULL;
176}
177
178
179/**
180 * Called recursively to build a suggested filename by prepending
181 * suggested names for its parent directories (if any).
182 *
183 * @param tm tree model this function gets the data from
184 * @param iter current position in the tree, for which we want a suggested filename
185 * @param top GNUNET_YES for the original call to this function,
186 * GNUNET_NO for recursive calls; used to decide if the
187 * current call is eligible to set 'anonymity' and 'local_parents'
188 * @param local_parents set to GNUNET_YES if all parents are directories, and are downloaded.
189 * @param anonymity set to the anonymity level of the closest ancestor download (if any);
190 * set to "-1" for uninitialized initially (will be left at -1 if
191 * no suitable parent download was found)
192 * @param filename_is_absolute set to GNUNET_YES if the suggestion is an absolute filename,
193 * GNUNET_NO for relative filenames (gotten from meta data)
194 * @return suggested filename, possibly NULL
195 */
196static char *
197get_suggested_filename_anonymity (GtkTreeModel *tm,
198 GtkTreeIter *iter,
199 int top,
200 int *local_parents,
201 int *anonymity,
202 int *filename_is_absolute)
203{
204 char *result;
205 char *dirname;
206 char *local_filename;
207 char *filename;
208 int downloaded_anonymity;
209 int have_a_parent;
210 struct GNUNET_CONTAINER_MetaData *meta;
211 GtkTreeIter parent;
212 const char *basename;
213 char *dot;
214
215 /* FIXME-BUG-MAYBE: this function is likely responsible for not always
216 suggesting the best filename... To be investigated some more... */
217 gtk_tree_model_get (tm, iter, 0, &meta,
218 15, &local_filename,
219 16, &downloaded_anonymity,
220 -1);
221 if ( (NULL == local_filename) && (GNUNET_NO == top) )
222 *local_parents = GNUNET_NO;
223 if ( (downloaded_anonymity != -1) && (*anonymity == -1) && (GNUNET_NO == top) )
224 *anonymity = downloaded_anonymity;
225 if (gtk_tree_model_iter_parent (tm, &parent, iter))
226 {
227 have_a_parent = GNUNET_YES;
228 dirname = get_suggested_filename_anonymity (tm, &parent, GNUNET_NO,
229 local_parents, anonymity,
230 filename_is_absolute);
231 }
232 else
233 {
234 have_a_parent = GNUNET_NO;
235 dirname = NULL;
236 if (GNUNET_NO == top)
237 *local_parents = GNUNET_NO;
238 }
239 if (local_filename == NULL)
240 {
241 filename = GNUNET_FS_meta_data_suggest_filename (meta);
242 }
243 else
244 {
245 /* This directory was downloaded as /foo/bar/baz/somedirname
246 * Hopefully, "somedirname" is actually "somedir.gnd"
247 * We need to strip the ".gnd" part to get "somedir", which is
248 * what we're going to use instead of suggested original filename
249 * Without the .gnd extension we're going to just use a copy
250 * of the directory file name - and that would fail. Sad.
251 */
252 if ( (NULL == dirname) && (GNUNET_NO == have_a_parent))
253 {
254 /* This is the ealderlest parent directory. Use absolute path. */
255 basename = (const char *) local_filename;
256 *filename_is_absolute = GNUNET_YES;
257 }
258 else
259 {
260 basename = GNUNET_STRINGS_get_short_name (local_filename);
261 }
262 if ( (NULL != basename) && (strlen (basename) > 0) )
263 {
264 filename = GNUNET_strdup (basename);
265 dot = strrchr (filename, '.');
266 if (dot)
267 *dot = '\0';
268 }
269 else
270 {
271 filename = GNUNET_FS_meta_data_suggest_filename (meta);
272 }
273 }
274 if ( (NULL != dirname) && (NULL != filename) )
275 {
276 GNUNET_asprintf (&result, "%s%s%s", dirname, DIR_SEPARATOR_STR, filename);
277 GNUNET_free (filename);
278 GNUNET_free (dirname);
279 return result;
280 }
281 if (NULL != filename)
282 return filename;
283 return NULL;
284}
285
286
287/**
288 * This function is called when the user double-clicks on a search
289 * result. Begins the download, if necessary by opening the "save as"
290 * window.
291 *
292 * @param tree_view tree view with the details
293 * @param path path selecting which entry we want to download
294 * @param tab the search tab where the user triggered the download request
295 * @param is_recursive was the request for a recursive download?
296 */
297static void
298start_download (GtkTreeView *tree_view,
299 GtkTreePath *path,
300 struct SearchTab *tab,
301 int is_recursive)
302{
303 GtkTreeModel *tm;
304 GtkTreeIter iter;
305 struct GNUNET_FS_Uri *uri;
306 struct GNUNET_CONTAINER_MetaData *meta;
307 struct SearchResult *sr;
308 gchar *mime;
309 struct DownloadContext *dc;
310 char *buf = NULL;
311 char *tmp;
312 size_t tmplen;
313 char cwd[FILENAME_MAX];
314 char *download_directory;
315 char *filename;
316 int local_parents;
317 int have_a_suggestion;
318 int anonymity;
319 int filename_is_absolute;
320
321 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
322 "Starting a %sdownload\n",
323 is_recursive ? "recursive " : "");
324
325 GNUNET_assert (tab != NULL);
326 tm = gtk_tree_view_get_model (tree_view);
327 if (TRUE != gtk_tree_model_get_iter (tm, &iter, path))
328 {
329 GNUNET_break (0);
330 return;
331 }
332 gtk_tree_model_get (tm, &iter, 0, &meta, 1, &uri, 9, &sr, 10, &mime, -1);
333 if (NULL == uri)
334 {
335 /* user clicked on directory that was opened (not downloaded!), so we
336 have no URI and downloading makes no sense. Ignore! */
337 g_free (mime);
338 return;
339 }
340 if (!(GNUNET_FS_uri_test_chk (uri) || GNUNET_FS_uri_test_loc (uri)))
341 {
342 /* can only download chk/loc URIs, ignore */
343 g_free (mime);
344 return;
345 }
346
347 download_directory = get_default_download_directory (cwd, sizeof (cwd));
348 /* If no download directory is known, try working directory */
349 if (download_directory == NULL)
350 download_directory = getcwd (cwd, sizeof (cwd));
351 /* Calculate suggested filename */
352 local_parents = GNUNET_YES;
353 anonymity = -1;
354 filename_is_absolute = GNUNET_NO;
355 filename = get_suggested_filename_anonymity (tm, &iter, GNUNET_YES,
356 &local_parents, &anonymity,
357 &filename_is_absolute);
358 have_a_suggestion = GNUNET_NO;
359 if (NULL != download_directory)
360 {
361 if (NULL == filename)
362 {
363 buf = GNUNET_strdup (download_directory);
364 }
365 else
366 {
367 have_a_suggestion = GNUNET_YES;
368 if (filename_is_absolute)
369 GNUNET_asprintf (&tmp, "%s", filename);
370 else
371 GNUNET_asprintf (&tmp, "%s%s%s",
372 download_directory,
373 DIR_SEPARATOR_STR,
374 filename);
375 tmplen = strlen (tmp);
376 /* now, if we have a directory, replace trailing '/' with ".gnd" */
377 if (GNUNET_YES ==
378 GNUNET_FS_meta_data_test_for_directory (meta))
379 {
380 if ( (tmp[tmplen-1] == '/') ||
381 (tmp[tmplen-1] == '\\') )
382 tmp[tmplen-1] = '\0';
383 GNUNET_asprintf (&buf,
384 "%s%s",
385 tmp,
386 GNUNET_FS_DIRECTORY_EXT);
387 GNUNET_free (tmp);
388 }
389 else
390 {
391 buf = tmp;
392 }
393 }
394 }
395 GNUNET_free_non_null (filename);
396
397 /* now setup everything for the save-as dialog */
398 dc = GNUNET_malloc (sizeof (struct DownloadContext));
399 dc->uri = GNUNET_FS_uri_dup (uri);
400 dc->mime = mime;
401 dc->filename = buf;
402 dc->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
403 dc->rr = gtk_tree_row_reference_new (tm, path);
404 dc->sr = sr->result;
405 dc->anonymity = anonymity;
406 dc->is_recursive = is_recursive;
407 dc->tab = tab;
408 if ( (GNUNET_YES == local_parents) &&
409 (GNUNET_YES == have_a_suggestion) )
410 /* Skip the dialog, call directly */
411 GNUNET_FS_GTK_download_context_start_download (dc);
412 else
413 GNUNET_FS_GTK_open_download_as_dialog (dc);
414}
415
416
417/**
418 * An item was selected from the context menu; destroy the menu shell.
419 *
420 * @param menushell menu to destroy
421 * @parma user_data the 'struct DownloadEntry' for the menu (unused)
422 */
423static void
424search_list_popup_selection_done (GtkMenuShell *menushell,
425 gpointer user_data)
426{
427 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
428 "Item selected in menu shell %p, destroying it.\n",
429 menushell);
430 gtk_widget_destroy (GTK_WIDGET (menushell));
431}
432
433
434/**
435 * This function is called when the user double-clicks on a search
436 * result. Begins the download, if necessary by opening the "save as"
437 * window.
438 *
439 * @param tree_view tree view with the details
440 * @param path path selecting which entry we want to download
441 * @param column unused entry specifying which column the mouse was in
442 * @param user_data the 'struct SearchTab' that was activated
443 */
444static void
445start_download_row_activated (GtkTreeView * tree_view, GtkTreePath * path,
446 GtkTreeViewColumn * column, gpointer user_data)
447{
448 struct SearchTab *tab = user_data;
449
450 start_download (tree_view, path, tab, GNUNET_NO);
451}
452
453
454/**
455 * "Download" was selected in the current search context menu.
456 *
457 * @param item the 'download' menu item
458 * @parma user_data the 'struct DownloadEntry' to download.
459 */
460static void
461start_download_ctx_menu (GtkMenuItem *item, gpointer user_data)
462{
463 GtkTreePath *path;
464 GtkTreeView *tv;
465
466 if (current_context_row_reference == NULL)
467 {
468 GNUNET_break (0);
469 return;
470 }
471 path = gtk_tree_row_reference_get_path (current_context_row_reference);
472 gtk_tree_row_reference_free (current_context_row_reference);
473 current_context_row_reference = NULL;
474 tv = GTK_TREE_VIEW (gtk_builder_get_object
475 (current_context_search_tab->builder,
476 "_search_result_frame"));
477 start_download (tv, path, current_context_search_tab, GNUNET_NO);
478 gtk_tree_path_free (path);
479 current_context_search_tab = NULL;
480}
481
482
483/**
484 * "Download recursively" was selected in the current search context menu.
485 *
486 * @param item the 'download recursively' menu item
487 * @parma user_data the 'struct DownloadEntry' to download.
488 */
489static void
490start_download_recursively_ctx_menu (GtkMenuItem *item, gpointer user_data)
491{
492 GtkTreePath *path;
493 GtkTreeView *tv;
494
495 if (current_context_row_reference == NULL)
496 {
497 GNUNET_break (0);
498 return;
499 }
500 path = gtk_tree_row_reference_get_path (current_context_row_reference);
501 gtk_tree_row_reference_free (current_context_row_reference);
502 current_context_row_reference = NULL;
503 tv = GTK_TREE_VIEW (gtk_builder_get_object
504 (current_context_search_tab->builder,
505 "_search_result_frame"));
506 start_download (tv, path, current_context_search_tab, GNUNET_YES);
507 gtk_tree_path_free (path);
508 current_context_search_tab = NULL;
509}
510
511
512/**
513 * Download "abort" was selected in the current search context menu.
514 *
515 * @param item the 'abort' menu item
516 * @parma user_data the 'struct DownloadEntry' to abort.
517 */
518static void
519abort_download_ctx_menu (GtkMenuItem *item, gpointer user_data)
520{
521 struct DownloadEntry *de = user_data;
522
523 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
524 "Aborting download DE=%p\n",
525 de);
526 GNUNET_assert (de->dc != NULL);
527 GNUNET_FS_download_stop (de->dc, GNUNET_YES);
528 current_context_search_tab = NULL;
529}
530
531
532/**
533 * Copy current URI to clipboard was selected in the current context menu.
534 *
535 * @param item the 'copy-to-clipboard' menu item
536 * @parma user_data NULL
537 */
538static void
539copy_uri_to_clipboard_ctx_menu (GtkMenuItem *item, gpointer user_data)
540{
541 GtkTreePath *path;
542 GtkTreeView *tv;
543 GtkTreeModel *tm;
544 GtkTreeIter iter;
545 struct GNUNET_FS_Uri *uri;
546 char *uris;
547 GtkClipboard *cb;
548
549 if (NULL == current_context_row_reference)
550 {
551 GNUNET_break (0);
552 return;
553 }
554 path = gtk_tree_row_reference_get_path (current_context_row_reference);
555 gtk_tree_row_reference_free (current_context_row_reference);
556 current_context_row_reference = NULL;
557 tv = GTK_TREE_VIEW (gtk_builder_get_object
558 (current_context_search_tab->builder,
559 "_search_result_frame"));
560 tm = gtk_tree_view_get_model (tv);
561 if (! gtk_tree_model_get_iter (tm, &iter, path))
562 {
563 GNUNET_break (0);
564 gtk_tree_path_free (path);
565 return;
566 }
567 gtk_tree_model_get (tm, &iter, 1, &uri, -1);
568 gtk_tree_path_free (path);
569 current_context_search_tab = NULL;
570 if (uri == NULL)
571 {
572 GNUNET_break (0);
573 return;
574 }
575 uris = GNUNET_FS_uri_to_string (uri);
576 cb = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
577 gtk_clipboard_set_text (cb, uris, -1);
578 gtk_clipboard_store (cb);
579 GNUNET_free (uris);
580}
581
582
583/**
584 * Context menu was requested for a search result list.
585 * Compute which menu items are applicable and display
586 * an appropriate menu.
587 *
588 * @param tm tree model underlying the tree view where the event happened
589 * @param tab tab where the event happened
590 * @param event_button the event
591 * @return FALSE if no menu could be popped up,
592 * TRUE if there is now a pop-up menu
593 */
594static gboolean
595search_list_popup (GtkTreeModel *tm,
596 struct SearchTab *tab,
597 gint init_button,
598 guint32 event_time,
599 GtkTreeIter *iter)
600{
601 GtkMenu *menu;
602 GtkWidget *child;
603 GtkTreePath *path;
604 struct SearchResult *sr;
605 struct GNUNET_FS_Uri *uri;
606
607 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
608 "Creating a menu for SR=%p, DE=%p\n",
609 sr,
610 sr->download);
611
612 /* FIXME-UNCLEAN: move these to some menu context struct
613 (de-globalize) */
614 current_context_search_tab = tab;
615 if (current_context_row_reference != NULL)
616 {
617 gtk_tree_row_reference_free (current_context_row_reference);
618 current_context_row_reference = NULL;
619 }
620 path = gtk_tree_model_get_path (tm, iter);
621 current_context_row_reference = gtk_tree_row_reference_new (tm, path);
622 gtk_tree_path_free (path);
623
624 gtk_tree_model_get (tm, iter, 1, &uri, 9, &sr, -1);
625
626 menu = GTK_MENU (gtk_menu_new ());
627 if ( (NULL == sr->download) &&
628 (NULL != uri) )
629 {
630 /* only display download menus if there is a URI */
631 child = gtk_menu_item_new_with_label (_("_Download"));
632 g_signal_connect (child, "activate",
633 G_CALLBACK (start_download_ctx_menu), NULL);
634 gtk_label_set_use_underline (GTK_LABEL
635 (gtk_bin_get_child (GTK_BIN (child))),
636 TRUE);
637 gtk_widget_show (child);
638 gtk_menu_shell_append (GTK_MENU_SHELL (menu), child);
639
640 child = gtk_menu_item_new_with_label (_("Download _recursively"));
641 g_signal_connect (child, "activate",
642 G_CALLBACK (start_download_recursively_ctx_menu), NULL);
643 gtk_label_set_use_underline (GTK_LABEL
644 (gtk_bin_get_child (GTK_BIN (child))),
645 TRUE);
646 gtk_widget_show (child);
647 gtk_menu_shell_append (GTK_MENU_SHELL (menu), child);
648 }
649 if (NULL != sr->download)
650 {
651 child = gtk_menu_item_new_with_label (_("_Abort download"));
652 g_signal_connect (child, "activate",
653 G_CALLBACK (abort_download_ctx_menu), sr->download);
654 gtk_label_set_use_underline (GTK_LABEL
655 (gtk_bin_get_child (GTK_BIN (child))),
656 TRUE);
657 gtk_widget_show (child);
658 gtk_menu_shell_append (GTK_MENU_SHELL (menu), child);
659 }
660 if (NULL != uri)
661 {
662 child = gtk_menu_item_new_with_label (_("_Copy URI to Clipboard"));
663 g_signal_connect (child, "activate",
664 G_CALLBACK (copy_uri_to_clipboard_ctx_menu), NULL);
665 gtk_label_set_use_underline (GTK_LABEL
666 (gtk_bin_get_child (GTK_BIN (child))), TRUE);
667 gtk_widget_show (child);
668 }
669 g_signal_connect (menu, "selection-done",
670 G_CALLBACK (search_list_popup_selection_done), NULL);
671 gtk_menu_shell_append (GTK_MENU_SHELL (menu), child);
672 gtk_menu_popup (menu, NULL, NULL, NULL, NULL, init_button, event_time);
673 return TRUE;
674}
675
676
677/**
678 * We got a 'popup-menu' event, display the context menu.
679 *
680 * @param widget the tree view where the event happened
681 * @param user_data the 'struct SearchTab' of the tree view
682 * @return FALSE if no menu could be popped up,
683 * TRUE if there is now a pop-up menu
684 */
685static gboolean
686search_list_on_popup (GtkWidget *widget, gpointer user_data)
687{
688 GtkTreeView *tv = GTK_TREE_VIEW (widget);
689 struct SearchTab *tab = user_data;
690 GtkTreeSelection *sel;
691 GtkTreeIter iter;
692 GtkTreeModel *tm;
693
694 sel = gtk_tree_view_get_selection (tv);
695 if (! gtk_tree_selection_get_selected (sel, &tm, &iter))
696 return FALSE; /* nothing selected */
697 return search_list_popup (tm, tab, 0, gtk_get_current_event_time (), &iter);
698}
699
700
701/**
702 * We got a right-click on the search result list. Display the context
703 * menu.
704 *
705 * @param widget the GtkTreeView with the search result list
706 * @param event the event, we only care about button events
707 * @param user_data the 'struct SearchTab' the widget is in
708 * @return FALSE if no menu could be popped up,
709 * TRUE if there is now a pop-up menu
710 */
711static gboolean
712search_list_on_menu (GtkWidget * widget,
713 GdkEvent * event,
714 gpointer user_data)
715{
716 GtkTreeView *tv = GTK_TREE_VIEW (widget);
717 GdkEventButton *event_button = (GdkEventButton *) event;
718 struct SearchTab *tab = user_data;
719 GtkTreeModel *tm;
720 GtkTreePath *path;
721 GtkTreeIter iter;
722
723 if ( (event->type != GDK_BUTTON_PRESS) ||
724 (event_button->button != 3) )
725 return FALSE; /* not a right-click */
726 if (! gtk_tree_view_get_path_at_pos (tv,
727 event_button->x, event_button->y,
728 &path, NULL, NULL, NULL))
729 return FALSE; /* click outside of area with values, ignore */
730 tm = gtk_tree_view_get_model (tv);
731 if (! gtk_tree_model_get_iter (tm, &iter, path))
732 return FALSE; /* not sure how we got a path but no iter... */
733 gtk_tree_path_free (path);
734 return search_list_popup (tm, tab,
735 event_button->button,
736 event_button->time,
737 &iter);
738}
739
740
741/**
742 * Recalculate and update the label for a search, as we have
743 * received additional search results.
744 *
745 * @param tab search tab for which we should update the label
746 */
747static void
748update_search_label (struct SearchTab *tab)
749{
750 char *label_text;
751
752 while (tab->parent != NULL)
753 tab = tab->parent->tab;
754 if (tab->num_results > 0)
755 GNUNET_asprintf (&label_text, "%.*s%s (%u)", 20, tab->query_txt,
756 strlen (tab->query_txt) > 20 ? "..." : "",
757 tab->num_results);
758 else
759 GNUNET_asprintf (&label_text, "%.*s%s", 20, tab->query_txt,
760 strlen (tab->query_txt) > 20 ? "..." : "");
761 gtk_label_set_text (tab->label, label_text);
762 gtk_widget_set_tooltip_text (GTK_WIDGET (tab->label), tab->query_txt);
763 GNUNET_free (label_text);
764}
765
766
767/**
768 * Close a search tab and free associated state. Assumes that the
769 * respective tree model has already been cleaned up (this just
770 * updates the notebook and frees the 'tab' itself).
771 *
772 * @param tab search tab to close
773 */
774static void
775close_search_tab (struct SearchTab *tab)
776{
777 GtkNotebook *notebook;
778 int index;
779 int i;
780
781 if (tab->parent != NULL)
782 {
783 /* not a top-level search (namespace update search), do not close
784 tab here! */
785 GNUNET_free (tab);
786 return;
787 }
788 notebook =
789 GTK_NOTEBOOK (GNUNET_FS_GTK_get_main_window_object
790 ("GNUNET_GTK_main_window_notebook"));
791 index = -1;
792 for (i = gtk_notebook_get_n_pages (notebook) - 1; i >= 0; i--)
793 if (tab->frame == gtk_notebook_get_nth_page (notebook, i))
794 index = i;
795 gtk_notebook_remove_page (notebook, index);
796 g_object_unref (tab->builder);
797 GNUNET_free (tab->query_txt);
798 GNUNET_CONTAINER_DLL_remove (search_tab_head, search_tab_tail, tab);
799 if (tab == uri_tab)
800 uri_tab = NULL;
801 GNUNET_free (tab);
802}
803
804
805/**
806 * Free a particular search result and remove the respective
807 * entries from the respective tree store. This function
808 * is called when a search is stopped to clean up the state
809 * of the tab.
810 *
811 * @param sr the search result to clean up
812 */
813static void
814free_search_result (struct SearchResult *sr)
815{
816 GtkTreePath *tp;
817 GtkTreeModel *tm;
818 GtkTreeIter iter;
819 struct GNUNET_FS_Uri *uri;
820 struct GNUNET_CONTAINER_MetaData *meta;
821
822 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
823 "Freeing a search result SR=%p\n",
824 sr);
825 if ( (NULL == sr) ||
826 (NULL == sr->rr) ||
827 (NULL == (tm = gtk_tree_row_reference_get_model (sr->rr))) ||
828 (NULL == (tp = gtk_tree_row_reference_get_path (sr->rr))) )
829 {
830 GNUNET_break (0);
831 return;
832 }
833 if (! gtk_tree_model_get_iter (tm, &iter, tp))
834 {
835 GNUNET_break (0);
836 gtk_tree_path_free (tp);
837 return;
838 }
839 gtk_tree_path_free (tp);
840 gtk_tree_model_get (tm, &iter, 0, &meta, 1, &uri, -1);
841 if (uri != NULL)
842 GNUNET_FS_uri_destroy (uri);
843 if (meta != NULL)
844 GNUNET_CONTAINER_meta_data_destroy (meta);
845 gtk_tree_row_reference_free (sr->rr);
846 (void) gtk_tree_store_remove (GTK_TREE_STORE (tm), &iter);
847 GNUNET_free (sr);
848}
849
850
851/**
852 * Selected row has changed in search result tree view, update preview
853 * and metadata areas.
854 *
855 * @param tv the tree view in a search tab where the selection changed
856 * @param user_data the 'struct SearchTab' that contains the tree view
857 */
858static void
859update_meta_data_views (GtkTreeView *tv, gpointer user_data)
860{
861 struct SearchTab *tab = user_data;
862 GtkImage *image;
863 GtkListStore *ms;
864 GtkTreeSelection *sel;
865 GtkTreeModel *model;
866 GtkTreeIter iter;
867 struct GNUNET_CONTAINER_MetaData *meta;
868 GdkPixbuf *pixbuf;
869
870 GNUNET_assert (tab->query_txt != NULL);
871 image =
872 GTK_IMAGE (GNUNET_FS_GTK_get_main_window_object
873 ("GNUNET_GTK_main_window_preview_image"));
874 ms = GTK_LIST_STORE (GNUNET_FS_GTK_get_main_window_object
875 ("GNUNET_GTK_meta_data_list_store"));
876 sel = gtk_tree_view_get_selection (tv);
877 gtk_list_store_clear (ms);
878 if (! gtk_tree_selection_get_selected (sel, &model, &iter))
879 {
880 /* nothing selected, clear preview */
881 gtk_image_clear (image);
882 return;
883 }
884 meta = NULL;
885 pixbuf = NULL;
886 gtk_tree_model_get (model, &iter, 0, &meta, 3, &pixbuf, -1);
887 if (NULL != pixbuf)
888 {
889 gtk_image_set_from_pixbuf (image, pixbuf);
890 g_object_unref (G_OBJECT (pixbuf));
891 }
892 if (NULL != meta)
893 GNUNET_CONTAINER_meta_data_iterate (meta,
894 &GNUNET_FS_GTK_add_meta_data_to_list_store,
895 ms);
896}
897
898
899/**
900 * Page switched in main notebook, update thumbnail and
901 * metadata views.
902 *
903 * @param dummy widget emitting the event, unused
904 * @param data master Gtk builder, unused
905 */
906void
907GNUNET_GTK_main_window_notebook_switch_page_cb (GtkWidget * dummy,
908 gpointer data)
909{
910 GtkNotebook *notebook;
911 gint page;
912 GtkWidget *w;
913 struct SearchTab *tab;
914 GtkImage *image;
915 GtkListStore *ms;
916 GtkTreeView *tv;
917
918 notebook =
919 GTK_NOTEBOOK (GNUNET_FS_GTK_get_main_window_object
920 ("GNUNET_GTK_main_window_notebook"));
921 page = gtk_notebook_get_current_page (notebook);
922 w = gtk_notebook_get_nth_page (notebook, page);
923 for (tab = search_tab_head; NULL != tab; tab = tab->next)
924 {
925 if (tab->frame != w)
926 continue;
927 tv = GTK_TREE_VIEW (gtk_builder_get_object
928 (tab->builder, "_search_result_frame"));
929 update_meta_data_views (tv, tab);
930 return;
931 }
932 /* active tab is not a search tab (likely the 'publish' tab),
933 clear meta data and preview widgets */
934 image =
935 GTK_IMAGE (GNUNET_FS_GTK_get_main_window_object
936 ("GNUNET_GTK_main_window_preview_image"));
937 gtk_image_clear (image);
938 ms = GTK_LIST_STORE (GNUNET_FS_GTK_get_main_window_object
939 ("GNUNET_GTK_meta_data_list_store"));
940 gtk_list_store_clear (ms);
941}
942
943
944/**
945 * User clicked on the 'close' button for a search tab. Tell FS to stop the search.
946 *
947 * @param button the 'close' button
948 * @param user_data the 'struct SearchTab' of the tab to close
949 */
950static void
951stop_search (GtkButton *button, gpointer user_data)
952{
953 struct SearchTab *tab = user_data;
954 struct GNUNET_FS_SearchContext *sc;
955
956 sc = tab->sc;
957 if (NULL == sc)
958 {
959 GNUNET_break (0);
960 return;
961 }
962 tab->sc = NULL;
963 GNUNET_FS_search_stop (sc);
964 gtk_widget_hide (GTK_WIDGET (button));
965 gtk_widget_hide (tab->pause_button);
966 gtk_widget_hide (tab->play_button);
967}
968
969
970/**
971 * The user clicked on the 'pause' button for a search tab. Tell FS to pause the search.
972 *
973 * @param button the 'pause' button
974 * @param user_data the 'struct SearchTab' of the tab to pause
975 */
976static void
977pause_search (GtkButton *button, gpointer user_data)
978{
979 struct SearchTab *tab = user_data;
980
981 if (NULL == tab->sc)
982 {
983 GNUNET_break (0);
984 return;
985 }
986 GNUNET_FS_search_pause (tab->sc);
987 gtk_widget_show (tab->play_button);
988 gtk_widget_hide (tab->pause_button);
989}
990
991
992/**
993 * The user clicked on the 'resume' button for a search tab. Tell FS to resume the search.
994 *
995 * @param button the 'resume' button
996 * @param user_data the 'struct SearchTab' of the tab to resume
997 */
998static void
999continue_search (GtkButton * button, gpointer user_data)
1000{
1001 struct SearchTab *tab = user_data;
1002
1003 if (NULL == tab->sc)
1004 {
1005 GNUNET_break (0);
1006 return;
1007 }
1008 GNUNET_FS_search_continue (tab->sc);
1009 gtk_widget_show (tab->pause_button);
1010 gtk_widget_hide (tab->play_button);
1011}
1012
1013
1014/**
1015 * User clicked on the 'clean' button of a search tab.
1016 * Stop completed downloads (or those that failed). Should
1017 * iterate over the underlying tree store and stop all
1018 * completed entries. Furthermore, if the resulting tree
1019 * store is empty and has no search associated with it,
1020 * the tab should be closed.
1021 *
1022 * @param button the button pressed by the user
1023 * @param user_data the 'struct SearchTab' of the respective tab to clean up
1024 */
1025static void
1026clear_downloads (GtkButton * button, gpointer user_data)
1027{
1028 struct SearchTab *tab = user_data;
1029 struct SearchResult *sr;
1030 GtkTreeModel *tm;
1031 GtkTreeIter iter;
1032
1033 tm = GTK_TREE_MODEL (tab->ts);
1034 if (TRUE != gtk_tree_model_get_iter_first (tm, &iter))
1035 return;
1036 /* FIXME-BUG: this is a tree, what about cleaning up
1037 of the children? */
1038 do
1039 {
1040 gtk_tree_model_get (tm, &iter, 9, &sr, -1);
1041 if ( (sr->download != NULL) &&
1042 (sr->download->is_done == GNUNET_YES) )
1043 {
1044 /* got a finished download, stop it */
1045 GNUNET_FS_download_stop (sr->download->dc, GNUNET_YES);
1046 }
1047 if ( (NULL == sr->download) &&
1048 (NULL == sr->result) )
1049 {
1050 /* no active download and no associated FS-API search result;
1051 so this must be some left-over entry from an opened
1052 directory; clean it up */
1053 free_search_result (sr);
1054 }
1055 }
1056 while (TRUE == gtk_tree_model_iter_next (tm, &iter));
1057}
1058
1059
1060/**
1061 * We received a search error message from the FS library.
1062 * Present it to the user in an appropriate form.
1063 *
1064 * @param tab search tab affected by the error
1065 * @param emsg the error message
1066 */
1067static void
1068handle_search_error (struct SearchTab *tab,
1069 const char *emsg)
1070{
1071 gtk_label_set_text (tab->label, _("Error!"));
1072 gtk_widget_set_tooltip_text (GTK_WIDGET (tab->label), emsg);
1073}
1074
1075
1076/**
1077 * Obtain the string we will use to describe a search result from
1078 * the respective meta data.
1079 *
1080 * @param meta meta data to inspect
1081 * @return description of the result in utf-8, never NULL
1082 */
1083static char *
1084get_description_from_metadata (const struct GNUNET_CONTAINER_MetaData *meta)
1085{
1086 char *desc;
1087 char *utf8_desc;
1088
1089 desc =
1090 GNUNET_CONTAINER_meta_data_get_first_by_types (meta,
1091 EXTRACTOR_METATYPE_PACKAGE_NAME,
1092 EXTRACTOR_METATYPE_TITLE,
1093 EXTRACTOR_METATYPE_BOOK_TITLE,
1094 EXTRACTOR_METATYPE_FILENAME,
1095 EXTRACTOR_METATYPE_DESCRIPTION,
1096 EXTRACTOR_METATYPE_SUMMARY,
1097 EXTRACTOR_METATYPE_ALBUM,
1098 EXTRACTOR_METATYPE_COMMENT,
1099 EXTRACTOR_METATYPE_SUBJECT,
1100 EXTRACTOR_METATYPE_KEYWORDS,
1101 -1);
1102 if (desc == NULL)
1103 return GNUNET_strdup (_("no description supplied"));
1104 utf8_desc =
1105 GNUNET_FS_GTK_dubious_meta_to_utf8 (EXTRACTOR_METAFORMAT_UTF8, desc,
1106 strlen (desc) + 1);
1107 GNUNET_free (desc);
1108 if (utf8_desc == NULL)
1109 return GNUNET_strdup (_("no description supplied"));
1110 return utf8_desc;
1111}
1112
1113
1114/**
1115 * Obtain the mime type (or format description) will use to describe a search result from
1116 * the respective meta data.
1117 *
1118 * @param meta meta data to inspect
1119 * @return mime type to use, possibly NULL
1120 */
1121static char *
1122get_mimetype_from_metadata (const struct GNUNET_CONTAINER_MetaData *meta)
1123{
1124 return GNUNET_CONTAINER_meta_data_get_first_by_types (meta,
1125 EXTRACTOR_METATYPE_MIMETYPE,
1126 EXTRACTOR_METATYPE_FORMAT,
1127 -1);
1128}
1129
1130
1131/**
1132 * Some additional information about a search result has been
1133 * received. Update the view accordingly.
1134 *
1135 * @param sr search result that is being updated
1136 * @param meta updated meta data
1137 * @param availability_rank updated availability information
1138 * @param availability_certainty updated availability certainty
1139 * @param applicability_rank updated applicability information
1140 */
1141static void
1142update_search_result (struct SearchResult *sr,
1143 const struct GNUNET_CONTAINER_MetaData *meta,
1144 int32_t availability_rank,
1145 uint32_t availability_certainty,
1146 uint32_t applicability_rank)
1147{
1148 GtkTreeIter iter;
1149 struct GNUNET_CONTAINER_MetaData *ometa;
1150 GtkTreeView *tv;
1151 GtkTreePath *tp;
1152 GtkTreeStore *ts;
1153 GtkTreeModel *tm;
1154 char *desc;
1155 char *mime;
1156 GdkPixbuf *pixbuf;
1157 guint percent_avail;
1158 GtkNotebook *notebook;
1159 gint page;
1160
1161 if (sr == NULL)
1162 {
1163 GNUNET_break (0);
1164 return;
1165 }
1166 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1167 "Updating search result SR=%p with %d, %u, %u\n",
1168 sr, availability_rank,
1169 availability_certainty, applicability_rank);
1170 desc = get_description_from_metadata (meta);
1171 mime = get_mimetype_from_metadata (meta);
1172 pixbuf = GNUNET_FS_GTK_get_thumbnail_from_meta_data (meta);
1173 tp = gtk_tree_row_reference_get_path (sr->rr);
1174 tm = gtk_tree_row_reference_get_model (sr->rr);
1175 ts = GTK_TREE_STORE (tm);
1176 gtk_tree_model_get_iter (tm, &iter, tp);
1177 gtk_tree_path_free (tp);
1178 gtk_tree_model_get (tm, &iter, 0, &ometa, -1);
1179 if (NULL != ometa)
1180 GNUNET_CONTAINER_meta_data_destroy (ometa);
1181 if (availability_certainty > 0)
1182 percent_avail =
1183 (availability_certainty +
1184 availability_rank) * 50 / availability_certainty;
1185 else
1186 percent_avail = 0;
1187 gtk_tree_store_set (ts, &iter,
1188 0, GNUNET_CONTAINER_meta_data_duplicate (meta),
1189 3, pixbuf /* preview */ ,
1190 5, (guint) percent_avail /* percent availability */ ,
1191 6, desc /* filename/description */ ,
1192 10, mime, 11, (guint) applicability_rank, 12,
1193 (guint) availability_certainty, 13,
1194 (gint) availability_rank, -1);
1195 if (pixbuf != NULL)
1196 g_object_unref (pixbuf);
1197 GNUNET_free (desc);
1198 GNUNET_free_non_null (mime);
1199
1200 notebook =
1201 GTK_NOTEBOOK (GNUNET_FS_GTK_get_main_window_object
1202 ("GNUNET_GTK_main_window_notebook"));
1203 page = gtk_notebook_get_current_page (notebook);
1204 if (gtk_notebook_get_nth_page (notebook, page) == sr->tab->frame)
1205 {
1206 tv = GTK_TREE_VIEW (gtk_builder_get_object
1207 (sr->tab->builder, "_search_result_frame"));
1208 update_meta_data_views (tv, sr->tab);
1209 }
1210}
1211
1212
1213/**
1214 * Add a search result to the given search tab. This function is called
1215 * not only for 'normal' search results but also for directories that
1216 * are being opened and if the user manually enters a URI.
1217 *
1218 * @param tab search tab to extend, never NULL
1219 * @param iter set to position where search result is added (OUT only)
1220 * @param parent_rr reference to parent entry in search tab, NULL for normal
1221 * search results,
1222 * @param uri uri to add, can be NULL for top-level entry of a directory opened from disk
1223 * (in this case, we don't know the URI and should probably not
1224 * bother to calculate it)
1225 * @param meta metadata of the entry
1226 * @param result associated FS search result (can be NULL if this result
1227 * was part of a directory)
1228 * @param applicability_rank how relevant is the result
1229 * @return struct representing the search result (also stored in the tree
1230 * model at 'iter')
1231 */
1232struct SearchResult *
1233GNUNET_GTK_add_search_result (struct SearchTab *tab,
1234 GtkTreeIter *iter,
1235 GtkTreeRowReference *parent_rr,
1236 const struct GNUNET_FS_Uri *uri,
1237 const struct GNUNET_CONTAINER_MetaData *meta,
1238 struct GNUNET_FS_SearchResult *result,
1239 uint32_t applicability_rank)
1240{
1241 struct SearchResult *sr;
1242 GtkTreePath *tp;
1243 const char *status_colour;
1244 char *desc;
1245 char *mime;
1246 char *uris;
1247 GdkPixbuf *pixbuf;
1248 GtkTreeIter *pitr;
1249 GtkTreeIter pmem;
1250 GtkTreePath *path;
1251 GtkTreeModel *tm;
1252 GtkTreeStore *ts;
1253 uint64_t fsize;
1254
1255 if (NULL == uri)
1256 {
1257 /* opened directory file */
1258 fsize = 0;
1259 status_colour = "gray";
1260 mime = NULL; /* FIXME-FEATURE-MAYBE: should we set mime to directory? */
1261 uris = GNUNET_strdup (_("no URI"));
1262 }
1263 else
1264 {
1265 if ( (GNUNET_FS_uri_test_loc (uri)) ||
1266 (GNUNET_FS_uri_test_chk (uri)) )
1267 {
1268 fsize = GNUNET_FS_uri_chk_get_file_size (uri);
1269 mime = get_mimetype_from_metadata (meta);
1270 status_colour = "white";
1271 }
1272 else
1273 {
1274 /* FIXME-FEATURE-MAYBE: create mime type for namespaces? */
1275 /* FIXME-BUG-MAYBE: can we encounter ksk URIs here too? */
1276 fsize = 0;
1277 mime = GNUNET_strdup ("GNUnet namespace");
1278 status_colour = "lightgreen";
1279 }
1280 uris = GNUNET_FS_uri_to_string (uri);
1281 }
1282 desc = get_description_from_metadata (meta);
1283 pixbuf = GNUNET_FS_GTK_get_thumbnail_from_meta_data (meta);
1284
1285 sr = GNUNET_malloc (sizeof (struct SearchResult));
1286 sr->result = result;
1287 sr->tab = tab;
1288 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1289 "Allocated a search result SR=%p\n",
1290 sr);
1291 if (parent_rr != NULL)
1292 {
1293 /* get piter from parent */
1294 path = gtk_tree_row_reference_get_path (parent_rr);
1295 tm = gtk_tree_row_reference_get_model (parent_rr);
1296 if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (tm), &pmem, path))
1297 {
1298 GNUNET_break (0);
1299 gtk_tree_path_free (path);
1300 /* desperate measure: make top-level entry */
1301 pitr = NULL;
1302 }
1303 else
1304 {
1305 pitr = &pmem;
1306 }
1307 ts = GTK_TREE_STORE (tm);
1308 }
1309 else
1310 {
1311 /* top-level result */
1312 pitr = NULL;
1313 ts = tab->ts;
1314 }
1315 gtk_tree_store_insert_with_values (ts, iter, pitr, G_MAXINT,
1316 0, GNUNET_CONTAINER_meta_data_duplicate (meta),
1317 1, (uri == NULL) ? NULL : GNUNET_FS_uri_dup (uri),
1318 2, fsize,
1319 3, pixbuf /* preview */ ,
1320 4, 0 /* percent progress */ ,
1321 5, 0 /* percent availability */ ,
1322 6, desc /* filename/description */ ,
1323 7, uris,
1324 8, status_colour,
1325 9, sr,
1326 10, mime,
1327 11, applicability_rank,
1328 12, 0 /* avail-cert */ ,
1329 13, 0, /* avail-rank */
1330 14, (guint64) 0, /* completed */
1331 15, NULL, /* downloaded_filename */
1332 16, -1, /* downloaded_anonymity */
1333 -1);
1334 if (pixbuf != NULL)
1335 g_object_unref (pixbuf);
1336 GNUNET_free (uris);
1337 GNUNET_free (desc);
1338 GNUNET_free_non_null (mime);
1339
1340 /* remember in 'sr' where we added the result */
1341 tp = gtk_tree_model_get_path (GTK_TREE_MODEL (ts), iter);
1342 sr->rr = gtk_tree_row_reference_new (GTK_TREE_MODEL (ts), tp);
1343 gtk_tree_path_free (tp);
1344
1345 /* move up to the outermost tab, in case this is an 'inner'
1346 search (namespace update case) */
1347 while (tab->parent != NULL)
1348 tab = tab->parent->tab;
1349 tab->num_results++;
1350
1351 return sr;
1352}
1353
1354
1355/**
1356 * We have received a search result from the FS API. Add it to the
1357 * respective search tab. The search result can be an 'inner'
1358 * search result (updated result for a namespace search) or a
1359 * top-level search result. Update the tree view and the label
1360 * of the search tab accordingly.
1361 *
1362 * @param tab the search tab where the new result should be added
1363 * @param parent parent search result (if this is a namespace update result), or NULL
1364 * @param uri URI of the search result
1365 * @param meta meta data for the result
1366 * @param result FS API handle to the result
1367 * @param applicability_rank how applicable is the result to the query
1368 * @return struct representing the search result (also stored in the tree
1369 * model at 'iter')
1370 */
1371static struct SearchResult *
1372process_search_result (struct SearchTab *tab,
1373 struct SearchResult *parent,
1374 const struct GNUNET_FS_Uri *uri,
1375 const struct GNUNET_CONTAINER_MetaData *meta,
1376 struct GNUNET_FS_SearchResult *result,
1377 uint32_t applicability_rank)
1378{
1379 struct SearchResult *sr;
1380 GtkTreeIter iter;
1381
1382 sr = GNUNET_GTK_add_search_result (tab, &iter,
1383 (parent != NULL) ? parent->rr : NULL,
1384 uri,
1385 meta, result, applicability_rank);
1386 update_search_label (tab);
1387 return sr;
1388}
1389
1390
1391/**
1392 * Setup a new search tab.
1393 *
1394 * @param sc context with FS for the search, NULL for none (open-URI/orphan tab)
1395 * @param query the query, NULL for none (open-URI/orphan tab)
1396 * @return search tab handle
1397 */
1398static struct SearchTab *
1399setup_search_tab (struct GNUNET_FS_SearchContext *sc,
1400 const struct GNUNET_FS_Uri *query)
1401{
1402 struct SearchTab *tab;
1403 GtkTreeView *tv;
1404 GtkNotebook *notebook;
1405 GtkWindow *sf;
1406 gint pages;
1407
1408 tab = GNUNET_malloc (sizeof (struct SearchTab));
1409 GNUNET_CONTAINER_DLL_insert (search_tab_head, search_tab_tail, tab);
1410 tab->sc = sc;
1411 if (query == NULL)
1412 {
1413 /* no real query, tab is for non-queries, use special label */
1414 tab->query_txt = GNUNET_strdup ("*");
1415 }
1416 else
1417 {
1418 /* FS_uri functions should produce UTF-8, so let them be */
1419 if (GNUNET_FS_uri_test_ksk (query))
1420 tab->query_txt = GNUNET_FS_uri_ksk_to_string_fancy (query);
1421 else
1422 tab->query_txt = GNUNET_FS_uri_to_string (query);
1423 }
1424 tab->builder = GNUNET_GTK_get_new_builder ("gnunet_fs_gtk_search_tab.glade",
1425 tab);
1426 tab->ts =
1427 GTK_TREE_STORE (gtk_builder_get_object
1428 (tab->builder,
1429 "GNUNET_GTK_file_sharing_result_tree_store"));
1430 /* load frame */
1431 sf = GTK_WINDOW (gtk_builder_get_object
1432 (tab->builder, "_search_result_frame_window"));
1433 tab->frame = gtk_bin_get_child (GTK_BIN (sf));
1434 g_object_ref (tab->frame);
1435 gtk_container_remove (GTK_CONTAINER (sf), tab->frame);
1436 gtk_widget_destroy (GTK_WIDGET (sf));
1437
1438 /* load tab_label */
1439 sf = GTK_WINDOW (gtk_builder_get_object
1440 (tab->builder, "_search_result_label_window"));
1441 tab->tab_label = gtk_bin_get_child (GTK_BIN (sf));
1442 g_object_ref (tab->tab_label);
1443 gtk_container_remove (GTK_CONTAINER (sf), tab->tab_label);
1444 gtk_widget_destroy (GTK_WIDGET (sf));
1445
1446 /* get refs to widgets */
1447 tab->label =
1448 GTK_LABEL (gtk_builder_get_object
1449 (tab->builder, "_search_result_label_window_label"));
1450
1451 /* FIXME-UNCLEAN: connect these signals using glade!!! */
1452 tab->close_button =
1453 GTK_WIDGET (gtk_builder_get_object
1454 (tab->builder, "_search_result_label_close_button"));
1455 g_signal_connect (G_OBJECT (tab->close_button), "clicked",
1456 G_CALLBACK (stop_search), tab);
1457 tab->clear_button =
1458 GTK_WIDGET (gtk_builder_get_object
1459 (tab->builder, "_search_result_label_clear_button"));
1460 g_signal_connect (G_OBJECT (tab->clear_button), "clicked",
1461 G_CALLBACK (clear_downloads), tab);
1462 tab->play_button =
1463 GTK_WIDGET (gtk_builder_get_object
1464 (tab->builder, "_search_result_label_play_button"));
1465 g_signal_connect (G_OBJECT (tab->play_button), "clicked",
1466 G_CALLBACK (continue_search), tab);
1467 tab->pause_button =
1468 GTK_WIDGET (gtk_builder_get_object
1469 (tab->builder, "_search_result_label_pause_button"));
1470 g_signal_connect (G_OBJECT (tab->pause_button), "clicked",
1471 G_CALLBACK (pause_search), tab);
1472 /* patch text */
1473 update_search_label (tab);
1474
1475 /* add signal handlers; FIXME-UNCLEAN: again, connect these with glade... */
1476 tv = GTK_TREE_VIEW (gtk_builder_get_object
1477 (tab->builder, "_search_result_frame"));
1478 g_signal_connect (G_OBJECT (tv), "row-activated",
1479 G_CALLBACK (start_download_row_activated), tab);
1480 g_signal_connect (G_OBJECT (tv), "cursor-changed",
1481 G_CALLBACK (update_meta_data_views), tab);
1482 g_signal_connect (G_OBJECT (tv), "button_press_event",
1483 G_CALLBACK (search_list_on_menu), tab);
1484 g_signal_connect (G_OBJECT (tv), "popup-menu",
1485 G_CALLBACK (search_list_on_popup), tab);
1486
1487
1488 /* make visible */
1489 notebook =
1490 GTK_NOTEBOOK (GNUNET_FS_GTK_get_main_window_object
1491 ("GNUNET_GTK_main_window_notebook"));
1492 pages = gtk_notebook_get_n_pages (notebook);
1493 gtk_notebook_insert_page (notebook, tab->frame, tab->tab_label, pages - 1);
1494 gtk_notebook_set_current_page (notebook, pages - 1);
1495 gtk_widget_show (GTK_WIDGET (notebook));
1496 return tab;
1497}
1498
1499
1500/**
1501 * Setup an "inner" search, that is a subtree representing namespace
1502 * 'update' results. We use a 'struct SearchTab' to represent this
1503 * sub-search. In the GUI, the presentation is similar to search
1504 * results in a directory, except that this is for a namespace search
1505 * result that gave pointers to an alternative keyword to use and this
1506 * is the set of the results found for this alternative keyword.
1507 *
1508 * All of the 'widget' elements of the returned 'search tab' reference
1509 * the parent search. The whole construction is essentially a trick
1510 * to allow us to store the FS-API's 'SearchContext' somewhere and to
1511 * find it when we get this kind of 'inner' search results (so that we
1512 * can then place them in the tree view in the right spot).
1513 *
1514 * FIXME-BUG-MAYBE: don't we need a bit more information then? Like exactly where
1515 * this 'right spot' is? Not sure how just having 'sc' helps there,
1516 * as it is not a search result (!) to hang this up on! This might
1517 * essentially boil down to an issue with the FS API, not sure...
1518 *
1519 * @param sc context with FS for the search
1520 * @param parent parent search tab
1521 * @return struct representing the search result (also stored in the tree
1522 * model at 'iter')
1523 */
1524static struct SearchTab *
1525setup_inner_search (struct GNUNET_FS_SearchContext *sc,
1526 struct SearchResult *parent)
1527{
1528 struct SearchTab *ret;
1529
1530 ret = GNUNET_malloc (sizeof (struct SearchTab));
1531 ret->parent = parent;
1532 ret->sc = sc;
1533 ret->query_txt = parent->tab->query_txt;
1534 ret->builder = parent->tab->builder;
1535 ret->frame = parent->tab->frame;
1536 ret->tab_label = parent->tab->tab_label;
1537 ret->close_button = parent->tab->close_button;
1538 ret->clear_button = parent->tab->clear_button;
1539 ret->play_button = parent->tab->play_button;
1540 ret->label = parent->tab->label;
1541
1542 return ret;
1543}
1544
1545
1546/**
1547 * Setup a new top-level entry in the URI/orphan tab. If necessary, create
1548 * the URI tab first.
1549 *
1550 * @param iter set to the new entry (OUT only)
1551 * @param srp set to search result (can be NULL)
1552 * @param meta metadata for the new entry
1553 * @param uri URI for the new entry
1554 * @return the 'uri_tab' the result was added to
1555 */
1556struct SearchTab *
1557GNUNET_GTK_add_to_uri_tab (GtkTreeIter *iter, struct SearchResult **srp,
1558 const struct GNUNET_CONTAINER_MetaData *meta,
1559 const struct GNUNET_FS_Uri *uri)
1560{
1561 struct SearchResult *sr;
1562 GtkNotebook *notebook;
1563 gint page;
1564
1565 if (NULL == uri_tab)
1566 {
1567 uri_tab = setup_search_tab (NULL, NULL);
1568 gtk_widget_set_visible (uri_tab->close_button, FALSE);
1569 gtk_widget_set_visible (uri_tab->pause_button, FALSE);
1570 }
1571 /* make 'uri_tab' the current page */
1572 notebook =
1573 GTK_NOTEBOOK (GNUNET_FS_GTK_get_main_window_object
1574 ("GNUNET_GTK_main_window_notebook"));
1575 for (page = 0; page < gtk_notebook_get_n_pages (notebook); page++)
1576 if (uri_tab->frame == gtk_notebook_get_nth_page (notebook, page))
1577 {
1578 gtk_notebook_set_current_page (notebook, page);
1579 break;
1580 }
1581 sr = GNUNET_GTK_add_search_result (uri_tab, iter, NULL, uri, meta, NULL, 0);
1582 if (NULL != srp)
1583 *srp = sr;
1584 return uri_tab;
1585}
1586
1587
1588
1589/* ***************** Download event handling ****************** */
1590
1591
1592
1593/**
1594 * Change the (background) color of the given download entry.
1595 *
1596 * @param de entry to change
1597 * @param color name of the color to use
1598 */
1599static void
1600change_download_color (struct DownloadEntry *de,
1601 const char *color)
1602{
1603 GtkTreeIter iter;
1604 GtkTreePath *path;
1605
1606 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1607 "Changing download DE=%p color to %s\n",
1608 de, color);
1609 path = gtk_tree_row_reference_get_path (de->rr);
1610 if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (de->ts), &iter, path))
1611 {
1612 GNUNET_break (0);
1613 gtk_tree_path_free (path);
1614 return;
1615 }
1616 gtk_tree_path_free (path);
1617 gtk_tree_store_set (de->ts, &iter, 8, color, -1);
1618}
1619
1620
1621/**
1622 * A download operation was stopped. Remove all state associated with
1623 * it and reset the search result's background color to 'white'.
1624 *
1625 * @param de the download that was stopped
1626 */
1627static void
1628stop_download (struct DownloadEntry *de)
1629{
1630 GtkTreeIter iter;
1631 GtkTreePath *path;
1632 GtkTreeModel *tm;
1633 struct SearchResult *search_result;
1634
1635 tm = gtk_tree_row_reference_get_model (de->rr);
1636 path = gtk_tree_row_reference_get_path (de->rr);
1637 if (! gtk_tree_model_get_iter (tm, &iter, path))
1638 {
1639 gtk_tree_path_free (path);
1640 GNUNET_break (0);
1641 return;
1642 }
1643 gtk_tree_path_free (path);
1644 gtk_tree_model_get (tm, &iter, 9, &search_result, -1);
1645 GNUNET_assert (search_result->download == de);
1646 search_result->download = NULL;
1647 change_download_color (de, "white");
1648 gtk_tree_row_reference_free (de->rr);
1649 GNUNET_FS_uri_destroy (de->uri);
1650 GNUNET_CONTAINER_meta_data_destroy (de->meta);
1651 GNUNET_free (de);
1652}
1653
1654
1655/**
1656 * Closure for 'add_directory_entry'.
1657 */
1658struct AddDirectoryEntryContext
1659{
1660
1661 /**
1662 * Search tab where we need to expand the result list.
1663 */
1664 struct SearchTab *tab;
1665
1666 /**
1667 * Row reference of parent (the directory).
1668 */
1669 GtkTreeRowReference *prr;
1670
1671 /**
1672 * Do we need to check if the given entry already exists to
1673 * avoid adding it twice? Set to YES if 'add_directory_entry'
1674 * is called upon directory completion (so we might see all
1675 * entries again) and to NO if this is the initial download
1676 * and we're calling during a 'PROGRESS' event.
1677 */
1678 int check_duplicates;
1679
1680};
1681
1682
1683/**
1684 * Function used to process entries in a directory. Whenever we
1685 * download a directory, this function is called on the entries in the
1686 * directory to add them to the search tab. Note that the function
1687 * maybe called twice for the same entry, once during incremental
1688 * processing and later once more when we have the complete directory.
1689 *
1690 * For the second round, the 'check_duplicates' flag will be set in
1691 * the closure. If called on an entry that already exists, the
1692 * function should simply do nothing.
1693 *
1694 * @param cls closure, our 'struct AddDirectoryEntryContext*'
1695 * @param filename name of the file in the directory
1696 * @param uri URI of the file, NULL for the directory itself
1697 * @param metadata metadata for the file; metadata for
1698 * the directory if everything else is NULL/zero
1699 * @param length length of the available data for the file
1700 * (of type size_t since data must certainly fit
1701 * into memory; if files are larger than size_t
1702 * permits, then they will certainly not be
1703 * embedded with the directory itself).
1704 * @param data data available for the file (length bytes)
1705 */
1706static void
1707add_directory_entry (void *cls, const char *filename,
1708 const struct GNUNET_FS_Uri *uri,
1709 const struct GNUNET_CONTAINER_MetaData *meta,
1710 size_t length, const void *data)
1711{
1712 struct AddDirectoryEntryContext *ade = cls;
1713 GtkTreeIter iter;
1714 GtkTreeIter piter;
1715 GtkTreePath *path;
1716 GtkTreeModel *tm;
1717 struct GNUNET_FS_Uri *xuri;
1718
1719 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1720 "Adding directory entry `%s'\n",
1721 filename);
1722
1723 if (NULL == uri)
1724 {
1725 /* directory meta data itself */
1726 /* FIXME-FEATURE-MAYBE: consider merging it with the meta data from
1727 the original search result... */
1728 return;
1729 }
1730 if (ade->check_duplicates == GNUNET_YES)
1731 {
1732 tm = gtk_tree_row_reference_get_model (ade->prr);
1733 path = gtk_tree_row_reference_get_path (ade->prr);
1734 if (! gtk_tree_model_get_iter (tm, &piter, path))
1735 {
1736 GNUNET_break (0);
1737 gtk_tree_path_free (path);
1738 return;
1739 }
1740 gtk_tree_path_free (path);
1741 if (TRUE == gtk_tree_model_iter_children (tm, &iter, &piter))
1742 {
1743 do
1744 {
1745 gtk_tree_model_get (tm, &iter, 1, &xuri, -1);
1746 if (GNUNET_YES == GNUNET_FS_uri_test_equal (xuri, uri))
1747 return; /* already present */
1748 }
1749 while (TRUE == gtk_tree_model_iter_next (tm, &iter));
1750 }
1751 }
1752 GNUNET_GTK_add_search_result (ade->tab, &iter, ade->prr, uri, meta, NULL,
1753 0);
1754}
1755
1756
1757/**
1758 * We got an event that some download is progressing. Update the tree
1759 * model accordingly. If the download is a directory, try to display
1760 * the contents.
1761 *
1762 * @param de download entry that is progressing
1763 * @param size overall size of the download
1764 * @param completed number of bytes we have completed
1765 * @param block_data current block we've downloaded
1766 * @param offset offset of block_data in the overall file
1767 * @param block_size number of bytes in block_data
1768 * @param depth depth of the block in the ECRS tree
1769 */
1770static void
1771mark_download_progress (struct DownloadEntry *de, uint64_t size,
1772 uint64_t completed, const void *block_data,
1773 uint64_t offset, uint64_t block_size,
1774 unsigned int depth)
1775{
1776 GtkTreeIter iter;
1777 GtkTreePath *path;
1778
1779 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1780 "Marking download progress for DE=%p, %llu/%llu, %llu@%llu depth=%u\n",
1781 de, completed, size, block_size, offset, depth);
1782
1783 path = gtk_tree_row_reference_get_path (de->rr);
1784 if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (de->ts), &iter, path))
1785 {
1786 GNUNET_break (0);
1787 gtk_tree_path_free (path);
1788 return;
1789 }
1790 gtk_tree_path_free (path);
1791 /* FIXME-FEATURE: update availability-score here as well! */
1792 gtk_tree_store_set (de->ts, &iter,
1793 4, (guint) ((size >
1794 0) ? (100 * completed /
1795 size) : 100) /* progress */ ,
1796 14, completed,
1797 -1);
1798 if ( (depth == 0) &&
1799 (block_size > 0) &&
1800 (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (de->meta)) )
1801 {
1802 /* got a data block of a directory, list its contents */
1803 struct AddDirectoryEntryContext ade;
1804
1805 ade.tab = de->tab;
1806 ade.prr = de->rr;
1807 ade.check_duplicates = GNUNET_NO;
1808 if (GNUNET_SYSERR ==
1809 GNUNET_FS_directory_list_contents ((size_t) block_size, block_data,
1810 offset, &add_directory_entry, &ade))
1811 {
1812 /* Mime type was wrong, this is not a directory, update model! */
1813 GNUNET_break (GNUNET_OK ==
1814 GNUNET_CONTAINER_meta_data_delete (de->meta,
1815 EXTRACTOR_METATYPE_MIMETYPE, NULL, 0));
1816 gtk_tree_store_set (de->ts, &iter,
1817 10, "" /* unknown mime type */, -1);
1818 }
1819 }
1820}
1821
1822
1823/**
1824 * FS-API encountered an error downloading a file. Update the
1825 * view accordingly.
1826 *
1827 * @param de download that had an error
1828 * @param emsg error message to display
1829 */
1830static void
1831mark_download_error (struct DownloadEntry *de,
1832 const char *emsg)
1833{
1834 GtkTreeIter iter;
1835 GtkTreePath *path;
1836
1837 change_download_color (de, "red");
1838 de->is_done = GNUNET_YES;
1839 path = gtk_tree_row_reference_get_path (de->rr);
1840 if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (de->tab->ts), &iter, path))
1841 {
1842 GNUNET_break (0);
1843 gtk_tree_path_free (path);
1844 return;
1845 }
1846 gtk_tree_path_free (path);
1847 gtk_tree_store_set (de->tab->ts, &iter, 4, 0, 7, emsg, -1);
1848}
1849
1850
1851/**
1852 * FS-API notified us that we're done with a download. Update the
1853 * view accordingly. If the download is a directory, try to display
1854 * the contents.
1855 *
1856 * @param de download that has finished
1857 * @param size overall size of the file
1858 * @param filename name of the downloaded file on disk (possibly a temporary file)
1859 */
1860static void
1861mark_download_completed (struct DownloadEntry *de, uint64_t size,
1862 const char *filename)
1863{
1864 struct AddDirectoryEntryContext ade;
1865
1866 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1867 "Marking download completed for DE=%p, %llu-byte `%s'\n",
1868 de, size, filename);
1869
1870 de->is_done = GNUNET_YES;
1871 mark_download_progress (de, size, size, NULL, 0, 0, 0);
1872 if ( (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (de->meta)) &&
1873 (filename != NULL) )
1874 {
1875 /* download was for a directory (and we have a temp file for scanning);
1876 add contents of the directory to the view */
1877 ade.tab = de->tab;
1878 ade.prr = de->rr;
1879 ade.check_duplicates = GNUNET_YES;
1880 GNUNET_FS_GTK_mmap_and_scan (filename, &add_directory_entry, &ade);
1881 }
1882 change_download_color (de, "green");
1883}
1884
1885
1886/**
1887 * Copy all of the children of 'src_iter' from the 'src_model' to
1888 * become children of 'dst_iter' in the 'dst_model'. The models are
1889 * both 'GNUNET_GTK_file_sharing_result_tree_store' models.
1890 *
1891 * Note that we also need to update the 'struct SearchResult'
1892 * and (if it exists) the respective 'struct DownloadEntry'
1893 * to refer to the new model.
1894 *
1895 * @param src_model source model
1896 * @param src_iter parent of the nodes to move
1897 * @param dst_model destination model
1898 * @param dst_iter new parent of the entries we are moving
1899 */
1900static void
1901copy_children (GtkTreeModel * src_model, GtkTreeIter * src_iter,
1902 GtkTreeModel * dst_model, GtkTreeIter * dst_iter)
1903{
1904 GtkTreeIter src_child;
1905 GtkTreeIter dst_child;
1906 GtkTreePath *path;
1907 struct GNUNET_CONTAINER_MetaData *meta;
1908 struct GNUNET_FS_Uri *uri;
1909 guint64 filesize, completed;
1910 GdkPixbuf *preview;
1911 guint percent_progress;
1912 guint percent_availability;
1913 gchar *filename;
1914 gchar *uri_as_string;
1915 gchar *status_colour;
1916 struct SearchResult *search_result;
1917 gchar *mimetype;
1918 guint applicability_rank;
1919 guint availability_certainty;
1920 gint availability_rank;
1921 gchar *downloaded_filename;
1922 gint downloaded_anonymity;
1923
1924 if (! gtk_tree_model_iter_children (src_model, &src_child, src_iter))
1925 return;
1926 do
1927 {
1928 gtk_tree_model_get (src_model, &src_child, 0, &meta, 1, &uri, 2,
1929 &filesize, 3, &preview, 4, &percent_progress, 5,
1930 &percent_availability, 6, &filename, 7,
1931 &uri_as_string, 8, &status_colour, 9, &search_result,
1932 10, &mimetype, 11, &applicability_rank, 12,
1933 &availability_certainty, 13, &availability_rank, 14,
1934 &completed, 15, &downloaded_filename, 16,
1935 &downloaded_anonymity, -1);
1936 gtk_tree_store_insert_with_values (GTK_TREE_STORE (dst_model), &dst_child,
1937 dst_iter, G_MAXINT, 0, meta, 1, uri, 2,
1938 filesize, 3, preview, 4,
1939 percent_progress, 5,
1940 percent_availability, 6, filename, 7,
1941 uri_as_string, 8, status_colour, 9,
1942 search_result, 10, mimetype, 11,
1943 applicability_rank, 12,
1944 availability_certainty, 13,
1945 availability_rank, 14, completed, 15,
1946 downloaded_filename, 16,
1947 downloaded_anonymity, -1);
1948 g_free (filename);
1949 g_free (downloaded_filename);
1950 g_free (uri_as_string);
1951 g_free (status_colour);
1952 g_free (mimetype);
1953 if (preview != NULL)
1954 g_object_unref (preview);
1955 gtk_tree_row_reference_free (search_result->rr);
1956 path = gtk_tree_model_get_path (dst_model, &dst_child);
1957 search_result->rr = gtk_tree_row_reference_new (dst_model, path);
1958 search_result->result = NULL;
1959 gtk_tree_path_free (path);
1960 if (search_result->download != NULL)
1961 {
1962 search_result->download->ts = GTK_TREE_STORE (dst_model);
1963 gtk_tree_row_reference_free (search_result->download->rr);
1964 search_result->download->rr =
1965 gtk_tree_row_reference_copy (search_result->rr);
1966 }
1967 copy_children (src_model, &src_child, dst_model, &dst_child);
1968 }
1969 while (TRUE == gtk_tree_model_iter_next (src_model, &src_child));
1970}
1971
1972
1973/**
1974 * Delete the entire given subtree from the model. Does not free
1975 * anything inside of the respective model's fields (since they have
1976 * been moved).
1977 *
1978 * @param model model that contains the subtree to remove
1979 * @param iter root of the subtree to remove
1980 */
1981static void
1982delete_stale_subtree (GtkTreeModel * model, GtkTreeIter * iter)
1983{
1984 GtkTreeIter child;
1985
1986 while (TRUE == gtk_tree_model_iter_children (model, &child, iter))
1987 delete_stale_subtree (model, &child);
1988 gtk_tree_store_remove (GTK_TREE_STORE (model), iter);
1989}
1990
1991
1992/**
1993 * Handle the case where an active download lost its
1994 * search parent by moving it to the URI tab.
1995 *
1996 * @param de download where the parent (i.e. search) was lost
1997 */
1998static void
1999download_lost_parent (struct DownloadEntry *de)
2000{
2001 GtkTreeIter iter;
2002 GtkTreePath *path;
2003 struct SearchTab *tab;
2004 GtkTreeRowReference *rr_old;
2005 GtkTreeModel *tm_old;
2006 GtkTreeIter iter_old;
2007 GtkTreeIter child;
2008 GtkTreeModel *model;
2009
2010 /* first, move the root of the respective 'de'-tree */
2011 rr_old = de->rr;
2012 tab = GNUNET_GTK_add_to_uri_tab (&iter, &de->sr, de->meta, de->uri);
2013 de->sr->download = de;
2014 de->ts = tab->ts;
2015 model = GTK_TREE_MODEL (de->ts);
2016 path = gtk_tree_model_get_path (model, &iter);
2017 de->rr = gtk_tree_row_reference_new (model, path);
2018 gtk_tree_path_free (path);
2019 tm_old = gtk_tree_row_reference_get_model (rr_old);
2020 path = gtk_tree_row_reference_get_path (rr_old);
2021 gtk_tree_row_reference_free (rr_old);
2022 if (! gtk_tree_model_get_iter (tm_old, &iter_old, path))
2023 {
2024 GNUNET_break (0);
2025 gtk_tree_path_free (path);
2026 return;
2027 }
2028 gtk_tree_path_free (path);
2029
2030 /* finally, move all children over as well */
2031 copy_children (tm_old, &iter_old, model, &iter);
2032 while (gtk_tree_model_iter_children (model, &child, &iter))
2033 delete_stale_subtree (model, &child);
2034}
2035
2036
2037/**
2038 * Setup a new download entry.
2039 *
2040 * @param de existing download entry for the download, or NULL (in which case we create a fresh one)
2041 * @param pde parent download entry, or NULL
2042 * @param sr search result, or NULL
2043 * @param dc download context (for stopping)
2044 * @param uri the URI, must not be NULL
2045 * @param filename filename on disk
2046 * @param meta metadata
2047 * @param size total size
2048 * @param completed current progress
2049 * @return download entry struct for the download (equal to 'de' if 'de' was not NULL)
2050 */
2051static struct DownloadEntry *
2052setup_download (struct DownloadEntry *de, struct DownloadEntry *pde,
2053 struct SearchResult *sr, struct GNUNET_FS_DownloadContext *dc,
2054 const struct GNUNET_FS_Uri *uri, const char *filename,
2055 const struct GNUNET_CONTAINER_MetaData *meta, uint64_t size,
2056 uint64_t completed)
2057{
2058 GtkTreeIter iter;
2059 GtkTreePath *path;
2060 struct SearchResult *srp;
2061
2062 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2063 "Setting up download, initially DE=%p, PDE=%p for %p & %p into %llu/%llu `%s'\n",
2064 de, pde, sr, dc, completed, size, filename);
2065 GNUNET_assert (NULL != uri);
2066 srp = NULL;
2067 if (NULL == de)
2068 {
2069 /* no existing download entry to build on, create a fresh one */
2070 de = GNUNET_malloc (sizeof (struct DownloadEntry));
2071 de->uri = GNUNET_FS_uri_dup (uri);
2072 }
2073 else
2074 {
2075 GNUNET_assert (GNUNET_YES == GNUNET_FS_uri_test_equal (de->uri, uri));
2076 }
2077 de->dc = dc;
2078 de->sr = sr;
2079 de->pde = pde;
2080 if ( (meta != NULL) && (de->meta == NULL) )
2081 de->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
2082 if (NULL != sr)
2083 {
2084 /* got a search result; display the download in the same location as the search result */
2085 GNUNET_assert (sr->download == NULL);
2086 sr->download = de;
2087 de->rr = gtk_tree_row_reference_copy (sr->rr);
2088 de->ts = sr->tab->ts;
2089 de->tab = sr->tab;
2090 srp = sr;
2091 }
2092 if (NULL == de->rr)
2093 {
2094 /* Stand-alone download with no 'row'/search result affiliated
2095 with the download so far; create a fresh entry for this
2096 download in the URI tab */
2097 de->tab = GNUNET_GTK_add_to_uri_tab (&iter, &srp, meta, uri);
2098 de->ts = de->tab->ts;
2099 path = gtk_tree_model_get_path (GTK_TREE_MODEL (de->ts), &iter);
2100 de->rr = gtk_tree_row_reference_new (GTK_TREE_MODEL (de->ts), path);
2101 gtk_tree_path_free (path);
2102 srp->download = de;
2103 }
2104 path = gtk_tree_row_reference_get_path (de->rr);
2105 if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (de->ts), &iter, path))
2106 {
2107 GNUNET_break (0);
2108 gtk_tree_path_free (path);
2109 return de;
2110 }
2111 gtk_tree_path_free (path);
2112 gtk_tree_store_set (de->ts, &iter,
2113 4, (guint) ((size >
2114 0) ? (100 * completed /
2115 size) : 100) /* progress */ ,
2116 6, filename /* filename/description */ ,
2117 8, "blue" /* status colour: pending */ ,
2118 9, srp,
2119 14, completed,
2120 -1);
2121 return de;
2122}
2123
2124
2125
2126/* ***************** Publish event handling ****************** */
2127
2128
2129
2130/**
2131 * Change the (background) color of the given publish entry.
2132 *
2133 * @param pe entry to change
2134 * @param color name of the color to use
2135 */
2136static void
2137change_publish_color (struct PublishEntry *pe,
2138 const char *color)
2139{
2140 GtkTreeIter iter;
2141 GtkTreePath *path;
2142
2143 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2144 "Changing publish PE=%p color to %s\n",
2145 pe, color);
2146 path = gtk_tree_row_reference_get_path (pe->rr);
2147 if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (pe->tab->ts), &iter, path))
2148 {
2149 GNUNET_break (0);
2150 gtk_tree_path_free (path);
2151 return;
2152 }
2153 gtk_tree_path_free (path);
2154 gtk_tree_store_set (pe->tab->ts, &iter, 2, color, -1);
2155}
2156
2157
2158/**
2159 * We got an event that some publishing operation is progressing.
2160 * Update the tree model accordingly.
2161 *
2162 * @param pe publish entry that is progressing
2163 * @param size overall size of the file or directory
2164 * @param completed number of bytes we have completed
2165 */
2166static void
2167mark_publish_progress (struct PublishEntry *pe, uint64_t size,
2168 uint64_t completed)
2169{
2170 GtkTreeIter iter;
2171 GtkTreePath *path;
2172
2173 path = gtk_tree_row_reference_get_path (pe->rr);
2174 if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (pe->tab->ts), &iter, path))
2175 {
2176 GNUNET_break (0);
2177 gtk_tree_path_free (path);
2178 return;
2179 }
2180 gtk_tree_path_free (path);
2181 gtk_tree_store_set (pe->tab->ts, &iter, 3,
2182 (guint) ((size >
2183 0) ? (100 * completed /
2184 size) : 100) /* progress */ ,
2185 -1);
2186}
2187
2188
2189/**
2190 * FS-API notified us that we're done with some publish operation.
2191 * Update the view accordingly.
2192 *
2193 * @param pe publish operation that has finished
2194 * @param uri resulting URI
2195 */
2196static void
2197handle_publish_completed (struct PublishEntry *pe,
2198 const struct GNUNET_FS_Uri *uri)
2199{
2200 GtkTreeIter iter;
2201 GtkTreePath *path;
2202 char *uris;
2203
2204 path = gtk_tree_row_reference_get_path (pe->rr);
2205 if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (pe->tab->ts), &iter, path))
2206 {
2207 GNUNET_break (0);
2208 gtk_tree_path_free (path);
2209 return;
2210 }
2211 gtk_tree_path_free (path);
2212 pe->uri = GNUNET_FS_uri_dup (uri);
2213 uris = GNUNET_FS_uri_to_string (uri);
2214 gtk_tree_store_set (pe->tab->ts, &iter,
2215 5, uris,
2216 -1);
2217 GNUNET_free (uris);
2218 change_publish_color (pe, "green");
2219}
2220
2221
2222/**
2223 * We received a publish error message from the FS library.
2224 * Present it to the user in an appropriate form.
2225 *
2226 * @param pe publishing operation affected by the error
2227 * @param emsg the error message
2228 */
2229static void
2230handle_publish_error (struct PublishEntry *pe,
2231 const char *emsg)
2232{
2233 GtkTreeIter iter;
2234 GtkTreePath *path;
2235
2236 path = gtk_tree_row_reference_get_path (pe->rr);
2237 if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (pe->tab->ts), &iter, path))
2238 {
2239 GNUNET_break (0);
2240 gtk_tree_path_free (path);
2241 return;
2242 }
2243 gtk_tree_path_free (path);
2244 gtk_tree_store_set (pe->tab->ts, &iter,
2245 5, emsg,
2246 -1);
2247 change_publish_color (pe, "red");
2248}
2249
2250
2251/**
2252 * A publishing operation was stopped (in FS API). Free an entry in
2253 * the publish tab and its associated state.
2254 *
2255 * @param pe publishing operation that was stopped
2256 */
2257static void
2258handle_publish_stop (struct PublishEntry *pe)
2259{
2260 GtkTreeIter iter;
2261 GtkTreePath *path;
2262
2263 path = gtk_tree_row_reference_get_path (pe->rr);
2264 /* This is a child of a directory, and we've had that directory
2265 free'd already */
2266 if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (pe->tab->ts), &iter, path))
2267 {
2268 GNUNET_break (0);
2269 return;
2270 }
2271 (void) gtk_tree_store_remove (pe->tab->ts, &iter);
2272 gtk_tree_path_free (path);
2273 gtk_tree_row_reference_free (pe->rr);
2274 if (pe->uri != NULL)
2275 {
2276 GNUNET_FS_uri_destroy (pe->uri);
2277 pe->uri = NULL;
2278 }
2279 GNUNET_free (pe);
2280}
2281
2282
2283/**
2284 * The user clicked on the "close" button of the publishing tab.
2285 * Tell FS to stop all active publish operations. Then close the tab.
2286 *
2287 * @param button the stop button
2288 * @param user_data the 'struct PublishTab' that is being closed
2289 */
2290static void
2291stop_publishing (GtkButton * button, gpointer user_data)
2292{
2293 struct PublishTab *tab = user_data;
2294 struct PublishEntry *ent;
2295 GtkTreeIter iter;
2296 GtkTreeModel *tm;
2297 GtkNotebook *notebook;
2298 int index;
2299 int i;
2300
2301 GNUNET_assert (tab == publish_tab);
2302 /* stop all active operations */
2303 tm = GTK_TREE_MODEL (publish_tab->ts);
2304 if (gtk_tree_model_iter_children (tm, &iter, NULL))
2305 {
2306 do
2307 {
2308 gtk_tree_model_get (tm, &iter, 4, &ent, -1);
2309 GNUNET_FS_publish_stop (ent->pc);
2310 ent->pc = NULL;
2311 }
2312 while (TRUE == gtk_tree_model_iter_next (tm, &iter));
2313 }
2314
2315 /* remove tab from notebook */
2316 notebook =
2317 GTK_NOTEBOOK (GNUNET_FS_GTK_get_main_window_object
2318 ("GNUNET_GTK_main_window_notebook"));
2319 index = -1;
2320 for (i = gtk_notebook_get_n_pages (notebook) - 1; i >= 0; i--)
2321 if (publish_tab->frame == gtk_notebook_get_nth_page (notebook, i))
2322 index = i;
2323 gtk_notebook_remove_page (notebook, index);
2324
2325 /* fully destroy tab */
2326 g_object_unref (publish_tab->builder);
2327 GNUNET_free (publish_tab);
2328 publish_tab = NULL;
2329}
2330
2331
2332/**
2333 * The user started a publishing operation. Add it to the publishing
2334 * tab. If needed, create the publishing tab.
2335 *
2336 * @param pc the FS-API's publishing context for the operation
2337 * @param fn the name of the file (or directory) that is being published
2338 * @param fsize size of the file
2339 * @param parent parent of this publishing operation (for recursive operations), NULL for top-level operations
2340 * @return the publishing entry that will represent this operation
2341 */
2342static struct PublishEntry *
2343setup_publish (struct GNUNET_FS_PublishContext *pc, const char *fn,
2344 uint64_t fsize, struct PublishEntry *parent)
2345{
2346 struct PublishEntry *ent;
2347 GtkTreeIter *pitrptr;
2348 GtkTreeIter iter;
2349 GtkTreeIter piter;
2350 GtkTreePath *path;
2351 GtkWindow *df;
2352 GtkWidget *tab_label;
2353 GtkWidget *close_button;
2354 GtkNotebook *notebook;
2355 char *size_fancy;
2356
2357 if (NULL == publish_tab)
2358 {
2359 /* create new tab */
2360 publish_tab = GNUNET_malloc (sizeof (struct PublishTab));
2361 publish_tab->builder =
2362 GNUNET_GTK_get_new_builder ("gnunet_fs_gtk_publish_tab.glade",
2363 publish_tab);
2364 df = GTK_WINDOW (gtk_builder_get_object
2365 (publish_tab->builder, "_publish_frame_window"));
2366 publish_tab->frame = gtk_bin_get_child (GTK_BIN (df));
2367 g_object_ref (publish_tab->frame);
2368 gtk_container_remove (GTK_CONTAINER (df), publish_tab->frame);
2369 gtk_widget_destroy (GTK_WIDGET (df));
2370
2371 /* load tab_label */
2372 df = GTK_WINDOW (gtk_builder_get_object
2373 (publish_tab->builder, "_publish_label_window"));
2374 tab_label = gtk_bin_get_child (GTK_BIN (df));
2375 g_object_ref (tab_label);
2376 gtk_container_remove (GTK_CONTAINER (df), tab_label);
2377 gtk_widget_destroy (GTK_WIDGET (df));
2378
2379 /* FIXME-UNCLEAN: connect these signals using GLADE!!! */
2380 /* get refs to widgets */
2381 close_button =
2382 GTK_WIDGET (gtk_builder_get_object
2383 (publish_tab->builder, "_publish_label_close_button"));
2384 g_signal_connect (G_OBJECT (close_button), "clicked",
2385 G_CALLBACK (stop_publishing), publish_tab);
2386 /* make visible */
2387 notebook =
2388 GTK_NOTEBOOK (GNUNET_FS_GTK_get_main_window_object
2389 ("GNUNET_GTK_main_window_notebook"));
2390 gtk_notebook_insert_page (notebook, publish_tab->frame, tab_label, 0);
2391 gtk_widget_show (GTK_WIDGET (notebook));
2392 gtk_notebook_set_current_page (notebook, 0);
2393 publish_tab->ts =
2394 GTK_TREE_STORE (gtk_builder_get_object
2395 (publish_tab->builder, "_publish_frame_tree_store"));
2396 }
2397
2398 /* decide where to insert in the tab */
2399 if (NULL == parent)
2400 {
2401 pitrptr = NULL;
2402 }
2403 else
2404 {
2405 /* create new iter from parent */
2406 path = gtk_tree_row_reference_get_path (parent->rr);
2407 if (TRUE !=
2408 gtk_tree_model_get_iter (GTK_TREE_MODEL (publish_tab->ts), &piter,
2409 path))
2410 {
2411 GNUNET_break (0);
2412 return NULL;
2413 }
2414 pitrptr = &piter;
2415 }
2416
2417 /* create entry and perform insertion */
2418 ent = GNUNET_malloc (sizeof (struct PublishEntry));
2419 ent->is_top = (parent == NULL) ? GNUNET_YES : GNUNET_NO;
2420 ent->tab = publish_tab;
2421 ent->pc = pc;
2422 size_fancy = GNUNET_STRINGS_byte_size_fancy (fsize);
2423 gtk_tree_store_insert_with_values (publish_tab->ts, &iter, pitrptr, G_MAXINT,
2424 0, fn, 1, size_fancy, 2, "white", 3,
2425 (guint) 0 /* progress */ ,
2426 4, ent, -1);
2427 GNUNET_free (size_fancy);
2428 path = gtk_tree_model_get_path (GTK_TREE_MODEL (publish_tab->ts), &iter);
2429 ent->rr = gtk_tree_row_reference_new (GTK_TREE_MODEL (publish_tab->ts), path);
2430 gtk_tree_path_free (path);
2431 return ent;
2432}
2433
2434
2435
2436/* ***************** Master event handler ****************** */
2437
2438
2439
2440/**
2441 * Notification of FS to a client about the progress of an
2442 * operation. Callbacks of this type will be used for uploads,
2443 * downloads and searches. Some of the arguments depend a bit
2444 * in their meaning on the context in which the callback is used.
2445 *
2446 * @param cls closure
2447 * @param info details about the event, specifying the event type
2448 * and various bits about the event
2449 * @return client-context (for the next progress call
2450 * for this operation; should be set to NULL for
2451 * SUSPEND and STOPPED events). The value returned
2452 * will be passed to future callbacks in the respective
2453 * field in the GNUNET_FS_ProgressInfo struct.
2454 */
2455void *
2456GNUNET_GTK_fs_event_handler (void *cls,
2457 const struct GNUNET_FS_ProgressInfo *info)
2458{
2459 void *ret;
2460
2461 switch (info->status)
2462 {
2463 case GNUNET_FS_STATUS_PUBLISH_START:
2464 return setup_publish (info->value.publish.pc, info->value.publish.filename,
2465 info->value.publish.size, info->value.publish.pctx);
2466 case GNUNET_FS_STATUS_PUBLISH_RESUME:
2467 ret =
2468 setup_publish (info->value.publish.pc, info->value.publish.filename,
2469 info->value.publish.size, info->value.publish.pctx);
2470 if (ret == NULL)
2471 return ret;
2472 if (info->value.publish.specifics.resume.message != NULL)
2473 {
2474 handle_publish_error (ret,
2475 info->value.publish.specifics.resume.message);
2476 }
2477 else if (info->value.publish.specifics.resume.chk_uri != NULL)
2478 {
2479 handle_publish_completed (ret,
2480 info->value.publish.specifics.resume.chk_uri);
2481 }
2482 return ret;
2483 case GNUNET_FS_STATUS_PUBLISH_SUSPEND:
2484 handle_publish_stop (info->value.publish.cctx);
2485 return NULL;
2486 case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
2487 mark_publish_progress (info->value.publish.cctx,
2488 info->value.publish.size,
2489 info->value.publish.completed);
2490 return info->value.publish.cctx;
2491 case GNUNET_FS_STATUS_PUBLISH_ERROR:
2492 handle_publish_error (info->value.publish.cctx,
2493 info->value.publish.specifics.error.message);
2494 return info->value.publish.cctx;
2495 case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
2496 handle_publish_completed (info->value.publish.cctx,
2497 info->value.publish.specifics.completed.chk_uri);
2498 return info->value.publish.cctx;
2499 case GNUNET_FS_STATUS_PUBLISH_STOPPED:
2500 handle_publish_stop (info->value.publish.cctx);
2501 return NULL;
2502 case GNUNET_FS_STATUS_DOWNLOAD_START:
2503 return setup_download (info->value.download.cctx, info->value.download.pctx,
2504 info->value.download.sctx, info->value.download.dc,
2505 info->value.download.uri,
2506 info->value.download.filename,
2507 info->value.download.specifics.start.meta,
2508 info->value.download.size,
2509 info->value.download.completed);
2510 case GNUNET_FS_STATUS_DOWNLOAD_RESUME:
2511 ret =
2512 setup_download (info->value.download.cctx, info->value.download.pctx,
2513 info->value.download.sctx, info->value.download.dc,
2514 info->value.download.uri, info->value.download.filename,
2515 info->value.download.specifics.resume.meta,
2516 info->value.download.size,
2517 info->value.download.completed);
2518 if (info->value.download.specifics.resume.message != NULL)
2519 mark_download_error (ret,
2520 info->value.download.specifics.resume.message);
2521 return ret;
2522 case GNUNET_FS_STATUS_DOWNLOAD_SUSPEND:
2523 stop_download (info->value.download.cctx);
2524 return NULL;
2525 case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
2526 mark_download_progress (info->value.download.cctx,
2527 info->value.download.size,
2528 info->value.download.completed,
2529 info->value.download.specifics.progress.data,
2530 info->value.download.specifics.progress.
2531 offset,
2532 info->value.download.specifics.progress.
2533 data_len,
2534 info->value.download.specifics.progress.
2535 depth);
2536 return info->value.download.cctx;
2537 case GNUNET_FS_STATUS_DOWNLOAD_ERROR:
2538 mark_download_error (info->value.download.cctx,
2539 info->value.download.specifics.error.message);
2540 return info->value.download.cctx;
2541 case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
2542 mark_download_completed (info->value.download.cctx,
2543 info->value.download.size,
2544 info->value.download.filename);
2545 return info->value.download.cctx;
2546 case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
2547 stop_download (info->value.download.cctx);
2548 return NULL;
2549 case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
2550 change_download_color (info->value.download.cctx, "yellow");
2551 return info->value.download.cctx;
2552 case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
2553 change_download_color (info->value.download.cctx, "blue");
2554 return info->value.download.cctx;
2555 case GNUNET_FS_STATUS_DOWNLOAD_LOST_PARENT:
2556 download_lost_parent (info->value.download.cctx);
2557 return info->value.download.cctx;
2558 case GNUNET_FS_STATUS_SEARCH_START:
2559 if (info->value.search.pctx != NULL)
2560 return setup_inner_search (info->value.search.sc,
2561 info->value.search.pctx);
2562 return setup_search_tab (info->value.search.sc, info->value.search.query);
2563 case GNUNET_FS_STATUS_SEARCH_RESUME:
2564 ret = setup_search_tab (info->value.search.sc, info->value.search.query);
2565 if (info->value.search.specifics.resume.message)
2566 handle_search_error (ret,
2567 info->value.search.specifics.resume.message);
2568 return ret;
2569 case GNUNET_FS_STATUS_SEARCH_RESUME_RESULT:
2570 ret =
2571 process_search_result (info->value.search.cctx, info->value.search.pctx,
2572 info->value.search.specifics.resume_result.uri,
2573 info->value.search.specifics.resume_result.meta,
2574 info->value.search.specifics.resume_result.
2575 result,
2576 info->value.search.specifics.resume_result.
2577 applicability_rank);
2578 update_search_result (ret,
2579 info->value.search.specifics.resume_result.
2580 meta,
2581 info->value.search.specifics.resume_result.
2582 applicability_rank,
2583 info->value.search.specifics.resume_result.
2584 availability_certainty,
2585 info->value.search.specifics.resume_result.
2586 availability_rank);
2587 return ret;
2588 case GNUNET_FS_STATUS_SEARCH_SUSPEND:
2589 close_search_tab (info->value.search.cctx);
2590 return NULL;
2591 case GNUNET_FS_STATUS_SEARCH_RESULT:
2592 return process_search_result (info->value.search.cctx,
2593 info->value.search.pctx,
2594 info->value.search.specifics.result.uri,
2595 info->value.search.specifics.result.meta,
2596 info->value.search.specifics.result.result,
2597 info->value.search.specifics.result.
2598 applicability_rank);
2599 case GNUNET_FS_STATUS_SEARCH_RESULT_NAMESPACE:
2600 GNUNET_break (0);
2601 break;
2602 case GNUNET_FS_STATUS_SEARCH_UPDATE:
2603 update_search_result (info->value.search.specifics.update.cctx,
2604 info->value.search.specifics.update.meta,
2605 info->value.search.specifics.update.
2606 applicability_rank,
2607 info->value.search.specifics.update.
2608 availability_certainty,
2609 info->value.search.specifics.update.
2610 availability_rank);
2611 return info->value.search.specifics.update.cctx;
2612 case GNUNET_FS_STATUS_SEARCH_ERROR:
2613 handle_search_error (info->value.search.cctx,
2614 info->value.search.specifics.error.message);
2615 return info->value.search.cctx;
2616 case GNUNET_FS_STATUS_SEARCH_PAUSED:
2617 return info->value.search.cctx;
2618 case GNUNET_FS_STATUS_SEARCH_CONTINUED:
2619 return info->value.search.cctx;
2620 case GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED:
2621 free_search_result (info->value.search.specifics.result_stopped.cctx);
2622 return NULL;
2623 case GNUNET_FS_STATUS_SEARCH_RESULT_SUSPEND:
2624 free_search_result (info->value.search.specifics.result_suspend.cctx);
2625 return NULL;
2626 case GNUNET_FS_STATUS_SEARCH_STOPPED:
2627 close_search_tab (info->value.search.cctx);
2628 return NULL;
2629 case GNUNET_FS_STATUS_UNINDEX_START:
2630 GNUNET_break (0);
2631 break;
2632 case GNUNET_FS_STATUS_UNINDEX_RESUME:
2633 GNUNET_break (0);
2634 break;
2635 case GNUNET_FS_STATUS_UNINDEX_SUSPEND:
2636 GNUNET_break (0);
2637 break;
2638 case GNUNET_FS_STATUS_UNINDEX_PROGRESS:
2639 GNUNET_break (0);
2640 break;
2641 case GNUNET_FS_STATUS_UNINDEX_ERROR:
2642 GNUNET_break (0);
2643 break;
2644 case GNUNET_FS_STATUS_UNINDEX_COMPLETED:
2645 GNUNET_break (0);
2646 break;
2647 case GNUNET_FS_STATUS_UNINDEX_STOPPED:
2648 GNUNET_break (0);
2649 break;
2650 default:
2651 GNUNET_break (0);
2652 break;
2653 }
2654 return NULL;
2655}
2656
2657
2658/* end of gnunet-fs-gtk-event_handler.c */