diff options
Diffstat (limited to 'src/fs/gnunet-fs-gtk_event-handler.c')
-rw-r--r-- | src/fs/gnunet-fs-gtk_event-handler.c | 2658 |
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 | */ | ||
36 | struct 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 | */ | ||
61 | struct 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 | */ | ||
97 | struct 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 | */ | ||
126 | static struct SearchTab *search_tab_head; | ||
127 | |||
128 | /** | ||
129 | * Tail of linked list of tabs for searches. | ||
130 | */ | ||
131 | static 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 | */ | ||
137 | static struct SearchTab *uri_tab; | ||
138 | |||
139 | /** | ||
140 | * Special tab we use to store publishing operations. | ||
141 | */ | ||
142 | static struct PublishTab *publish_tab; | ||
143 | |||
144 | /** | ||
145 | * Row reference for the current search context menu. | ||
146 | * FIXME-UNCLEAN: de-globalize? | ||
147 | */ | ||
148 | static GtkTreeRowReference *current_context_row_reference; | ||
149 | |||
150 | /** | ||
151 | * Search tab used for the current search context menu. | ||
152 | * FIXME-UNCLEAN: de-globalize? | ||
153 | */ | ||
154 | static 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 | */ | ||
171 | static char * | ||
172 | get_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 | */ | ||
196 | static char * | ||
197 | get_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 | */ | ||
297 | static void | ||
298 | start_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 | */ | ||
423 | static void | ||
424 | search_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 | */ | ||
444 | static void | ||
445 | start_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 | */ | ||
460 | static void | ||
461 | start_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 | */ | ||
489 | static void | ||
490 | start_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 | */ | ||
518 | static void | ||
519 | abort_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 | */ | ||
538 | static void | ||
539 | copy_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 | */ | ||
594 | static gboolean | ||
595 | search_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 | */ | ||
685 | static gboolean | ||
686 | search_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 | */ | ||
711 | static gboolean | ||
712 | search_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 | */ | ||
747 | static void | ||
748 | update_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 | */ | ||
774 | static void | ||
775 | close_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 | */ | ||
813 | static void | ||
814 | free_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 | */ | ||
858 | static void | ||
859 | update_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 | */ | ||
906 | void | ||
907 | GNUNET_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 | */ | ||
950 | static void | ||
951 | stop_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 | */ | ||
976 | static void | ||
977 | pause_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 | */ | ||
998 | static void | ||
999 | continue_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 | */ | ||
1025 | static void | ||
1026 | clear_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 | */ | ||
1067 | static void | ||
1068 | handle_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 | */ | ||
1083 | static char * | ||
1084 | get_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 | */ | ||
1121 | static char * | ||
1122 | get_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 | */ | ||
1141 | static void | ||
1142 | update_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 | */ | ||
1232 | struct SearchResult * | ||
1233 | GNUNET_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 | */ | ||
1371 | static struct SearchResult * | ||
1372 | process_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 | */ | ||
1398 | static struct SearchTab * | ||
1399 | setup_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 | */ | ||
1524 | static struct SearchTab * | ||
1525 | setup_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 | */ | ||
1556 | struct SearchTab * | ||
1557 | GNUNET_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 | */ | ||
1599 | static void | ||
1600 | change_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 | */ | ||
1627 | static void | ||
1628 | stop_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 | */ | ||
1658 | struct 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 | */ | ||
1706 | static void | ||
1707 | add_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 | */ | ||
1770 | static void | ||
1771 | mark_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 | */ | ||
1830 | static void | ||
1831 | mark_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 | */ | ||
1860 | static void | ||
1861 | mark_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 | */ | ||
1900 | static void | ||
1901 | copy_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 | */ | ||
1981 | static void | ||
1982 | delete_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 | */ | ||
1998 | static void | ||
1999 | download_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 | */ | ||
2051 | static struct DownloadEntry * | ||
2052 | setup_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 | */ | ||
2136 | static void | ||
2137 | change_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 | */ | ||
2166 | static void | ||
2167 | mark_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 | */ | ||
2196 | static void | ||
2197 | handle_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 | */ | ||
2229 | static void | ||
2230 | handle_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 | */ | ||
2257 | static void | ||
2258 | handle_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 | */ | ||
2290 | static void | ||
2291 | stop_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 | */ | ||
2342 | static struct PublishEntry * | ||
2343 | setup_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 | */ | ||
2455 | void * | ||
2456 | GNUNET_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 */ | ||