aboutsummaryrefslogtreecommitdiff
path: root/src/fs/gnunet-fs-gtk_publish-edit-dialog.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/fs/gnunet-fs-gtk_publish-edit-dialog.c')
-rw-r--r--src/fs/gnunet-fs-gtk_publish-edit-dialog.c1163
1 files changed, 1163 insertions, 0 deletions
diff --git a/src/fs/gnunet-fs-gtk_publish-edit-dialog.c b/src/fs/gnunet-fs-gtk_publish-edit-dialog.c
new file mode 100644
index 00000000..6dcf5653
--- /dev/null
+++ b/src/fs/gnunet-fs-gtk_publish-edit-dialog.c
@@ -0,0 +1,1163 @@
1/*
2 This file is part of GNUnet
3 (C) 2005, 2006, 2010, 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_publish-edit-dialog.c
23 * @author Christian Grothoff
24 */
25#include "gnunet-fs-gtk-common.h"
26#include "gnunet-fs-gtk-edit_publish_dialog.h"
27#include "gnunet-fs-gtk.h"
28#include <gnunet/gnunet_util_lib.h>
29
30#include "metatypes.c"
31
32
33/**
34 * Internal state kept for each "edit" dialog where the user can edit
35 * publishing information for a file.
36 */
37struct EditPublicationDialogContext
38{
39 /**
40 * Builder for the dialog.
41 */
42 GtkBuilder *builder;
43
44 /**
45 * The 'window' object for the dialog.
46 */
47 GtkWindow *edit_publication_window;
48
49 /**
50 * The confirmation button which closes the dialog (only sensitive
51 * if the values entered are valid).
52 */
53 GtkWidget *confirm_button;
54
55 /**
56 * Tree view showing the meta data for the file.
57 */
58 GtkTreeView *meta_treeview;
59
60 /**
61 * Tree view showing the keywords for the file.
62 */
63 GtkTreeView *keywords_treeview;
64
65 /**
66 * Image showing the preview image for the file.
67 */
68 GtkImage *preview_image;
69
70 /**
71 * Combo box where the user can select the anonymity level.
72 */
73 GtkComboBox *anonymity_combo;
74
75 /**
76 * Liststore of possible publication types.
77 */
78 GtkListStore *pubtypes_liststore;
79
80 /**
81 * Liststore of all possible meta types the user can choose from.
82 * (updated to based on the selected publication type).
83 */
84 GtkListStore *metatypes_liststore;
85
86 /**
87 * Liststore showing the meta data of the file (associated with
88 * the 'meta_treeview'.
89 */
90 GtkListStore *meta_liststore;
91
92 /**
93 * Liststore with the keywords of the file (associated with the
94 * 'keywords_treeview'.
95 */
96 GtkListStore *keywords_liststore;
97
98 /**
99 * Spin button to select content priority level for the file.
100 */
101 GtkSpinButton *priority_spin;
102
103 /**
104 * Spin button to select the expiration year.
105 */
106 GtkSpinButton *expiration_year_spin;
107
108 /**
109 * Spin button to select the replication level.
110 */
111 GtkSpinButton *replication_spin;
112
113 /**
114 * Entry line for adding additional keywords.
115 */
116 GtkEntry *keyword_entry;
117
118 /**
119 * Entry line for setting a namespace root (possibly invisible).
120 */
121 GtkEntry *root_entry;
122
123 /**
124 * Entry line to check indexing vs. inserting (possibly invisible)
125 */
126 GtkToggleButton *index_checkbutton;
127
128 /**
129 * Type ID of the last selected item in the GtkCellRendererCombo
130 * of the meta data tree view.
131 */
132 gint meta_combo_selected_type_id;
133
134 /**
135 * Continuation to call once the dialog has been closed
136 */
137 GNUNET_FS_GTK_EditPublishDialogCallback cb;
138
139 /**
140 * Closure for 'cb'.
141 */
142 void *cb_cls;
143
144 /**
145 * Briefly used temporary meta data set.
146 */
147 struct GNUNET_CONTAINER_MetaData *md;
148
149 /**
150 * Information about the file being published as seen by the FS-API.
151 * This is what we are primarily editing.
152 */
153 struct GNUNET_FS_FileInformation *fip;
154
155 /**
156 * Flag to track if we changed the preview and thus should keep/discard
157 * binary metadata.
158 */
159 int preview_changed;
160
161 /**
162 * Is this operation for a directory?
163 */
164 int is_directory;
165
166 /**
167 * Is it allowed for the user to supply keywords in this dialog?
168 */
169 int allow_no_keywords;
170
171};
172
173
174/**
175 * Free resources associated with the edit publication dialog.
176 *
177 * @param ctx the context of the dialog to release resources of
178 */
179static void
180free_edit_dialog_context (struct EditPublicationDialogContext *ctx)
181{
182 gtk_widget_destroy (GTK_WIDGET (ctx->edit_publication_window));
183 // FIXME-LEAK: destroy builder!
184 GNUNET_free (ctx);
185}
186
187
188
189/* ****************** metadata editing ******************** */
190
191
192
193/**
194 * Update the set of metatypes listed in the dialog based on the
195 * given code.
196 *
197 * @param ctx main dialog context
198 * @param code which set of metatypes is desired?
199 */
200static void
201change_metatypes (struct EditPublicationDialogContext *ctx, gint code)
202{
203 gint pubtype_count;
204 gint max_type;
205 gint i;
206 GtkTreeIter iter;
207
208 /* double-check that 'code' is valid */
209 for (pubtype_count = 0; NULL != types[pubtype_count]; pubtype_count++) ;
210 GNUNET_assert (code < pubtype_count);
211
212 /* clear existing selection of metatypes */
213 gtk_list_store_clear (ctx->metatypes_liststore);
214 max_type = EXTRACTOR_metatype_get_max ();
215 /* add new types based on selection */
216 for (i = 0; types[code][i] != EXTRACTOR_METATYPE_RESERVED; i++)
217 if ( (types[code][i] < max_type) && (types[code][i] > 0) )
218 gtk_list_store_insert_with_values (ctx->metatypes_liststore,
219 &iter, G_MAXINT,
220 0, types[code][i],
221 1, EXTRACTOR_METAFORMAT_UTF8,
222 2, EXTRACTOR_metatype_to_string (types [code][i]),
223 3, EXTRACTOR_metatype_to_description (types[code][i]),
224 -1);
225}
226
227
228/**
229 * The user has selected a different publication type.
230 * Update the meta type selection.
231 *
232 * @param widget the publication type combo box widget
233 * @param user_data the 'struct EditPublicationDialogContext'
234 */
235void
236GNUNET_GTK_edit_publication_type_combo_changed_cb (GtkComboBox * widget,
237 gpointer user_data)
238{
239 struct EditPublicationDialogContext *ctx = user_data;
240 GtkTreeIter iter;
241 gint code;
242
243 if (! gtk_combo_box_get_active_iter (widget, &iter))
244 return;
245 gtk_tree_model_get (GTK_TREE_MODEL (ctx->pubtypes_liststore), &iter, 0, &code, -1);
246 change_metatypes (ctx, code);
247}
248
249
250/**
251 * The user has changed the selection in the meta data tree view.
252 * Update the sensitivity of the 'delete' button.
253 *
254 * @param ts the tree selection object
255 * @param user_data the 'struct EditPublicationDialogContext'
256 */
257/* FIXME-UNCLEAN: connect this signal via glade (modern versions of Glade support this) */
258static void
259metadata_selection_changed_cb (GtkTreeSelection *ts,
260 gpointer user_data)
261{
262 struct EditPublicationDialogContext *ctx = user_data;
263
264 gtk_widget_set_sensitive (GTK_WIDGET (gtk_builder_get_object
265 (ctx->builder,
266 "GNUNET_GTK_edit_publication_delete_button")),
267 gtk_tree_selection_get_selected (ts, NULL, NULL));
268}
269
270
271/**
272 * The user changed (and confirmed the change) the type of a
273 * meta-data item in the meta data tree view. Update the type and
274 * text in the list store accordingly.
275 *
276 * @param renderer widget where the change happened
277 * @param path which item was changed in the tree view
278 * @param new_text new value for the item
279 * @param user_data the 'struct EditPublicationDialogContext'
280 */
281void
282GNUNET_GTK_edit_publication_metadata_tree_view_type_renderer_edited_cb (GtkCellRendererText *renderer,
283 gchar * path,
284 gchar * new_text,
285 gpointer user_data)
286{
287 struct EditPublicationDialogContext *ctx = user_data;
288 GtkTreeIter iter;
289 gint type_id;
290
291 if (! gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (ctx->meta_liststore),
292 &iter,
293 path))
294 {
295 GNUNET_break (0);
296 return;
297 }
298 if (-1 == ctx->meta_combo_selected_type_id)
299 {
300 GNUNET_break (0);
301 return;
302 }
303 type_id = ctx->meta_combo_selected_type_id;
304 ctx->meta_combo_selected_type_id = -1;
305 gtk_list_store_set (ctx->meta_liststore, &iter,
306 0, type_id,
307 1, EXTRACTOR_METAFORMAT_UTF8,
308 2, EXTRACTOR_metatype_to_string (type_id),
309 4, EXTRACTOR_metatype_to_description (type_id),
310 -1);
311}
312
313
314/**
315 * The user changed the type of a meta-data item in the meta data
316 * tree view. Obtain the selected type_id and store it in
317 * the 'meta_combo_selected_type_id' field for use by
318 * 'GNUNET_GTK_edit_publication_metadata_tree_view_type_renderer_edited_cb'.
319 *
320 * @param combo combo box that was dropped down
321 * @param path which item was changed in the tree view
322 * @param new_iter item that is now selected in the drop-down combo box
323 * @param user_data the 'struct EditPublicationDialogContext'
324 */
325void
326GNUNET_GTK_edit_publication_metadata_tree_view_type_renderer_changed_cb (GtkCellRendererCombo * combo,
327 gchar * path_string,
328 GtkTreeIter * new_iter,
329 gpointer user_data)
330{
331 struct EditPublicationDialogContext *ctx = user_data;
332 GtkTreeModel *combo_model;
333 gint type_id;
334
335 g_object_get (combo,
336 "model", &combo_model, NULL);
337 gtk_tree_model_get (combo_model, new_iter,
338 0, &type_id,
339 -1);
340 ctx->meta_combo_selected_type_id = type_id;
341}
342
343
344/**
345 * The user changed (and confirmed the change) the value of a
346 * meta-data item in the meta data tree view. Update the value
347 * in the list store accordingly.
348 *
349 * @param renderer widget where the change happened
350 * @param path which item was changed in the tree view
351 * @param new_text new value for the item
352 * @param user_data the 'struct EditPublicationDialogContext'
353 */
354void
355GNUNET_GTK_edit_publication_metadata_tree_view_value_renderer_edited_cb (GtkCellRendererText * renderer,
356 gchar * path,
357 gchar * new_text,
358 gpointer user_data)
359{
360 struct EditPublicationDialogContext *ctx = user_data;
361 GtkTreeIter iter;
362 gint metatype;
363 char *avalue;
364 const char *ivalue;
365 size_t slen;
366 char *pos;
367
368 if (! gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (ctx->meta_liststore),
369 &iter,
370 path))
371 {
372 GNUNET_break (0);
373 return;
374 }
375
376 gtk_tree_model_get (GTK_TREE_MODEL (ctx->meta_liststore), &iter,
377 0, &metatype, -1);
378 if (metatype == EXTRACTOR_METATYPE_FILENAME)
379 {
380 /* apply filename rules */
381 /* First, use UNIX-style separators */
382 avalue = GNUNET_strdup (new_text);
383 while (NULL != (pos = strstr (avalue, "\\")))
384 *pos = '/';
385
386 /* if user put '/' at the end, remove it' */
387 slen = strlen (avalue);
388 while ( (slen > 1) && (avalue[slen - 1] == '\\'))
389 {
390 avalue[slen - 1] = '\0';
391 slen--;
392 }
393
394 /* However, directories must end with '/', so add it */
395 if ( (new_text[strlen (new_text) - 1] != '/') &&
396 ctx->is_directory )
397 {
398 char * tmp;
399
400 GNUNET_asprintf (&tmp, "%s/", avalue);
401 GNUNET_free (avalue);
402 avalue = tmp;
403 }
404
405 /* Also, replace '../' everywhere with "___" */
406 while (NULL != (pos = strstr (avalue, "../")))
407 memset (pos, '_', 3);
408
409 ivalue = avalue;
410 }
411 else
412 {
413 ivalue = new_text;
414 avalue = NULL;
415 }
416 gtk_list_store_set (ctx->meta_liststore, &iter,
417 3, ivalue,
418 -1);
419 GNUNET_free_non_null (avalue);
420}
421
422
423/**
424 * The user has pushed the 'add' button for metadata. Add a 'dummy' value
425 * to our meta data store (to be edited by the user).
426 *
427 * @param button the 'add' button
428 * @param user_data the 'struct EditPublicationDialogContext'
429 */
430void
431GNUNET_GTK_edit_publication_add_button_clicked_cb (GtkButton * button,
432 gpointer user_data)
433{
434 struct EditPublicationDialogContext *ctx = user_data;
435 GtkTreeIter iter;
436
437 gtk_list_store_insert_with_values (ctx->meta_liststore,
438 &iter, 0,
439 0, 0,
440 1, EXTRACTOR_METAFORMAT_UTF8,
441 2, _("Select a type"),
442 3, _("Specify a value"),
443 4, NULL,
444 -1);
445}
446
447
448/**
449 * The user has pushed the 'del' button for metadata.
450 * If there is a metadata selected, remove it from the list store.
451 *
452 * @param widget the button
453 * @param user_data the 'struct EditPublicationDialogContext'
454 */
455void
456GNUNET_GTK_edit_publication_delete_button_clicked_cb (GtkButton * button,
457 gpointer user_data)
458{
459 struct EditPublicationDialogContext *ctx = user_data;
460 GtkTreeIter iter;
461 GtkTreeSelection *meta_selection;
462
463 meta_selection = gtk_tree_view_get_selection (ctx->meta_treeview);
464 if (! gtk_tree_selection_get_selected (meta_selection, NULL, &iter))
465 {
466 GNUNET_break (0);
467 return;
468 }
469 if (gtk_list_store_remove (ctx->meta_liststore, &iter))
470 gtk_tree_selection_select_iter (meta_selection, &iter);
471}
472
473
474/**
475 * The user has selected another preview image file. Update the
476 * preview image.
477 *
478 * @param widget the file chooser dialog that completed
479 * @param user_data the 'struct EditPublicationDialogContext'
480 */
481void
482GNUNET_GTK_edit_publication_metadata_preview_file_chooser_button_file_set_cb (GtkFileChooserButton * widget,
483 gpointer user_data)
484{
485 struct EditPublicationDialogContext *ctx = user_data;
486 gchar *fn;
487
488 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget));
489 gtk_image_set_from_file (ctx->preview_image, fn);
490 g_free (fn);
491 ctx->preview_changed = GNUNET_YES;
492}
493
494
495/* ****************** keyword list editing ******************** */
496
497
498
499/**
500 * The user has changed the selection in the keyword tree view.
501 * Update the sensitivity of the 'delete' button.
502 *
503 * @param ts the tree selection object
504 * @param user_data the 'struct EditPublicationDialogContext'
505 */
506/* FIXME-UNCLEAN: connect this signal via glade (modern versions of Glade support this) */
507static void
508keywords_selection_changed_cb (GtkTreeSelection *ts,
509 gpointer user_data)
510{
511 struct EditPublicationDialogContext *ctx = user_data;
512
513 gtk_widget_set_sensitive (GTK_WIDGET (gtk_builder_get_object
514 (ctx->builder,
515 "GNUNET_GTK_edit_publication_keyword_list_del_button")),
516 gtk_tree_selection_get_selected (ts, NULL, NULL));
517}
518
519
520/**
521 * The user has edited the keyword entry line. Update the
522 * sensitivity of the 'add' button.
523 *
524 * @param editable the entry line for keywords
525 * @param user_data the 'struct EditPublicationDialogContext'
526 */
527void
528GNUNET_GTK_edit_publication_keyword_entry_changed_cb (GtkEditable * editable,
529 gpointer user_data)
530{
531 struct EditPublicationDialogContext *ctx = user_data;
532 const char *keyword;
533
534 keyword = gtk_entry_get_text (ctx->keyword_entry);
535 gtk_widget_set_sensitive (GTK_WIDGET (gtk_builder_get_object (ctx->builder,
536 "GNUNET_GTK_edit_publication_keyword_list_add_button")),
537 (strlen (keyword) > 0) ? TRUE : FALSE);
538}
539
540
541/**
542 * The user has pushed the 'del' button for the keyword.
543 * If there is a keyword selected, remove it from the list store.
544 *
545 * @param widget the button
546 * @param user_data the 'struct EditPublicationDialogContext'
547 */
548void
549GNUNET_GTK_edit_publication_keyword_list_del_button_clicked_cb (GtkButton *
550 button,
551 gpointer user_data)
552{
553 struct EditPublicationDialogContext *ctx = user_data;
554 GtkTreeIter iter;
555 GtkTreeSelection *keywords_selection;
556
557 keywords_selection = gtk_tree_view_get_selection (ctx->keywords_treeview);
558 if (! gtk_tree_selection_get_selected (keywords_selection, NULL, &iter))
559 {
560 GNUNET_break (0);
561 return;
562 }
563 if (gtk_list_store_remove (GTK_LIST_STORE (ctx->keywords_liststore), &iter))
564 gtk_tree_selection_select_iter (keywords_selection, &iter);
565
566 /* disable confirm button if keywords are required and we have no more keywords */
567 if ( (! ctx->allow_no_keywords) &&
568 (! gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ctx->keywords_liststore),
569 &iter)) )
570 gtk_widget_set_sensitive (ctx->confirm_button, FALSE);
571}
572
573
574/**
575 * The user has pushed the 'add' button for the keyword (or pressed RETURN).
576 * If there is a keyword in the line, add it to the list store.
577 *
578 * @param widget the entry line, or NULL (if we are called from 'RETURN')
579 * @param user_data the 'struct EditPublicationDialogContext'
580 */
581void
582GNUNET_GTK_edit_publication_keyword_list_add_button_clicked_cb (GtkButton *
583 button,
584 gpointer user_data)
585{
586 struct EditPublicationDialogContext *ctx = user_data;
587 const char *keyword;
588 GtkTreeIter iter;
589
590 keyword = gtk_entry_get_text (ctx->keyword_entry);
591 if (strlen (keyword) == 0)
592 return;
593 gtk_list_store_insert_with_values (ctx->keywords_liststore,
594 &iter, G_MAXINT,
595 0, keyword,
596 1, TRUE,
597 -1);
598 gtk_widget_set_sensitive (ctx->confirm_button, TRUE);
599 gtk_entry_set_text (ctx->keyword_entry, "");
600}
601
602
603/**
604 * The user has pushed a button in the entry line. Check if it was 'RETURN'
605 * and if so consider executing the 'add' action.
606 *
607 * @param widget the entry line
608 * @param event the event information
609 * @param user_data the 'struct EditPublicationDialogContext'
610 */
611gboolean
612GNUNET_GTK_edit_publication_keyword_entry_key_press_event_cb (GtkWidget *
613 widget,
614 GdkEventKey *
615 event,
616 gpointer user_data)
617{
618 struct EditPublicationDialogContext *ctx = user_data;
619
620 if (event->keyval != GDK_KEY_Return)
621 return FALSE;
622 GNUNET_GTK_edit_publication_keyword_list_add_button_clicked_cb (NULL, ctx);
623 return TRUE;
624}
625
626
627
628/* ****************** handlers for closing the dialog ******************** */
629
630
631/**
632 * The user clicked the 'cancel' button. Abort the operation.
633 *
634 * @param button the cancel button
635 * @param user_data the 'struct EditPublicationDialogContext'
636 */
637void
638GNUNET_GTK_edit_publication_cancel_button_clicked_cb (GtkButton * button,
639 gpointer user_data)
640{
641 struct EditPublicationDialogContext *ctx = user_data;
642
643 ctx->cb (ctx->cb_cls, GTK_RESPONSE_CANCEL, NULL);
644 free_edit_dialog_context (ctx);
645}
646
647
648/**
649 * The user closed the window. Abort the operation.
650 *
651 * @param widget the window
652 * @param event the event that caused the window to close
653 * @param user_data the 'struct EditPublicationDialogContext'
654 * @return TRUE (always)
655 */
656gboolean
657GNUNET_GTK_edit_publication_window_delete_event_cb (GtkWidget * widget,
658 GdkEvent * event,
659 gpointer user_data)
660{
661 struct EditPublicationDialogContext *ctx = user_data;
662
663 ctx->cb (ctx->cb_cls, GTK_RESPONSE_CANCEL, NULL);
664 free_edit_dialog_context (ctx);
665 return TRUE;
666}
667
668
669/**
670 * Copy binary meta data from to the new container and also preserve
671 * all entries that were not changed. In particular, all binary meta
672 * data is removed if the preview was changed, otherwise it is all
673 * kept. Similarly, if values are still in the liststore, they are
674 * fully kept (including plugin name and original format). "Keeping"
675 * a value means that it is added to the 'md' meta data in the dialog
676 * context.
677 *
678 * @param cls closure, a 'struct FileInformationUpdateContext';
679 * contains the 'new' meta data to construct in the 'md'
680 * field and the liststore to check the current value
681 * against in 'meta_liststore'.
682 * @param plugin_name name of the plugin that produced this value;
683 * special values can be used (i.e. '<zlib>' for zlib being
684 * used in the main libextractor library and yielding
685 * meta data).
686 * @param type libextractor-type describing the meta data
687 * @param format basic format information about data
688 * @param data_mime_type mime-type of data (not of the original file);
689 * can be NULL (if mime-type is not known)
690 * @param data actual meta-data found
691 * @param data_len number of bytes in data
692 * @return 0 to continue extracting
693 */
694static int
695preserve_meta_items (void *cls, const char *plugin_name,
696 enum EXTRACTOR_MetaType type,
697 enum EXTRACTOR_MetaFormat format,
698 const char *data_mime_type, const char *data,
699 size_t data_len)
700{
701 struct EditPublicationDialogContext *ctx = cls;
702 GtkTreeIter iter;
703 gchar *value;
704 guint ntype;
705 guint nformat;
706 int keep;
707
708 keep = GNUNET_NO;
709 switch (format)
710 {
711 case EXTRACTOR_METAFORMAT_UTF8:
712 case EXTRACTOR_METAFORMAT_C_STRING:
713 if (TRUE == gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ctx->meta_liststore), &iter))
714 {
715 do
716 {
717 gtk_tree_model_get (GTK_TREE_MODEL (ctx->meta_liststore), &iter, 0, &ntype, 1, &nformat, 3, &value, -1);
718 if ((ntype == type) && (nformat == format) &&
719 (0 == strcmp (value, data)))
720 {
721 gtk_list_store_remove (ctx->meta_liststore, &iter);
722 keep = GNUNET_YES;
723 g_free (value);
724 break;
725 }
726 g_free (value);
727 }
728 while (TRUE == gtk_tree_model_iter_next (GTK_TREE_MODEL (ctx->meta_liststore), &iter));
729 }
730 break;
731 case EXTRACTOR_METAFORMAT_UNKNOWN:
732 break;
733 case EXTRACTOR_METAFORMAT_BINARY:
734 if (ctx->preview_changed == GNUNET_NO)
735 keep = GNUNET_YES;
736 break;
737 default:
738 GNUNET_break (0);
739 break;
740 }
741 if (GNUNET_YES == keep)
742 GNUNET_break (GNUNET_OK ==
743 GNUNET_CONTAINER_meta_data_insert (ctx->md, plugin_name, type,
744 format, data_mime_type,
745 data, data_len));
746 return 0;
747}
748
749
750/**
751 * Function called to update the information in FI based on the changes made in
752 * the edit dialog.
753 *
754 * @param cls closure with a 'struct FileInformationUpdateContext *'
755 * @param fi the entry in the publish-structure
756 * @param length length of the file or directory
757 * @param meta metadata for the file or directory (can be modified)
758 * @param uri pointer to the keywords that will be used for this entry (can be modified)
759 * @param bo block options (can be modified)
760 * @param do_index should we index (can be modified)
761 * @param client_info pointer to client context set upon creation (can be modified)
762 * @return GNUNET_SYSERR (aborts after first call)
763 */
764static int
765file_information_update (void *cls, struct GNUNET_FS_FileInformation *fi,
766 uint64_t length,
767 struct GNUNET_CONTAINER_MetaData *meta,
768 struct GNUNET_FS_Uri **uri,
769 struct GNUNET_FS_BlockOptions *bo, int *do_index,
770 void **client_info)
771{
772 struct EditPublicationDialogContext *ctx = cls;
773 GtkTreeIter iter;
774 gint year;
775
776 /* gather publishing options */
777 *do_index = gtk_toggle_button_get_active (ctx->index_checkbutton);
778 GNUNET_break (GNUNET_GTK_get_selected_anonymity_combo_level (ctx->anonymity_combo,
779 &bo->anonymity_level));
780 bo->content_priority = gtk_spin_button_get_value (ctx->priority_spin);
781 bo->replication_level = gtk_spin_button_get_value (ctx->replication_spin);
782 year = gtk_spin_button_get_value (ctx->expiration_year_spin);
783 bo->expiration_time = GNUNET_FS_year_to_time (year);
784
785 /* update keyword-URI */
786 if (NULL != (*uri))
787 GNUNET_FS_uri_destroy (*uri);
788 *uri = NULL;
789 if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ctx->keywords_liststore), &iter))
790 {
791 do
792 {
793 gchar *value;
794
795 gtk_tree_model_get (GTK_TREE_MODEL (ctx->keywords_liststore), &iter, 0, &value, -1);
796 if (NULL == *uri)
797 *uri = GNUNET_FS_uri_ksk_create_from_args (1, (const char **) &value);
798 else
799 GNUNET_FS_uri_ksk_add_keyword (*uri, value, GNUNET_NO);
800 g_free (value);
801 }
802 while (gtk_tree_model_iter_next (GTK_TREE_MODEL (ctx->keywords_liststore), &iter));
803 }
804
805 /* update meta data; first, we copy the unchanged values from the original meta data */
806 ctx->md = GNUNET_CONTAINER_meta_data_create ();
807 GNUNET_CONTAINER_meta_data_iterate (meta,
808 &preserve_meta_items,
809 ctx);
810 /* clear original meta data */
811 GNUNET_CONTAINER_meta_data_clear (meta);
812 /* add all of the 'preserved' values */
813 GNUNET_CONTAINER_meta_data_merge (meta, ctx->md);
814 GNUNET_CONTAINER_meta_data_destroy (ctx->md);
815 ctx->md = NULL;
816 /* now add all of the values from the model; adding will simply do
817 nothing for values that are already in the set because they were preserved */
818 if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ctx->meta_liststore), &iter))
819 {
820 do
821 {
822 guint ntype;
823 guint nformat;
824 gchar *value;
825
826 gtk_tree_model_get (GTK_TREE_MODEL (ctx->meta_liststore), &iter, 0, &ntype, 1, &nformat, 3, &value, -1);
827 if (ntype > 0)
828 GNUNET_CONTAINER_meta_data_insert (ctx->md, "<user>", ntype, nformat,
829 "text/plain", value,
830 strlen (value) + 1);
831 g_free (value);
832 }
833 while (gtk_tree_model_iter_next (GTK_TREE_MODEL (ctx->meta_liststore), &iter));
834 }
835
836 /* finally, if we got a new preview, add it as well */
837 if (ctx->preview_changed == GNUNET_YES)
838 {
839 gchar *fn;
840 char *data;
841 gsize data_size;
842 const char *mime;
843 GFile *f;
844 GFileInfo *finfo;
845
846 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER
847 (gtk_builder_get_object
848 (ctx->builder,
849 "GNUNET_GTK_edit_publication_metadata_preview_file_chooser_button")));
850 f = g_file_new_for_path (fn);
851 finfo =
852 g_file_query_info (f, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, 0, NULL,
853 NULL);
854 if (! g_file_load_contents (f, NULL, &data, &data_size, NULL, NULL))
855 {
856 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
857 _("Could not load preview `%s' into memory\n"), fn);
858 }
859 else
860 {
861 mime =
862 g_file_info_get_attribute_string (finfo,
863 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
864 GNUNET_CONTAINER_meta_data_insert (meta, "<user>",
865 EXTRACTOR_METATYPE_THUMBNAIL,
866 EXTRACTOR_METAFORMAT_BINARY, mime,
867 data, data_size);
868 }
869 g_object_unref (finfo);
870 g_object_unref (f);
871 g_free (fn);
872 }
873 return GNUNET_SYSERR; /* only visit top-level item */
874}
875
876
877/**
878 * The user clicked the 'confirm' button. Push the edits back into the
879 * FileInformation structure and given it and the options back to the
880 * callback. Then clean up the dialog.
881 *
882 * @param button the cancel button
883 * @param user_data the 'struct EditPublicationDialogContext'
884 */
885void
886GNUNET_GTK_edit_publication_confirm_button_clicked_cb (GtkButton * button,
887 gpointer user_data)
888{
889 struct EditPublicationDialogContext *ctx = user_data;
890
891 /* push back changes to file-information */
892 GNUNET_FS_file_information_inspect (ctx->fip,
893 &file_information_update,
894 ctx);
895 /* call our continuation */
896 ctx->cb (ctx->cb_cls,
897 GTK_RESPONSE_OK,
898 gtk_entry_get_text (ctx->root_entry));
899 /* free resources from the edit dialog */
900 free_edit_dialog_context (ctx);
901}
902
903
904
905/* ****************** code for initialization of the dialog ******************** */
906
907
908
909/**
910 * Add each of the keywords to the keyword list store.
911 *
912 * @param cls closure
913 * @param keyword the keyword
914 * @param is_mandatory is the keyword mandatory (in a search)
915 * @return GNUNET_OK to continue to iterate
916 */
917static int
918add_keyword (void *cls, const char *keyword, int is_mandatory)
919{
920 GtkListStore *ls;
921 GtkTreeIter iter;
922
923 ls = GTK_LIST_STORE (cls);
924 gtk_list_store_insert_with_values (ls, &iter, G_MAXINT,
925 0, keyword,
926 1, FALSE,
927 -1);
928 return GNUNET_OK;
929}
930
931
932/**
933 * Function called to extract the information from FI to populate the edit dialog.
934 *
935 * @param cls the 'struct EditPublicationDialogContext'
936 * @param fi the entry in the publish-structure (unused)
937 * @param length length of the file or directory (unused)
938 * @param meta metadata for the file or directory (can be modified)
939 * @param uri pointer to the keywords that will be used for this entry (can be modified)
940 * @param bo block options
941 * @param do_index should we index (can be modified)
942 * @param client_info pointer to client context set upon creation (can be modified)
943 * @return GNUNET_SYSERR (aborts after first call)
944 */
945static int
946file_information_import (void *cls,
947 struct GNUNET_FS_FileInformation *fi,
948 uint64_t length,
949 struct GNUNET_CONTAINER_MetaData *meta,
950 struct GNUNET_FS_Uri **uri,
951 struct GNUNET_FS_BlockOptions *bo, int *do_index,
952 void **client_info)
953{
954 struct EditPublicationDialogContext *ctx = cls;
955 GdkPixbuf *pixbuf;
956 char *short_fn;
957 int year;
958
959 /* import options */
960 year = (int) GNUNET_FS_time_to_year (bo->expiration_time);
961 gtk_spin_button_set_value (ctx->expiration_year_spin, year);
962 GNUNET_GTK_select_anonymity_combo_level (ctx->anonymity_combo,
963 bo->anonymity_level);
964 gtk_spin_button_set_value (ctx->priority_spin, bo->content_priority);
965 gtk_spin_button_set_value (ctx->replication_spin, bo->replication_level);
966 gtk_toggle_button_set_active (ctx->index_checkbutton, *do_index);
967
968
969 /* import keywords */
970 if (NULL != *uri)
971 GNUNET_FS_uri_ksk_get_keywords (*uri, &add_keyword, ctx->keywords_liststore);
972
973 /* import meta data */
974 if (NULL != meta)
975 {
976 GNUNET_CONTAINER_meta_data_iterate (meta,
977 &GNUNET_FS_GTK_add_meta_data_to_list_store,
978 ctx->meta_liststore);
979 pixbuf = GNUNET_FS_GTK_get_thumbnail_from_meta_data (meta);
980 if (pixbuf != NULL)
981 {
982 gtk_image_set_from_pixbuf (ctx->preview_image, pixbuf);
983 }
984 }
985
986 /* Also update window title based on filename */
987 short_fn = GNUNET_CONTAINER_meta_data_get_first_by_types (meta,
988 EXTRACTOR_METATYPE_FILENAME,
989 -1);
990 if (NULL == short_fn)
991 {
992 gtk_window_set_title (ctx->edit_publication_window, _("<unnamed>"));
993 }
994 else
995 {
996 /* FIXME-BUG-MAYBE: ensure that short_fn is UTF-8 encoded */
997 gtk_window_set_title (ctx->edit_publication_window, short_fn);
998 GNUNET_free (short_fn);
999 }
1000 return GNUNET_SYSERR; /* only visit top-level item */
1001}
1002
1003
1004/**
1005 * Open the dialog to edit file information data.
1006 *
1007 * @param parent parent window of the dialog
1008 * @param fip information about the file information that is to be edited
1009 * @param allow_no_keywords is it OK to close the dialog without any keywords?
1010 * also used to indicate that this is a namespace operation
1011 * (FIXME-UNCLEAN: overloaded/badly-named argument)
1012 * @param anon_liststore liststore with anonymity options (FIXME-UNCLEAN: bad sharing)
1013 * @param cb function to call when the dialog is closed
1014 * @param cb_cls closure for 'cb'
1015 */
1016void
1017GNUNET_FS_GTK_edit_publish_dialog (GtkWindow * parent,
1018 struct GNUNET_FS_FileInformation *fip,
1019 int allow_no_keywords,
1020 GtkListStore *anon_liststore,
1021 GNUNET_FS_GTK_EditPublishDialogCallback cb,
1022 gpointer cb_cls)
1023{
1024 GtkTreeIter iter;
1025 gint code;
1026 GtkComboBox *pubtypes_combo;
1027 GtkLabel *index_label;
1028 GtkLabel *root_label;
1029 struct EditPublicationDialogContext *ctx;
1030 GtkTreeSelection *meta_selection;
1031 GtkTreeSelection *keywords_selection;
1032
1033 ctx = GNUNET_malloc (sizeof (struct EditPublicationDialogContext));
1034 ctx->fip = fip;
1035 ctx->preview_changed = GNUNET_NO;
1036 ctx->allow_no_keywords = allow_no_keywords;
1037 ctx->is_directory = GNUNET_FS_file_information_is_directory (fip);
1038 ctx->cb = cb;
1039 ctx->cb_cls = cb_cls;
1040 ctx->meta_combo_selected_type_id = -1;
1041 ctx->builder = GNUNET_GTK_get_new_builder ("gnunet_fs_gtk_edit_publication.glade", ctx);
1042
1043 if (ctx->builder == NULL)
1044 {
1045 GNUNET_free (ctx);
1046 return;
1047 }
1048
1049 /* obtain various widgets for use later */
1050 ctx->pubtypes_liststore =
1051 GTK_LIST_STORE (gtk_builder_get_object
1052 (ctx->builder, "GNUNET_GTK_publication_types_liststore"));
1053 ctx->metatypes_liststore =
1054 GTK_LIST_STORE (gtk_builder_get_object
1055 (ctx->builder,
1056 "GNUNET_GTK_publication_metadata_types_liststore"));
1057 ctx->meta_treeview = GTK_TREE_VIEW (gtk_builder_get_object
1058 (ctx->builder,
1059 "GNUNET_GTK_edit_publication_metadata_tree_view"));
1060 ctx->keywords_treeview = GTK_TREE_VIEW (gtk_builder_get_object
1061 (ctx->builder,
1062 "GNUNET_GTK_edit_publication_keyword_list_tree_view"));
1063 ctx->edit_publication_window =
1064 GTK_WINDOW (gtk_builder_get_object (ctx->builder,
1065 "GNUNET_GTK_edit_publication_window"));
1066 ctx->keywords_liststore = GTK_LIST_STORE (gtk_builder_get_object
1067 (ctx->builder, "GNUNET_GTK_publication_keywords_liststore"));
1068 ctx->keyword_entry =
1069 GTK_ENTRY (gtk_builder_get_object
1070 (ctx->builder, "GNUNET_GTK_edit_publication_keyword_entry"));
1071 ctx->confirm_button = GTK_WIDGET (gtk_builder_get_object
1072 (ctx->builder, "GNUNET_GTK_edit_publication_confirm_button"));
1073 ctx->preview_image =
1074 GTK_IMAGE (gtk_builder_get_object
1075 (ctx->builder,
1076 "GNUNET_GTK_edit_publication_metadata_preview_image"));
1077 ctx->meta_liststore = GTK_LIST_STORE (gtk_builder_get_object
1078 (ctx->builder,
1079 "GNUNET_GTK_publication_metadata_liststore"));
1080 ctx->root_entry = GTK_ENTRY (gtk_builder_get_object
1081 (ctx->builder, "GNUNET_GTK_edit_publication_root_entry"));
1082 ctx->expiration_year_spin = GTK_SPIN_BUTTON
1083 (gtk_builder_get_object
1084 (ctx->builder,
1085 "GNUNET_GTK_edit_publication_expiration_year_spin_button"));
1086 ctx->priority_spin = GTK_SPIN_BUTTON
1087 (gtk_builder_get_object
1088 (ctx->builder,
1089 "GNUNET_GTK_edit_publication_priority_spin_button"));
1090 ctx->replication_spin = GTK_SPIN_BUTTON
1091 (gtk_builder_get_object
1092 (ctx->builder,
1093 "GNUNET_GTK_edit_publication_replication_spin_button"));
1094 ctx->index_checkbutton = GTK_TOGGLE_BUTTON
1095 (gtk_builder_get_object
1096 (ctx->builder,
1097 "GNUNET_GTK_edit_publication_index_checkbutton"));
1098 ctx->anonymity_combo = GTK_COMBO_BOX (gtk_builder_get_object (ctx->builder,
1099 "GNUNET_GTK_edit_publication_anonymity_combobox"));
1100
1101 /* Basic initialization of widgets models and visibility */
1102 gtk_combo_box_set_model (ctx->anonymity_combo,
1103 GTK_TREE_MODEL (anon_liststore));
1104 GNUNET_FS_GTK_setup_expiration_year_adjustment (ctx->builder);
1105
1106 /* FIXME-UNCLEAN: are the following three even required anymore? */
1107 gtk_list_store_clear (ctx->keywords_liststore);
1108 gtk_list_store_clear (ctx->meta_liststore);
1109 gtk_entry_set_text (ctx->keyword_entry, "");
1110
1111 pubtypes_combo =
1112 GTK_COMBO_BOX (gtk_builder_get_object
1113 (ctx->builder, "GNUNET_GTK_edit_publication_type_combo"));
1114 if (gtk_combo_box_get_active_iter (pubtypes_combo, &iter))
1115 {
1116 gtk_tree_model_get (GTK_TREE_MODEL (ctx->pubtypes_liststore), &iter, 0, &code, -1);
1117 change_metatypes (ctx, 0);
1118 }
1119 else
1120 gtk_combo_box_set_active (pubtypes_combo, 0);
1121
1122 /* indexing does not apply to directories */
1123 gtk_widget_set_visible (GTK_WIDGET (ctx->index_checkbutton),
1124 ! ctx->is_directory);
1125 index_label = GTK_LABEL (gtk_builder_get_object
1126 (ctx->builder,
1127 "GNUNET_GTK_edit_publication_index_label"));
1128 gtk_widget_set_visible (GTK_WIDGET (index_label),
1129 ! ctx->is_directory);
1130
1131 /* show root label only if we must have keywords, which is also only the
1132 case for namespaces (FIXME-UNCLEAN: overloaded use of the argument) */
1133 gtk_widget_set_visible (GTK_WIDGET (ctx->root_entry),
1134 !allow_no_keywords);
1135 root_label = GTK_LABEL (gtk_builder_get_object
1136 (ctx->builder, "GNUNET_GTK_edit_publication_root_label"));
1137 gtk_widget_set_visible (GTK_WIDGET (root_label),
1138 !allow_no_keywords);
1139
1140 /* FIXME-UNCLEAN: what if we already have keywords? Again, does not really
1141 apply to namespace-case, but this seems a bit ugly */
1142 gtk_widget_set_sensitive (ctx->confirm_button, allow_no_keywords ? TRUE : FALSE);
1143
1144
1145 /* FIXME-UNCLEAN: these signal handlers can be set by (modern) versions of Glade */
1146 keywords_selection = gtk_tree_view_get_selection (ctx->keywords_treeview);
1147 g_signal_connect (G_OBJECT (keywords_selection), "changed",
1148 G_CALLBACK (keywords_selection_changed_cb), ctx);
1149 meta_selection = gtk_tree_view_get_selection (ctx->meta_treeview);
1150 g_signal_connect (G_OBJECT (meta_selection), "changed",
1151 G_CALLBACK (metadata_selection_changed_cb), ctx);
1152
1153 /* import meta data and options */
1154 GNUNET_FS_file_information_inspect (fip, &file_information_import, ctx);
1155
1156
1157 /* Finally, display window */
1158 gtk_window_set_transient_for (ctx->edit_publication_window, parent);
1159 gtk_window_present (ctx->edit_publication_window);
1160}
1161
1162
1163/* end of gnunet-fs-gtk-edit_publish_dialog.c */