gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] r16543 - in gnunet-gtk/src: . fs


From: gnunet
Subject: [GNUnet-SVN] r16543 - in gnunet-gtk/src: . fs
Date: Mon, 15 Aug 2011 16:11:40 +0200

Author: grothoff
Date: 2011-08-15 16:11:40 +0200 (Mon, 15 Aug 2011)
New Revision: 16543

Added:
   gnunet-gtk/src/fs/
   gnunet-gtk/src/fs/Makefile.am
   gnunet-gtk/src/fs/about.c
   gnunet-gtk/src/fs/common.c
   gnunet-gtk/src/fs/common.h
   gnunet-gtk/src/fs/download.c
   gnunet-gtk/src/fs/download.h
   gnunet-gtk/src/fs/edit_publish_dialog.c
   gnunet-gtk/src/fs/edit_publish_dialog.h
   gnunet-gtk/src/fs/fs_anonymity_spin_buttons.c
   gnunet-gtk/src/fs/fs_event_handler.c
   gnunet-gtk/src/fs/fs_event_handler.h
   gnunet-gtk/src/fs/gnunet-fs-gtk.c
   gnunet-gtk/src/fs/gnunet-fs-gtk.h
   gnunet-gtk/src/fs/main_window_adv_pseudonym.c
   gnunet-gtk/src/fs/main_window_create_pseudonym.c
   gnunet-gtk/src/fs/main_window_file_download.c
   gnunet-gtk/src/fs/main_window_file_publish.c
   gnunet-gtk/src/fs/main_window_file_search.c
   gnunet-gtk/src/fs/main_window_open_directory.c
   gnunet-gtk/src/fs/main_window_view_toggles.c
Log:
gnunet-fs-gtk against new libgnunetgtk library

Added: gnunet-gtk/src/fs/Makefile.am
===================================================================
--- gnunet-gtk/src/fs/Makefile.am                               (rev 0)
+++ gnunet-gtk/src/fs/Makefile.am       2011-08-15 14:11:40 UTC (rev 16543)
@@ -0,0 +1,36 @@
+SUBDIRS = .
+
+INCLUDES = \
+  -I$(top_srcdir)/ \
+  -I$(top_srcdir)/src/include \
+  @GTK_CFLAGS@ \
+  @GNUNET_CFLAGS@ \
+  @GLADE_CFLAGS@
+
+bin_PROGRAMS = gnunet-fs-gtk
+
+gnunet_fs_gtk_SOURCES = \
+  about.c \
+  common.c common.h \
+  download.c download.h \
+  edit_publish_dialog.c edit_publish_dialog.h \
+  fs_event_handler.c fs_event_handler.h \
+  fs_anonymity_spin_buttons.c \
+  gnunet-fs-gtk.c gnunet-fs-gtk.h \
+  main_window_create_pseudonym.c \
+  main_window_file_download.c \
+  main_window_file_publish.c \
+  main_window_file_search.c \
+  main_window_open_directory.c \
+  main_window_adv_pseudonym.c \
+  main_window_view_toggles.c 
+gnunet_fs_gtk_LDADD = \
+  $(top_builddir)/src/lib/libgnunetgtk.la \
+  @GTK_LIBS@ \
+  @GLADE_LIBS@ \
+  -lextractor \
+  -lgnunetutil \
+  -lgnunetfs \
+  $(INTLLIBS) 
+gnunet_fs_gtk_LDFLAGS = \
+  -export-dynamic 

Copied: gnunet-gtk/src/fs/about.c (from rev 16478, gnunet-gtk/src/about.c)
===================================================================
--- gnunet-gtk/src/fs/about.c                           (rev 0)
+++ gnunet-gtk/src/fs/about.c   2011-08-15 14:11:40 UTC (rev 16543)
@@ -0,0 +1,42 @@
+/*
+     This file is part of GNUnet
+     (C) 2005, 2006, 2010 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/fs/about.c
+ * @author Christian Grothoff
+ * @author Igor Wronsky
+ *
+ * This file contains the about dialog.
+ */
+#include "gnunet_gtk.h"
+
+
+/**
+ * This displays an about window
+ */
+void
+GNUNET_GTK_main_menu_help_about_activate_cb (GtkWidget * dummy, gpointer data)
+{
+  GNUNET_GTK_display_about ("gnunet_fs_gtk_about_dialog.glade",
+                           "GNUNET_GKT_about_dialog");
+}
+
+
+/* end of about.c */

Copied: gnunet-gtk/src/fs/common.c (from rev 16478, gnunet-gtk/src/common.c)
===================================================================
--- gnunet-gtk/src/fs/common.c                          (rev 0)
+++ gnunet-gtk/src/fs/common.c  2011-08-15 14:11:40 UTC (rev 16543)
@@ -0,0 +1,183 @@
+/*
+     This file is part of GNUnet.
+     (C) 2010 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/fs/common.c
+ * @brief Common functions used in various places
+ * @author Christian Grothoff
+ */
+#include "common.h"
+
+/**
+ * Add meta data to list store.
+ *
+ * @param cls closure (the GtkListStore)
+ * @param plugin_name name of the plugin that produced this value;
+ *        special values can be used (i.e. '<zlib>' for zlib being
+ *        used in the main libextractor library and yielding
+ *        meta data).
+ * @param type libextractor-type describing the meta data
+ * @param format basic format information about data 
+ * @param data_mime_type mime-type of data (not of the original file);
+ *        can be NULL (if mime-type is not known)
+ * @param data actual meta-data found
+ * @param data_len number of bytes in data
+ * @return 0 to continue (always)
+ */ 
+int
+GNUNET_FS_GTK_add_meta_data_to_list_store (void *cls,
+                                          const char *plugin_name,
+                                          enum EXTRACTOR_MetaType type,
+                                          enum EXTRACTOR_MetaFormat format,
+                                          const char *data_mime_type,
+                                          const char *data,
+                                          size_t data_len)
+{
+  GtkListStore *ls = GTK_LIST_STORE (cls);
+
+  if ( (format == EXTRACTOR_METAFORMAT_UTF8) ||
+       (format == EXTRACTOR_METAFORMAT_C_STRING) )
+    gtk_list_store_insert_with_values (ls,
+                                      NULL,
+                                      G_MAXINT,
+                                      0, type,
+                                      1, format,
+                                      2, EXTRACTOR_metatype_to_string (type),
+                                      3, data,
+                                      -1);
+  return 0;
+}
+
+
+/**
+ * Convert the year from the spin button to an expiration
+ * time (on midnight, January 1st of that year).
+ */
+struct GNUNET_TIME_Absolute 
+GNUNET_FS_GTK_get_expiration_time (GtkSpinButton *spin)
+{
+  struct GNUNET_TIME_Absolute ret;
+  int year;
+
+  year = gtk_spin_button_get_value_as_int (spin);
+  GNUNET_assert (year >= 0);
+  ret = GNUNET_FS_year_to_time ( (unsigned int) year);
+  GNUNET_break (GNUNET_TIME_absolute_get ().abs_value < ret.abs_value);
+  return ret;
+}
+
+
+void
+GNUNET_FS_GTK_setup_expiration_year_adjustment (GtkBuilder *builder)
+{
+  GtkAdjustment *aj;
+  unsigned int year;
+
+  year = GNUNET_FS_get_current_year ();
+  aj = GTK_ADJUSTMENT (gtk_builder_get_object (builder,
+                                              "expiration_year_adjustment"));
+  gtk_adjustment_set_value (aj, year + 2);
+  gtk_adjustment_set_lower (aj, year + 1);
+}
+
+
+GdkPixbuf *
+GNUNET_FS_GTK_get_thumbnail_from_meta_data (const struct 
GNUNET_CONTAINER_MetaData *meta)
+{
+  GdkPixbuf *pixbuf;
+  GdkPixbufLoader *loader;
+  size_t ts;
+  unsigned char *thumb;
+
+  thumb = NULL;
+  ts = GNUNET_CONTAINER_meta_data_get_thumbnail (meta, &thumb);
+  if (ts == 0)
+    return NULL;
+  loader = gdk_pixbuf_loader_new ();
+  gdk_pixbuf_loader_write (loader, (const guchar *) thumb, ts, NULL);
+  pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
+  gdk_pixbuf_loader_close (loader, NULL);
+  if (pixbuf != NULL)
+    g_object_ref (pixbuf);
+  g_object_unref (loader);
+  GNUNET_free (thumb);
+  return pixbuf;
+}
+
+
+/**
+ * mmap the given file and run the GNUNET_FS_directory_list_contents
+ * function on it.
+ */
+void
+GNUNET_FS_GTK_mmap_and_scan (const char *filename,
+                            GNUNET_FS_DirectoryEntryProcessor dep,
+                            void *dep_cls)
+{
+  struct GNUNET_DISK_FileHandle *fh;
+  struct GNUNET_DISK_MapHandle *mh;
+  uint64_t fsize;
+  void * ddata;
+
+  if (GNUNET_OK !=
+      GNUNET_DISK_file_size (filename,
+                            &fsize,
+                            GNUNET_YES))
+    {
+      GNUNET_break (0);
+      return;
+    }
+  fh = GNUNET_DISK_file_open (filename,
+                             GNUNET_DISK_OPEN_READ,
+                             GNUNET_DISK_PERM_NONE);
+  if (fh == NULL)
+    {
+      GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", filename);
+      return;
+    }
+  ddata = GNUNET_DISK_file_map (fh,
+                               &mh,
+                               GNUNET_DISK_MAP_TYPE_READ,
+                               (size_t) fsize);
+  if (ddata == NULL)
+    {
+      GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mmap", filename);
+      GNUNET_break (GNUNET_OK ==
+                   GNUNET_DISK_file_close (fh));
+      return;
+    }
+  if (GNUNET_SYSERR ==
+      GNUNET_FS_directory_list_contents ((size_t) fsize,
+                                        ddata,
+                                        0,
+                                        dep, dep_cls))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                 _("Selected file `%s' is not a GNUnet directory!\n"),
+                 filename);
+    }
+  GNUNET_break (GNUNET_OK ==
+               GNUNET_DISK_file_unmap (mh));
+  GNUNET_break (GNUNET_OK ==
+               GNUNET_DISK_file_close (fh));
+}
+
+
+/* end of common.c */

Copied: gnunet-gtk/src/fs/common.h (from rev 16478, gnunet-gtk/src/common.h)
===================================================================
--- gnunet-gtk/src/fs/common.h                          (rev 0)
+++ gnunet-gtk/src/fs/common.h  2011-08-15 14:11:40 UTC (rev 16543)
@@ -0,0 +1,89 @@
+/*
+     This file is part of GNUnet.
+     (C) 2010 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/fs/common.h
+ * @brief Common includes for all gnunet-gtk source files
+ * @author Christian Grothoff
+ */
+#ifndef COMMON_H
+#define COMMON_H
+
+#include "gnunet_gtk.h"
+#include <gnunet/gnunet_fs_service.h>
+#include <extractor.h>
+
+
+
+GdkPixbuf *
+GNUNET_FS_GTK_get_thumbnail_from_meta_data (const struct 
GNUNET_CONTAINER_MetaData *meta);
+
+/**
+ * Setup the expiration year adjustment to start with the
+ * next year and default to next year plus one.
+ */
+void
+GNUNET_FS_GTK_setup_expiration_year_adjustment (GtkBuilder *builder);
+
+/**
+ * Convert the year from the spin button to an expiration
+ * time (on midnight, January 1st of that year).
+ */
+struct GNUNET_TIME_Absolute 
+GNUNET_FS_GTK_get_expiration_time (GtkSpinButton *spin);
+
+
+/**
+ * mmap the given file and run the GNUNET_FS_directory_list_contents
+ * function on it.
+ */
+void
+GNUNET_FS_GTK_mmap_and_scan (const char *filename,
+                            GNUNET_FS_DirectoryEntryProcessor dep,
+                            void *dep_cls);
+
+
+/**
+ * Add meta data to list store.
+ *
+ * @param cls closure (the GtkListStore)
+ * @param plugin_name name of the plugin that produced this value;
+ *        special values can be used (i.e. '<zlib>' for zlib being
+ *        used in the main libextractor library and yielding
+ *        meta data).
+ * @param type libextractor-type describing the meta data
+ * @param format basic format information about data 
+ * @param data_mime_type mime-type of data (not of the original file);
+ *        can be NULL (if mime-type is not known)
+ * @param data actual meta-data found
+ * @param data_len number of bytes in data
+ * @return 0 to continue (always)
+ */ 
+int
+GNUNET_FS_GTK_add_meta_data_to_list_store (void *cls,
+                                          const char *plugin_name,
+                                          enum EXTRACTOR_MetaType type,
+                                          enum EXTRACTOR_MetaFormat format,
+                                          const char *data_mime_type,
+                                          const char *data,
+                                          size_t data_len);
+
+#endif
+/* end of common.h */

Copied: gnunet-gtk/src/fs/download.c (from rev 16478, gnunet-gtk/src/download.c)
===================================================================
--- gnunet-gtk/src/fs/download.c                                (rev 0)
+++ gnunet-gtk/src/fs/download.c        2011-08-15 14:11:40 UTC (rev 16543)
@@ -0,0 +1,136 @@
+/*
+     This file is part of GNUnet.
+     (C) 2010 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/download.c
+ * @brief functions for downloading
+ * @author Christian Grothoff
+ */
+#include "download.h"
+#include "gnunet-fs-gtk.h"
+#include "fs_event_handler.h"
+
+void
+GNUNET_GTK_open_download_as_dialog (struct DownloadContext *dc)
+{
+  GtkWidget *ad;
+  GtkBuilder *builder;
+  struct GNUNET_FS_Handle *fs;
+  uint64_t len;
+  enum GNUNET_FS_DownloadOptions opt;
+  uint32_t anonymity;
+  struct DownloadEntry *de;
+  GtkWidget *cb;
+
+  builder = GNUNET_GTK_get_new_builder ("download_as.glade");
+  if (builder == NULL)
+    {
+      if (dc->rr != NULL)
+       gtk_tree_row_reference_free (dc->rr);
+      GNUNET_free_non_null (dc->mime);
+      GNUNET_free_non_null (dc->filename);
+      GNUNET_FS_uri_destroy (dc->uri);
+      GNUNET_free (dc);
+      return;
+    }
+  cb = GTK_WIDGET (gtk_builder_get_object (builder,
+                                          
"GNUNET_GTK_save_as_recursive_check_button"));
+  if (GNUNET_FS_meta_data_test_for_directory (dc->meta))
+    gtk_widget_set_sensitive (cb, TRUE);
+  ad = GTK_WIDGET (gtk_builder_get_object (builder,
+                                          "GNUNET_GTK_save_as_dialog"));
+  if (dc->filename != NULL)
+    {
+      char buf[1024];
+      if (NULL != getcwd (buf, sizeof(buf)))
+       {
+         if (strlen (buf) + strlen (dc->filename) + 2 < sizeof(buf))
+           {
+             strcat (buf, DIR_SEPARATOR_STR);
+             strcat (buf, dc->filename);
+           }
+         gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (ad),
+                                           buf);
+       }
+    }
+  if (GTK_RESPONSE_OK != gtk_dialog_run (GTK_DIALOG (ad)))
+    {
+      gtk_widget_destroy (ad);
+      g_object_unref (G_OBJECT (builder));
+      if (dc->rr != NULL)
+       gtk_tree_row_reference_free (dc->rr);
+      GNUNET_free_non_null (dc->mime);
+      GNUNET_free_non_null (dc->filename);
+      GNUNET_FS_uri_destroy (dc->uri);
+      GNUNET_free (dc);
+      return;
+    }
+  GNUNET_free_non_null (dc->filename);
+  dc->filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(ad));
+  dc->is_recursive = (TRUE == gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON 
(cb))) ? GNUNET_YES : GNUNET_NO;
+  fs = GNUNET_FS_GTK_get_fs_handle ();
+  opt = GNUNET_FS_DOWNLOAD_OPTION_NONE;
+  if (dc->is_recursive)
+    opt |= GNUNET_FS_DOWNLOAD_OPTION_RECURSIVE;
+  anonymity = gtk_spin_button_get_value (GTK_SPIN_BUTTON 
(gtk_builder_get_object (builder,
+                                                                               
  "GNUNET_GTK_save_as_dialog_anonymity_spin_button")));
+  len = GNUNET_FS_uri_chk_get_file_size (dc->uri);
+  gtk_widget_destroy (ad);
+  g_object_unref (G_OBJECT (builder));
+  de = GNUNET_malloc (sizeof (struct DownloadEntry));
+  de->uri = dc->uri;
+  de->meta = dc->meta;
+  if (dc->rr != NULL)
+    {
+      de->rr = dc->rr;
+      de->ts = GTK_TREE_STORE (gtk_tree_row_reference_get_model (dc->rr));
+    }
+  if (dc->sr != NULL)
+    {
+      GNUNET_break (NULL !=
+                   GNUNET_FS_download_start_from_search (fs,
+                                                         dc->sr,
+                                                         dc->filename,
+                                                         NULL /* tempname */,
+                                                         0 /* offset */,
+                                                         len,
+                                                         anonymity,
+                                                         opt,
+                                                         de));
+    }
+  else
+    {
+      GNUNET_break (NULL !=
+                   GNUNET_FS_download_start (fs,
+                                             dc->uri,
+                                             NULL /* meta */,
+                                             dc->filename,
+                                             NULL /* tempname */,
+                                             0 /* offset */,
+                                             len,
+                                             anonymity,
+                                             opt,
+                                             de,
+                                             NULL /* parent download ctx */));
+    }
+  GNUNET_free (dc);
+}
+
+/* end of download.c */

Copied: gnunet-gtk/src/fs/download.h (from rev 16478, gnunet-gtk/src/download.h)
===================================================================
--- gnunet-gtk/src/fs/download.h                                (rev 0)
+++ gnunet-gtk/src/fs/download.h        2011-08-15 14:11:40 UTC (rev 16543)
@@ -0,0 +1,83 @@
+/*
+     This file is part of GNUnet.
+     (C) 2010 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/download.h
+ * @brief functions for downloading
+ * @author Christian Grothoff
+ */
+
+#ifndef DOWNLOAD_H
+#define DOWNLOAD_H
+#include "common.h"
+
+/**
+ * Information we keep for a download.
+ */
+struct DownloadContext
+{
+  /**
+   * URI for the download.
+   */
+  struct GNUNET_FS_Uri *uri;
+
+  /**
+   * Meta data.
+   */
+  struct GNUNET_CONTAINER_MetaData *meta;
+
+  /**
+   * Mime type.
+   */ 
+  char *mime;
+
+  /**
+   * Suggested filename, or NULL.
+   */
+  char *filename;
+
+  /**
+   * Row reference (if URI was found by search, or 
+   * part of directory, etc.); otherwise NULL (download by URI).
+   */
+  GtkTreeRowReference *rr;
+
+  /**
+   * Associated search result, or NULL.
+   */
+  struct GNUNET_FS_SearchResult *sr;
+
+  /**
+   * Is this a recursive download?
+   */
+  int is_recursive;
+
+  /**
+   * Desired (default) anonymity level.
+   */
+  int anonymity;
+};
+
+
+
+void
+GNUNET_GTK_open_download_as_dialog (struct DownloadContext *dc);
+
+#endif

Added: gnunet-gtk/src/fs/edit_publish_dialog.c
===================================================================
--- gnunet-gtk/src/fs/edit_publish_dialog.c                             (rev 0)
+++ gnunet-gtk/src/fs/edit_publish_dialog.c     2011-08-15 14:11:40 UTC (rev 
16543)
@@ -0,0 +1,771 @@
+/*
+     This file is part of GNUnet
+     (C) 2005, 2006, 2010 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/edit_publish_dialog.c
+ * @author Christian Grothoff
+ */
+#include "common.h"
+#include "edit_publish_dialog.h"
+#include <gnunet/gnunet_util_lib.h>
+
+/**
+ * Builder for the current dialog.
+ */
+static GtkBuilder *builder;
+
+/**
+ * Are we editing metadata for a directory?
+ */
+static int is_directory;
+
+/**
+ * Set to YES if the preview was changed.
+ */
+static int preview_changed;
+
+void
+GNUNET_GTK_edit_file_information_keyword_list_normalize_button_clicked_cb ()
+{
+  GNUNET_break (0);
+}
+
+void
+GNUNET_GTK_edit_file_information_keyword_list_del_button_clicked_cb ()
+{
+  GtkTreeView *tv;
+  GtkTreeSelection *sel;
+  GtkTreeModel *tm;
+  GtkTreeIter iter;
+
+  tv = GTK_TREE_VIEW (gtk_builder_get_object (builder,
+                                             
"GNUNET_GTK_edit_file_information_keyword_list_tree_view"));
+  sel = gtk_tree_view_get_selection (tv);
+  if (TRUE != gtk_tree_selection_get_selected (sel, &tm, &iter))
+    {
+      GNUNET_break (0);
+      return;
+    }
+  gtk_list_store_remove (GTK_LIST_STORE (tm),
+                        &iter);
+}
+
+void
+GNUNET_GTK_edit_file_information_keyword_list_add_button_clicked_cb ()
+{
+  const char *keyword;
+  GtkEntry *entry;
+  GtkListStore *ls;
+  GtkTreeIter iter;
+
+  ls = GTK_LIST_STORE (gtk_builder_get_object (builder,
+                                              
"GNUNET_GTK_keyword_list_store"));
+  entry = GTK_ENTRY (gtk_builder_get_object (builder,                          
                 
+                                            
"GNUNET_GTK_edit_file_information_keyword_entry"));
+  keyword = gtk_entry_get_text (entry);
+  if (strlen (keyword) > 0)
+    gtk_list_store_insert_with_values (ls, &iter, G_MAXINT, 0, keyword, -1);
+  gtk_entry_set_text (entry, "");
+}
+
+
+void
+GNUNET_GTK_edit_file_information_keyword_entry_changed_cb ()
+{
+  const char *keyword;
+  GtkEntry *entry;
+  GtkWidget *button;
+
+  button = GTK_WIDGET (gtk_builder_get_object (builder,                        
                         
+                                              
"GNUNET_GTK_edit_file_information_keyword_list_add_button"));
+  entry = GTK_ENTRY (gtk_builder_get_object (builder,                          
                 
+                                            
"GNUNET_GTK_edit_file_information_keyword_entry"));
+  keyword = gtk_entry_get_text (entry);
+  gtk_widget_set_sensitive (button,
+                           (strlen (keyword) > 0) ? TRUE : FALSE);
+}
+
+
+static void
+metadata_selection_changed_cb (GtkTreeSelection *ts,
+                              gpointer user_data)
+{
+  GtkTreeView *tv;
+  GtkTreeSelection *sel;
+  GtkWidget *button;
+
+  tv = GTK_TREE_VIEW (gtk_builder_get_object (builder,
+                                             
"GNUNET_GTK_edit_file_information_metadata_tree_view"));
+  sel = gtk_tree_view_get_selection (tv);
+  button = GTK_WIDGET (gtk_builder_get_object (builder,                        
                         
+                                              
"GNUNET_GTK_edit_file_information_metadata_delete_button"));
+  gtk_widget_set_sensitive (button,
+                           gtk_tree_selection_get_selected (sel, NULL, NULL));
+}
+
+
+static void
+keyword_selection_changed_cb (GtkTreeSelection *ts,
+                             gpointer user_data)
+{
+  GtkTreeView *tv;
+  GtkTreeSelection *sel;
+  GtkWidget *button;
+
+  tv = GTK_TREE_VIEW (gtk_builder_get_object (builder,
+                                             
"GNUNET_GTK_edit_file_information_keyword_list_tree_view"));
+  sel = gtk_tree_view_get_selection (tv);
+  button = GTK_WIDGET (gtk_builder_get_object (builder,                        
                         
+                                              
"GNUNET_GTK_edit_file_information_keyword_list_del_button"));
+
+  gtk_widget_set_sensitive (button,
+                           gtk_tree_selection_get_selected (sel, NULL, NULL));
+  button = GTK_WIDGET (gtk_builder_get_object (builder,                        
                         
+                                              
"GNUNET_GTK_edit_file_information_keyword_list_normalize_button"));
+  gtk_widget_set_sensitive (button,
+                           gtk_tree_selection_get_selected (sel, NULL, NULL));
+}
+
+
+void
+GNUNET_GTK_edit_file_information_metadata_value_entry_changed_cb ()
+{  
+  GtkTreeView *tv;
+  GtkTreeSelection *sel;
+  const char *value;
+  GtkEntry *entry;
+  GtkWidget *button;
+
+  entry = GTK_ENTRY (gtk_builder_get_object (builder,                          
                 
+                                            
"GNUNET_GTK_edit_file_information_metadata_value_entry"));
+  value = gtk_entry_get_text (entry);
+  tv = GTK_TREE_VIEW (gtk_builder_get_object (builder,
+                                             
"GNUNET_GTK_edit_file_information_metadata_type_tree_view"));
+  sel = gtk_tree_view_get_selection (tv);
+  button = GTK_WIDGET (gtk_builder_get_object (builder,                        
                         
+                                              
"GNUNET_GTK_edit_file_information_metadata_add_button"));  
+  gtk_widget_set_sensitive (button,
+                           (strlen (value) > 0)
+                           ? gtk_tree_selection_get_selected (sel, NULL, NULL) 
+                           : FALSE);
+}
+
+
+void
+GNUNET_GTK_edit_file_information_keyword_entry_activate_cb ()
+{
+  GNUNET_GTK_edit_file_information_keyword_list_add_button_clicked_cb ();
+}
+
+
+void
+GNUNET_GTK_edit_file_information_metadata_preview_file_chooser_button_file_set_cb
 (GtkFileChooserButton *widget,
+                                                                               
   gpointer user_data)
+{
+  gchar *fn;
+  GtkImage *image;
+
+  fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget));
+  image = GTK_IMAGE (gtk_builder_get_object (builder,                          
                 
+                                            
"GNUNET_GTK_edit_file_information_metadata_preview_image"));
+  gtk_image_set_from_file (image, fn);
+  preview_changed = GNUNET_YES;
+}
+
+
+void
+GNUNET_GTK_edit_file_information_metadata_delete_button_clicked_cb()
+{
+  GtkTreeView *tv;
+  GtkTreeSelection *sel;
+  GtkTreeModel *tm;
+  GtkTreeIter iter;
+
+  tv = GTK_TREE_VIEW (gtk_builder_get_object (builder,
+                                             
"GNUNET_GTK_edit_file_information_metadata_tree_view"));
+  sel = gtk_tree_view_get_selection (tv);
+  if (TRUE != gtk_tree_selection_get_selected (sel, &tm, &iter))
+    {
+      GNUNET_break (0);
+      return;
+    }
+  gtk_list_store_remove (GTK_LIST_STORE (tm),
+                        &iter);
+}
+
+
+void
+GNUNET_GTK_edit_file_information_metadata_add_button_clicked_cb ()
+{
+  const char *value;
+  char *avalue;
+  const char *ivalue;
+  GtkEntry *entry;
+  GtkListStore *ls;
+  GtkTreeModel *tm;
+  GtkTreeView *tv;
+  GtkTreeSelection *sel;
+  GtkTreeIter iter;
+  guint type;
+  size_t slen;
+  char *pos;
+
+  entry = GTK_ENTRY (gtk_builder_get_object (builder,
+                                            
"GNUNET_GTK_edit_file_information_metadata_value_entry"));
+  value = gtk_entry_get_text (entry);
+  if ((value == NULL) || (strlen (value) == 0))
+    {
+      GNUNET_break (0);
+      return;
+    }
+  tv = GTK_TREE_VIEW (gtk_builder_get_object (builder,
+                                             
"GNUNET_GTK_edit_file_information_metadata_type_tree_view"));
+  tm = gtk_tree_view_get_model (tv);
+  sel = gtk_tree_view_get_selection (tv);
+  if (TRUE != gtk_tree_selection_get_selected (sel,
+                                              &tm,
+                                              &iter))
+    {
+      GNUNET_break (0);
+      return;
+    }
+  gtk_tree_model_get (tm,
+                      &iter, 
+                     1, &type, -1);
+  ls = GTK_LIST_STORE (gtk_builder_get_object (builder,
+                                              
"GNUNET_GTK_meta_data_list_store"));
+  if ( (type == EXTRACTOR_METATYPE_FILENAME) &&
+       (value[strlen(value)-1] != '/') &&
+       (is_directory) )
+    {      
+      GNUNET_asprintf (&avalue,
+                      "%s/",
+                      value);
+      /* if user typed '\' instead of '/', change it! */
+      slen = strlen (avalue);
+      while ( (slen > 1) &&
+             (avalue[slen-2] == '\\') )
+       {
+         avalue[slen-2] = '/';
+         avalue[slen-1] = '\0';
+         slen--;
+       }
+      while (NULL != (pos = strstr (avalue, "\\")))
+       *pos = '/';
+      /* remove '../' everywhere */
+      while (NULL != (pos = strstr (avalue, "../")))
+       {
+         pos[0] = '_';
+         pos[1] = '_';
+         pos[2] = '_';
+       }
+      ivalue = avalue;
+    }
+  else
+    {
+      ivalue = value;
+      avalue = NULL;
+    }
+
+  gtk_list_store_insert_with_values (ls, &iter, G_MAXINT,
+                                    0, type,
+                                    1, (guint) EXTRACTOR_METAFORMAT_UTF8,
+                                    2, EXTRACTOR_metatype_to_string (type),
+                                    3, ivalue,
+                                    -1);
+  GNUNET_free_non_null (avalue);   
+  gtk_entry_set_text (GTK_ENTRY (entry), "");
+}
+
+
+/**
+ * Add each of the keywords to the keyword list store.
+ *
+ * @param cls closure
+ * @param keyword the keyword
+ * @param is_mandatory is the keyword mandatory (in a search)
+ * @return GNUNET_OK to continue to iterate
+ */
+static int
+add_keyword (void *cls,
+            const char *keyword,
+            int is_mandatory)
+{
+  GtkListStore *ls;
+  GtkTreeIter iter;
+  
+  ls = GTK_LIST_STORE (gtk_builder_get_object (builder,
+                                              
"GNUNET_GTK_keyword_list_store"));
+  gtk_list_store_insert_with_values (ls, &iter, 
+                                    G_MAXINT,
+                                    0, keyword,
+                                    -1);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Add the given meta data to the model (or make it the preview
+ * image if it is an image).
+ *
+ * @param cls closure, NULL
+ * @param plugin_name name of the plugin that produced this value;
+ *        special values can be used (i.e. '<zlib>' for zlib being
+ *        used in the main libextractor library and yielding
+ *        meta data).
+ * @param type libextractor-type describing the meta data
+ * @param format basic format information about data 
+ * @param data_mime_type mime-type of data (not of the original file);
+ *        can be NULL (if mime-type is not known)
+ * @param data actual meta-data found
+ * @param data_len number of bytes in data
+ * @return 0 to continue extracting
+ */ 
+static int
+add_meta_item (void *cls,
+              const char *plugin_name,
+              enum EXTRACTOR_MetaType type,
+              enum EXTRACTOR_MetaFormat format,
+              const char *data_mime_type,
+              const char *data,
+              size_t data_len)
+{
+  GtkListStore *ls;
+  GtkTreeIter iter;
+  
+  switch (format)
+    {
+    case EXTRACTOR_METAFORMAT_UTF8:
+    case EXTRACTOR_METAFORMAT_C_STRING:
+      ls = GTK_LIST_STORE (gtk_builder_get_object (builder,
+                                                  
"GNUNET_GTK_meta_data_list_store"));
+      gtk_list_store_insert_with_values (ls, &iter, 
+                                        G_MAXINT,
+                                        0, (guint) type,
+                                        1, (guint) format,
+                                        2, EXTRACTOR_metatype_to_string (type),
+                                        3, data,
+                                        -1);
+      break;
+    case EXTRACTOR_METAFORMAT_UNKNOWN:
+      break;
+    case EXTRACTOR_METAFORMAT_BINARY:
+      break;
+    default:
+      GNUNET_break (0);
+    }
+  return 0;
+}
+
+
+/**
+ * Function called to extract the information from FI.
+ *
+ * @param cls closure
+ * @param fi the entry in the publish-structure
+ * @param length length of the file or directory
+ * @param meta metadata for the file or directory (can be modified)
+ * @param uri pointer to the keywords that will be used for this entry (can be 
modified)
+ * @param bo block options
+ * @param do_index should we index (can be modified)
+ * @param client_info pointer to client context set upon creation (can be 
modified)
+ * @return GNUNET_SYSERR (aborts after first call)
+ */
+static int 
+file_information_extract (void *cls,
+                         struct GNUNET_FS_FileInformation *fi,
+                         uint64_t length,
+                         struct GNUNET_CONTAINER_MetaData *meta,
+                         struct GNUNET_FS_Uri **uri,
+                         struct GNUNET_FS_BlockOptions *bo,
+                         int *do_index,
+                         void **client_info)
+{
+  GtkImage *img;
+  GdkPixbuf *pixbuf;
+  struct tm *t;
+  int year;
+  time_t tp;
+
+  if (NULL != *uri)
+    GNUNET_FS_uri_ksk_get_keywords (*uri, &add_keyword, NULL);
+  if (NULL != meta)
+    {
+      GNUNET_CONTAINER_meta_data_iterate (meta,
+                                         &add_meta_item,
+                                         NULL);
+      pixbuf = GNUNET_FS_GTK_get_thumbnail_from_meta_data (meta);
+      if (pixbuf != NULL)
+       {
+         img = GTK_IMAGE (gtk_builder_get_object (builder,
+                                                  
"GNUNET_GTK_edit_file_information_metadata_preview_image"));
+         gtk_image_set_from_pixbuf (img,
+                                    pixbuf);
+       }
+    }
+  tp = bo->expiration_time.abs_value / 1000; /* ms to seconds */
+  t = gmtime (&tp);
+  if (t == NULL)
+    year = 0;
+  else
+    year = t->tm_year + 1900;
+  gtk_spin_button_set_value (GTK_SPIN_BUTTON (gtk_builder_get_object (builder,
+                                                                     
"GNUNET_GTK_edit_file_information_expiration_year_spin_button")),
+                            year);
+  gtk_spin_button_set_value (GTK_SPIN_BUTTON (gtk_builder_get_object (builder,
+                                                                     
"GNUNET_GTK_edit_file_information_anonymity_spin_button")),
+                            bo->anonymity_level);
+  gtk_spin_button_set_value (GTK_SPIN_BUTTON (gtk_builder_get_object (builder,
+                                                                     
"GNUNET_GTK_edit_file_information_priority_spin_button")),
+                            bo->content_priority);
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gtk_builder_get_object 
(builder,
+                                                                          
"GNUNET_GTK_edit_file_information_index_check_button")),
+                               *do_index);
+  return GNUNET_SYSERR; /* only visit top-level item */
+}
+
+
+/**
+ * Copy binary meta data from to the new container and also
+ * preserve all entries that were not changed.
+ *
+ * @param cls closure, new meta data container
+ * @param plugin_name name of the plugin that produced this value;
+ *        special values can be used (i.e. '<zlib>' for zlib being
+ *        used in the main libextractor library and yielding
+ *        meta data).
+ * @param type libextractor-type describing the meta data
+ * @param format basic format information about data 
+ * @param data_mime_type mime-type of data (not of the original file);
+ *        can be NULL (if mime-type is not known)
+ * @param data actual meta-data found
+ * @param data_len number of bytes in data
+ * @return 0 to continue extracting
+ */ 
+static int
+preserve_meta_items (void *cls,
+                    const char *plugin_name,
+                    enum EXTRACTOR_MetaType type,
+                    enum EXTRACTOR_MetaFormat format,
+                    const char *data_mime_type,
+                    const char *data,
+                    size_t data_len)
+{
+  struct GNUNET_CONTAINER_MetaData *md = cls;
+  GtkTreeModel *tm;
+  GtkTreeIter iter;
+  gchar *value;
+  guint ntype;  
+  guint nformat;
+  int keep;
+  
+  keep = GNUNET_NO;
+  switch (format)
+    {
+    case EXTRACTOR_METAFORMAT_UTF8:
+    case EXTRACTOR_METAFORMAT_C_STRING:
+      tm = GTK_TREE_MODEL (gtk_builder_get_object (builder,
+                                                  
"GNUNET_GTK_meta_data_list_store"));
+      if (TRUE ==
+         gtk_tree_model_get_iter_first (tm, &iter))
+       {
+         do
+           {
+             gtk_tree_model_get (tm, &iter,
+                                 0, &ntype,
+                                 1, &nformat,
+                                 3, &value,
+                                 -1);
+             if ( (ntype == type) &&
+                  (nformat == format) &&
+                  (0 == strcmp (value, data)) )
+               {
+                 gtk_list_store_remove (GTK_LIST_STORE (tm), &iter);           
  
+                 keep = GNUNET_YES;
+                 g_free (value);
+                 break;
+               }
+             g_free (value);
+           }
+         while (TRUE == gtk_tree_model_iter_next (tm, &iter));
+       }
+      break;
+    case EXTRACTOR_METAFORMAT_UNKNOWN:
+      break;
+    case EXTRACTOR_METAFORMAT_BINARY:
+      if (preview_changed == GNUNET_NO)
+       keep = GNUNET_YES;      
+      break;
+    default:
+      GNUNET_break (0);
+      break;
+    }
+  if (GNUNET_YES == keep)
+    GNUNET_break (GNUNET_OK ==
+                 GNUNET_CONTAINER_meta_data_insert (md,
+                                                    plugin_name, type, format,
+                                                    data_mime_type, data, 
data_len));
+  return 0;
+}
+
+
+/**
+ * Function called to update the information in FI.
+ *
+ * @param cls closure (short_fn to update)
+ * @param fi the entry in the publish-structure
+ * @param length length of the file or directory
+ * @param meta metadata for the file or directory (can be modified)
+ * @param uri pointer to the keywords that will be used for this entry (can be 
modified)
+ * @param bo block options (can be modified)
+ * @param do_index should we index (can be modified)
+ * @param client_info pointer to client context set upon creation (can be 
modified)
+ * @return GNUNET_SYSERR (aborts after first call)
+ */
+static int 
+file_information_update (void *cls,
+                        struct GNUNET_FS_FileInformation *fi,
+                        uint64_t length,
+                        struct GNUNET_CONTAINER_MetaData *meta,
+                        struct GNUNET_FS_Uri **uri,
+                        struct GNUNET_FS_BlockOptions *bo,
+                        int *do_index,
+                        void **client_info)
+{
+  char **short_fn = cls;
+  struct GNUNET_CONTAINER_MetaData *nm;
+  GtkTreeModel *tm;
+  GtkTreeIter iter;
+  struct GNUNET_FS_Uri *nxt;
+  struct GNUNET_FS_Uri *mrg;
+  gchar *value;
+  guint ntype;  
+  guint nformat;
+  GtkSpinButton *sb;
+  gchar *fn;
+  char *data;
+  gsize data_size;
+  const char *mime;
+  GFile *f;
+  GFileInfo *finfo;
+  
+  bo->anonymity_level = gtk_spin_button_get_value (GTK_SPIN_BUTTON 
(gtk_builder_get_object (builder,
+                                                                               
            "GNUNET_GTK_edit_file_information_anonymity_spin_button")));
+  bo->content_priority = gtk_spin_button_get_value (GTK_SPIN_BUTTON 
(gtk_builder_get_object (builder,
+                                                                               
             "GNUNET_GTK_edit_file_information_priority_spin_button")));
+  *do_index = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON 
(gtk_builder_get_object (builder,
+                                                                               
       "GNUNET_GTK_edit_file_information_index_check_button")));
+  sb =GTK_SPIN_BUTTON (gtk_builder_get_object (builder,
+                                              
"GNUNET_GTK_edit_file_information_expiration_year_spin_button"));
+  bo->expiration_time = GNUNET_FS_GTK_get_expiration_time (sb);
+  /* update URI */
+  if (NULL != (*uri))
+    GNUNET_FS_uri_destroy (*uri);
+  *uri = NULL;
+  nxt = NULL;
+  mrg = NULL;
+  
+  tm = GTK_TREE_MODEL (gtk_builder_get_object (builder,
+                                              
"GNUNET_GTK_keyword_list_store"));
+  if (TRUE ==
+      gtk_tree_model_get_iter_first (tm, &iter))
+    {
+      do
+       {
+         gtk_tree_model_get (tm, &iter,
+                             0, &value,
+                             -1);
+         nxt = GNUNET_FS_uri_ksk_create_from_args (1, (const char**) &value);
+         mrg = GNUNET_FS_uri_ksk_merge (nxt, *uri);
+         GNUNET_FS_uri_destroy (nxt);
+         if (NULL != *uri)
+           GNUNET_FS_uri_destroy (*uri);
+         *uri = mrg;
+         g_free (value);
+       }
+      while (TRUE == gtk_tree_model_iter_next (tm, &iter));
+    }
+
+  /* update meta */
+  nm = GNUNET_CONTAINER_meta_data_create ();
+  GNUNET_CONTAINER_meta_data_iterate (meta,
+                                     &preserve_meta_items,
+                                     nm);
+
+  GNUNET_CONTAINER_meta_data_clear (meta);
+  tm = GTK_TREE_MODEL (gtk_builder_get_object (builder,
+                                              
"GNUNET_GTK_meta_data_list_store"));
+  if (TRUE ==
+      gtk_tree_model_get_iter_first (tm, &iter))
+    {
+      do
+       {
+         gtk_tree_model_get (tm, &iter,
+                             0, &ntype,
+                             1, &nformat,
+                             3, &value,
+                             -1);
+         GNUNET_CONTAINER_meta_data_insert (nm,
+                                            "<user>",
+                                            ntype,
+                                            nformat,
+                                            "text/plain",
+                                            value,
+                                            strlen (value)+1);
+         g_free (value);
+       }
+      while (TRUE == gtk_tree_model_iter_next (tm, &iter));
+    }
+  GNUNET_CONTAINER_meta_data_merge (meta, nm);
+  if (preview_changed == GNUNET_YES)
+    {
+      fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER 
(gtk_builder_get_object (builder,                                            
+                                                                               
    "GNUNET_GTK_edit_file_information_metadata_preview_file_chooser_button")));
+      f = g_file_new_for_path (fn);
+      finfo = g_file_query_info (f, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, 0, 
NULL, NULL);
+      if (FALSE == g_file_load_contents (f, NULL, &data, &data_size, NULL, 
NULL))
+       {
+         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                     _("Could not load preview `%s' into memory\n"),
+                     fn);
+       }
+      else
+       {
+         mime = g_file_info_get_attribute_string (finfo,
+                                                  
G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
+         GNUNET_CONTAINER_meta_data_insert (meta,
+                                            "<user>",
+                                            EXTRACTOR_METATYPE_THUMBNAIL,
+                                            EXTRACTOR_METAFORMAT_BINARY,
+                                            mime,
+                                            data,
+                                            data_size);
+       }
+      g_object_unref (finfo);
+      g_object_unref (f);
+    }
+  GNUNET_CONTAINER_meta_data_destroy (nm);
+    
+  /* update short_fn */
+  GNUNET_free_non_null (*short_fn);
+  *short_fn = GNUNET_CONTAINER_meta_data_get_first_by_types (meta,
+                                                            
EXTRACTOR_METATYPE_FILENAME,
+                                                            -1);
+  /* FIXME: update expiration time? (not yet in dialog!) */
+  return GNUNET_SYSERR; /* only visit top-level item */
+}
+
+
+/**
+ * Open the dialog to edit file information data.
+ */
+void
+GNUNET_GTK_edit_publish_dialog (int *do_index,
+                               char **short_fn,
+                               guint *anonymity_level,
+                               guint *priority,
+                               struct GNUNET_FS_FileInformation *fip)
+{
+  GtkWidget *ad;
+  GtkListStore *ls;
+  GtkTreeIter iter;
+  guint type;
+  guint max_type;
+  GtkTreeView *tv;
+  GtkTreeSelection *sel;
+
+  GNUNET_assert (builder == NULL);
+  builder = GNUNET_GTK_get_new_builder ("publish_edit_dialog.glade");
+  if (builder == NULL)
+    {
+      GNUNET_break (0);
+      return;
+    }
+  preview_changed = GNUNET_NO;
+  GNUNET_FS_GTK_setup_expiration_year_adjustment (builder);
+  if (GNUNET_FS_file_information_is_directory (fip))
+    {
+      /* indexing does not apply to directories */
+      gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (builder,
+                                                                   
"GNUNET_GTK_edit_file_information_index_check_button")));
+      gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (builder,
+                                                                  
"GNUNET_GTK_edit_file_information_index_label")));
+      
+    }
+
+  ad = GTK_WIDGET (gtk_builder_get_object (builder,
+                                          
"GNUNET_GTK_edit_file_information_dialog"));
+  ls = GTK_LIST_STORE (gtk_builder_get_object (builder,
+                                              
"GNUNET_GTK_metatype_list_store"));
+  max_type = EXTRACTOR_metatype_get_max ();
+  type = 1; 
+  while (type < max_type - 1)
+    {
+      gtk_list_store_insert_with_values (ls, &iter, G_MAXINT,
+                                        0, EXTRACTOR_metatype_to_string (type),
+                                        1, type,
+                                        2, EXTRACTOR_metatype_to_description 
(type),
+                                        -1);
+      type++;
+    }
+
+  GNUNET_FS_file_information_inspect (fip, 
+                                     &file_information_extract,
+                                     NULL);
+  tv = GTK_TREE_VIEW (gtk_builder_get_object (builder,
+                                             
"GNUNET_GTK_edit_file_information_metadata_type_tree_view"));
+  sel = gtk_tree_view_get_selection (tv);
+  g_signal_connect(G_OBJECT(sel), "changed", 
+                  
G_CALLBACK(GNUNET_GTK_edit_file_information_metadata_value_entry_changed_cb), 
NULL); 
+
+  tv = GTK_TREE_VIEW (gtk_builder_get_object (builder,
+                                             
"GNUNET_GTK_edit_file_information_metadata_tree_view"));
+  sel = gtk_tree_view_get_selection (tv);
+  g_signal_connect(G_OBJECT(sel), "changed", 
+                  G_CALLBACK(metadata_selection_changed_cb), NULL); 
+
+  tv = GTK_TREE_VIEW (gtk_builder_get_object (builder,
+                                             
"GNUNET_GTK_edit_file_information_keyword_list_tree_view"));
+  sel = gtk_tree_view_get_selection (tv);
+  g_signal_connect(G_OBJECT(sel), "changed", 
+                  G_CALLBACK(keyword_selection_changed_cb), NULL); 
+  gtk_window_set_title (GTK_WINDOW (ad), *short_fn);
+  if (GTK_RESPONSE_OK != gtk_dialog_run (GTK_DIALOG (ad)))
+    {
+      gtk_widget_destroy (ad);
+      g_object_unref (G_OBJECT (builder));
+      builder = NULL;
+      return;
+    }
+  GNUNET_FS_file_information_inspect (fip, 
+                                     &file_information_update,
+                                     short_fn);
+  *anonymity_level = gtk_spin_button_get_value (GTK_SPIN_BUTTON 
(gtk_builder_get_object (builder,
+                                                                               
         "GNUNET_GTK_edit_file_information_anonymity_spin_button")));
+  *priority = gtk_spin_button_get_value (GTK_SPIN_BUTTON 
(gtk_builder_get_object (builder,
+                                                                               
  "GNUNET_GTK_edit_file_information_priority_spin_button")));
+  *do_index = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON 
(gtk_builder_get_object (builder,
+                                                                               
       "GNUNET_GTK_edit_file_information_index_check_button")));
+  gtk_widget_destroy (ad);
+  g_object_unref (G_OBJECT (builder));
+  builder = NULL;
+}
+
+/* end of edit_publish_dialog.c */

Added: gnunet-gtk/src/fs/edit_publish_dialog.h
===================================================================
--- gnunet-gtk/src/fs/edit_publish_dialog.h                             (rev 0)
+++ gnunet-gtk/src/fs/edit_publish_dialog.h     2011-08-15 14:11:40 UTC (rev 
16543)
@@ -0,0 +1,41 @@
+/*
+     This file is part of GNUnet
+     (C) 2005, 2006, 2010 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/edit_publish_dialog.h
+ * @author Christian Grothoff
+ */
+#ifndef EDIT_PUBLISH_DIALOG_H
+#define EDIT_PUBLISH_DIALOG_H
+
+#include "common.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_fs_service.h>
+
+
+void
+GNUNET_GTK_edit_publish_dialog (int *do_index,
+                               char **short_fn,
+                               guint *anonymity_level,
+                               guint *priority,
+                               struct GNUNET_FS_FileInformation *fip);
+
+#endif
+/* end of edit_publish_dialog.h */

Added: gnunet-gtk/src/fs/fs_anonymity_spin_buttons.c
===================================================================
--- gnunet-gtk/src/fs/fs_anonymity_spin_buttons.c                               
(rev 0)
+++ gnunet-gtk/src/fs/fs_anonymity_spin_buttons.c       2011-08-15 14:11:40 UTC 
(rev 16543)
@@ -0,0 +1,73 @@
+/*
+     This file is part of GNUnet
+     (C) 2005, 2006, 2010 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/fs_anonymtiy_spin_buttons.c
+ * @author Christian Grothoff
+ *
+ * This file contains callbacks to turn spin buttons red when the
+ * value reaches zero.
+ */
+#include "common.h"
+
+
+/**
+ * Spin button is changed, update its colour.
+ */
+void
+GNUNET_GTK_anonymity_spin_button_value_changed_cb (GtkWidget *w, 
+                                                  gpointer data)
+{
+  GtkSpinButton *spin;
+  gint val;
+  GdkColor bcolor;
+  GdkColor fcolor;
+
+  spin = GTK_SPIN_BUTTON (w);
+  if (spin == NULL)
+    {
+      GNUNET_break (0);
+      return;
+    }
+  val = gtk_spin_button_get_value_as_int (spin);
+  if (val == 0)
+    {
+      if ((TRUE == gdk_color_parse ("red",
+                                    &bcolor)) &&
+          (TRUE == gdk_colormap_alloc_color (gdk_colormap_get_system (),
+                                             &bcolor, FALSE, TRUE)) &&
+         (TRUE == gdk_color_parse ("black",
+                                    &fcolor)) &&
+          (TRUE == gdk_colormap_alloc_color (gdk_colormap_get_system (),
+                                             &fcolor, FALSE, TRUE)))
+       {
+         gtk_widget_modify_base (w, GTK_STATE_NORMAL, &bcolor);
+         gtk_widget_modify_text (w, GTK_STATE_NORMAL, &fcolor);
+       }
+    }
+  else
+    {
+      gtk_widget_modify_base (w, GTK_STATE_NORMAL, NULL);
+      gtk_widget_modify_text (w, GTK_STATE_NORMAL, NULL);
+    }
+}
+
+
+/* end of fs_anonymtiy_spin_buttons.c */

Copied: gnunet-gtk/src/fs/fs_event_handler.c (from rev 16478, 
gnunet-gtk/src/fs_event_handler.c)
===================================================================
--- gnunet-gtk/src/fs/fs_event_handler.c                                (rev 0)
+++ gnunet-gtk/src/fs/fs_event_handler.c        2011-08-15 14:11:40 UTC (rev 
16543)
@@ -0,0 +1,2028 @@
+/*
+     This file is part of GNUnet.
+     (C) 2010 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/fs_event_handler.c
+ * @brief Main event handler for file-sharing
+ * @author Christian Grothoff
+ */
+#include "common.h"
+#include "gnunet-fs-gtk.h"
+#include "download.h"
+#include "fs_event_handler.h"
+#include <string.h>
+
+static struct SearchTab *search_tab_head;
+
+static struct SearchTab *search_tab_tail;
+
+struct PublishTab
+{
+  /**
+   * This is a doubly-linked list.
+   */
+  struct PublishTab *next;
+
+  /**
+   * This is a doubly-linked list.
+   */
+  struct PublishTab *prev;
+
+  GtkWidget *frame;
+
+  GtkBuilder *builder;
+
+  /**
+   * Associated (top-level) FS publish operation.
+   */
+  struct GNUNET_FS_PublishContext *pc;
+
+  GtkTreeStore *ts;
+};
+
+
+struct PublishEntry
+{
+  /**
+   * Associated FS publish operation.
+   */
+  struct GNUNET_FS_PublishContext *pc;
+
+  /**
+   * Tab storing this entry.
+   */ 
+  struct PublishTab *tab;
+
+  /**
+   * Where in the tab is this entry?
+   */ 
+  GtkTreeRowReference *rr;
+
+  /**
+   * URI of the file (set after completion).
+   */ 
+  struct GNUNET_FS_Uri *uri;
+  
+  int is_top;
+};
+
+
+struct SearchResult
+{
+  /**
+   * Where in the tab is this result?
+   */ 
+  GtkTreeRowReference *rr;
+
+  /**
+   * Tab storing this result.
+   */ 
+  struct SearchTab *tab;
+
+  /**
+   * Search result for top-level results and
+   * namespace-update results.
+   */
+  struct GNUNET_FS_SearchResult *result;
+
+  /**
+   * Associated download, or NULL for none.
+   */
+  struct DownloadEntry *download;
+};
+
+
+
+static struct PublishTab *publish_tab_head;
+
+static struct PublishTab *publish_tab_tail;
+
+
+static struct DownloadEntry *
+change_download_colour (struct DownloadEntry *de,
+                      const char *colour)
+{
+  GtkTreeIter iter;
+  GtkTreePath *path;
+  
+  path = gtk_tree_row_reference_get_path (de->rr);
+  if (TRUE != gtk_tree_model_get_iter (GTK_TREE_MODEL (de->ts), 
+                                      &iter, path))
+    {
+      GNUNET_break (0);
+      gtk_tree_path_free (path);
+      return de;
+    }
+  gtk_tree_path_free (path);
+  gtk_tree_store_set (de->ts, &iter,
+                     8, colour,
+                     -1);
+  return de;
+}
+
+
+static struct PublishEntry *
+change_publish_colour (struct PublishEntry *pe,
+                      const char *colour)
+{
+  GtkTreeIter iter;
+  GtkTreePath *path;
+
+  if (pe == NULL)
+    {
+      GNUNET_break (0);
+      return NULL;
+    }  
+  path = gtk_tree_row_reference_get_path (pe->rr);
+  if (TRUE != gtk_tree_model_get_iter (GTK_TREE_MODEL (pe->tab->ts), 
+                                      &iter, path))
+    {
+      GNUNET_break (0);
+      gtk_tree_path_free (path);
+      return pe;
+    }
+  gtk_tree_path_free (path);
+  gtk_tree_store_set (pe->tab->ts, &iter,
+                     2, colour,
+                     -1);
+  return pe;
+}
+
+
+static void 
+stop_download (struct DownloadEntry *de,
+              int is_suspend)
+{
+  change_download_colour (de, "white");
+  gtk_tree_row_reference_free (de->rr);
+  if (is_suspend == GNUNET_NO)
+    GNUNET_FS_download_stop (de->dc, GNUNET_YES);
+  GNUNET_FS_uri_destroy (de->uri);
+  GNUNET_CONTAINER_meta_data_destroy (de->meta);
+  GNUNET_free (de);
+}
+
+
+
+struct AddDirectoryEntryContext
+{
+
+  struct DownloadEntry *de;
+
+  /**
+   * Row reference of parent (the directory).
+   */ 
+  GtkTreeRowReference *prr;
+
+  int check_duplicates;
+
+};
+
+
+/**
+ * Function used to process entries in a directory.
+ *
+ * @param cls closure, our 'struct AddDirectoryEntryContext*'
+ * @param filename name of the file in the directory
+ * @param uri URI of the file
+ * @param metadata metadata for the file; metadata for
+ *        the directory if everything else is NULL/zero
+ * @param length length of the available data for the file
+ *           (of type size_t since data must certainly fit
+ *            into memory; if files are larger than size_t
+ *            permits, then they will certainly not be
+ *            embedded with the directory itself).
+ * @param data data available for the file (length bytes)
+ */
+static void 
+add_directory_entry (void *cls,
+                    const char *filename,
+                    const struct GNUNET_FS_Uri *uri,
+                    const struct GNUNET_CONTAINER_MetaData *meta,
+                    size_t length,
+                    const void *data)
+{
+  struct AddDirectoryEntryContext *ade = cls;
+  GtkTreeIter iter;
+  GtkTreeIter piter;
+  GtkTreePath *path;
+  GtkTreeModel *tm;
+  struct GNUNET_FS_Uri *xuri;
+
+  if (uri == NULL)
+    {
+      /* directory meta data itself */
+      /* FIXME: consider merging it in... */
+      return;
+    }
+  if (ade->check_duplicates == GNUNET_YES)
+    {
+      path = gtk_tree_row_reference_get_path (ade->prr);
+      tm = gtk_tree_row_reference_get_model (ade->prr);
+      if (TRUE != gtk_tree_model_get_iter (tm,
+                                          &piter, path))
+       {
+         GNUNET_break (0);
+         gtk_tree_path_free (path);
+         return;
+       }
+      gtk_tree_path_free (path);
+      if (TRUE == gtk_tree_model_iter_children (tm, 
+                                               &iter,
+                                               &piter))
+       {
+         do
+           {
+             gtk_tree_model_get (tm,
+                                 &iter,
+                                 1, &xuri,
+                                 -1);
+             if (GNUNET_YES == 
+                 GNUNET_FS_uri_test_equal (xuri, uri))
+               return; /* already present */           
+           }
+         while (TRUE == gtk_tree_model_iter_next (tm, &iter));
+       }
+    }
+  GNUNET_GTK_add_search_result (ade->de->tab,
+                               &iter,
+                               ade->prr,
+                               uri,
+                               meta,
+                               NULL,
+                               0);
+}
+
+
+static struct DownloadEntry *
+mark_download_progress (struct DownloadEntry *de,
+                       uint64_t size,
+                       uint64_t completed,
+                       const void *block_data,
+                       uint64_t offset,
+                       uint64_t block_size,
+                       unsigned int depth)
+{
+  struct AddDirectoryEntryContext ade;
+  GtkTreeIter iter;
+  GtkTreePath *path;
+  
+  path = gtk_tree_row_reference_get_path (de->rr);
+  if (TRUE != gtk_tree_model_get_iter (GTK_TREE_MODEL (de->ts), 
+                                      &iter, path))
+    {
+      GNUNET_break (0);
+      gtk_tree_path_free (path);
+      return de;
+    }
+  gtk_tree_path_free (path);
+  gtk_tree_store_set (de->ts, &iter,
+                     4, (guint) ((size > 0) ? (100 * completed / size) : 100) 
/* progress */,
+                     -1);
+  if ( (depth == 0) &&
+       (block_size > 0) &&
+       (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (de->meta)) )
+    {
+      ade.de = de;
+      ade.prr = de->rr;
+      ade.check_duplicates = GNUNET_NO;
+      if (GNUNET_SYSERR ==
+         GNUNET_FS_directory_list_contents ((size_t) block_size,
+                                            block_data,
+                                            offset,
+                                            &add_directory_entry,
+                                            &ade))
+       {
+         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                     _("Metadata wrongly claims that this is a GNUnet 
directory!\n"));
+       }
+    }
+  return de;
+}
+
+
+static struct DownloadEntry *
+mark_download_error (struct DownloadEntry *de,
+                    const char *emsg)
+{
+  GtkTreeIter iter;
+  GtkTreePath *path;
+
+  de = change_download_colour (de,
+                              "red");    
+  de->is_done = GNUNET_YES;  
+  path = gtk_tree_row_reference_get_path (de->rr);
+  if (TRUE != gtk_tree_model_get_iter (GTK_TREE_MODEL (de->tab->ts), 
+                                      &iter, path))
+    {
+      GNUNET_break (0);
+      gtk_tree_path_free (path);
+      return de;
+    }
+  gtk_tree_path_free (path);
+  gtk_tree_store_set (de->tab->ts, &iter,
+                     4, 0,
+                     7, emsg,                
+                     -1);
+  return de;
+}
+
+
+static struct DownloadEntry *
+mark_download_completed (struct DownloadEntry *de,
+                        uint64_t size,
+                        const char *filename)
+{
+  struct AddDirectoryEntryContext ade;
+  
+  de->is_done = GNUNET_YES;
+  (void) mark_download_progress (de, size, size, NULL, 0, 0, 0);
+  if ( (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (de->meta)) &&
+       (filename != NULL) )
+    {
+      ade.de = de;
+      ade.prr = de->rr;
+      ade.check_duplicates = GNUNET_NO;      
+      GNUNET_FS_GTK_mmap_and_scan (filename,
+                                  &add_directory_entry,
+                                  &ade);
+    }
+  (void) change_download_colour (de, "green");
+  return de;
+}
+
+
+static struct PublishEntry *
+mark_publish_progress (struct PublishEntry *pe,
+                       uint64_t size,
+                       uint64_t completed)
+{
+  GtkTreeIter iter;
+  GtkTreePath *path;
+  
+  path = gtk_tree_row_reference_get_path (pe->rr);
+  if (TRUE != gtk_tree_model_get_iter (GTK_TREE_MODEL (pe->tab->ts), 
+                                      &iter, path))
+    {
+      GNUNET_break (0);
+      gtk_tree_path_free (path);
+      return pe;
+    }
+  gtk_tree_path_free (path);
+  gtk_tree_store_set (pe->tab->ts, &iter,
+                     3, (guint) ((size > 0) ? (100 * completed / size) : 100) 
/* progress */,
+                     -1);
+  return pe;
+}
+
+
+/**
+ * Move (aka copy) all of the children of 'src_iter' from the 'src_model'
+ * to become children of 'dst_iter' in the 'dst_model'.
+ *
+ * The models are both 'GNUNET_GTK_file_sharing_result_tree_store' models.
+ *
+ * Note that we also need to update the 'struct SearchResult'
+ * and (if it exists) the respective 'struct DownloadEntry'
+ * to refer to the new model.
+ */
+static void
+move_children (GtkTreeModel *src_model,
+              GtkTreeIter *src_iter,
+              GtkTreeModel *dst_model,
+              GtkTreeIter *dst_iter)
+{
+  GtkTreeIter src_child;
+  GtkTreeIter dst_child;
+  GtkTreePath *path;
+  struct GNUNET_CONTAINER_MetaData *meta;
+  struct GNUNET_FS_Uri *uri;
+  guint64 filesize;
+  GdkPixbuf *preview;
+  guint percent_progress;
+  guint percent_availability;
+  gchar *filename;
+  gchar *uri_as_string;
+  gchar *status_colour;
+  struct SearchResult *search_result;
+  gchar *mimetype;
+  guint applicability_rank;
+  guint availability_certainty;
+  gint availability_rank;  
+
+  if (TRUE == gtk_tree_model_iter_children (src_model,
+                                           &src_child,
+                                           src_iter))
+    {
+      do
+       {
+         gtk_tree_model_get (src_model,
+                             &src_child,
+                             0, &meta,
+                             1, &uri,
+                             2, &filesize,
+                             3, &preview,
+                             4, &percent_progress,
+                             5, &percent_availability,
+                             6, &filename,
+                             7, &uri_as_string,
+                             8, &status_colour,
+                             9, &search_result,
+                             10, &mimetype,
+                             11, &applicability_rank,
+                             12, &availability_certainty,
+                             13, &availability_rank,
+                             -1);
+         gtk_tree_store_insert_with_values (GTK_TREE_STORE (dst_model),
+                                            &dst_child,
+                                            dst_iter,
+                                            G_MAXINT,
+                                            0, meta,
+                                            1, uri,
+                                            2, filesize,
+                                            3, preview,
+                                            4, percent_progress,
+                                            5, percent_availability,
+                                            6, filename,
+                                            7, uri_as_string,
+                                            8, status_colour,
+                                            9, search_result,
+                                            10, mimetype,
+                                            11, applicability_rank,
+                                            12, availability_certainty,
+                                            13, availability_rank,
+                                            -1);
+         g_free (filename);
+         g_free (uri_as_string);
+         g_free (status_colour);
+         g_free (mimetype);
+         if (preview != NULL)
+           g_object_unref (preview);
+         gtk_tree_row_reference_free (search_result->rr);
+         path = gtk_tree_model_get_path (dst_model,
+                                         &dst_child);
+         search_result->rr = gtk_tree_row_reference_new (dst_model,
+                                              path);
+         gtk_tree_path_free (path);
+         if (search_result->download != NULL)
+           {
+             search_result->download->ts = GTK_TREE_STORE (dst_model);
+             gtk_tree_row_reference_free (search_result->download->rr);
+             search_result->download->rr = gtk_tree_row_reference_copy 
(search_result->rr);
+           }
+         move_children (src_model,
+                        &src_child,
+                        dst_model,
+                        &dst_child);
+       }
+      while (TRUE == gtk_tree_model_iter_next (src_model,
+                                              &src_child));
+    }
+}
+
+
+/**
+ * Delete the entire given subtree from the model.
+ * Does not free anything inside of the respective
+ * model's fields (since they have been moved).
+ */
+static void
+delete_stale_subtree (GtkTreeModel *model,
+                     GtkTreeIter *iter)
+{
+  GtkTreeIter child;
+
+  while (TRUE == gtk_tree_model_iter_children (model,
+                                              &child,
+                                              iter))
+    delete_stale_subtree (model, &child);
+  gtk_tree_store_remove (GTK_TREE_STORE (model),
+                        iter);
+}
+
+
+/**
+ * Handle the case where an active download lost its 
+ * search parent by moving it to the URI tab.
+ */
+static struct DownloadEntry *
+download_lost_parent (struct DownloadEntry *de,
+                     uint64_t size,
+                     uint64_t completed,
+                     int is_active)
+{
+  GtkTreeIter iter;
+  GtkTreePath *path;
+  struct SearchTab *tab;
+  GtkTreeRowReference *rr_old;
+  GtkTreeModel *tm_old;
+  GtkTreeIter iter_old;
+
+  rr_old = de->rr;
+  de->sr = NULL;
+  tab = GNUNET_GTK_add_to_uri_tab (&iter,
+                                  NULL,
+                                  de->meta,
+                                  de->uri);
+  de->ts = tab->ts;
+  path = gtk_tree_model_get_path (GTK_TREE_MODEL (de->ts),
+                                 &iter);
+  de->rr = gtk_tree_row_reference_new (GTK_TREE_MODEL (de->ts),
+                                      path);
+  gtk_tree_path_free (path);
+  mark_download_progress (de, size, completed,
+                         NULL, 0, 0, 0);
+  tm_old = gtk_tree_row_reference_get_model (rr_old);
+  path = gtk_tree_row_reference_get_path (rr_old);                             
  
+  gtk_tree_row_reference_free (rr_old);
+  gtk_tree_model_get_iter (tm_old,
+                          &iter_old,
+                          path);
+  gtk_tree_path_free (path);
+  move_children (tm_old,
+                &iter_old,
+                GTK_TREE_MODEL (de->ts),
+                &iter);
+  delete_stale_subtree (tm_old,
+                       &iter_old);
+  if (size > completed)
+    {
+      if (is_active)
+       change_download_colour (de, "yellow");
+      else
+       change_download_colour (de, "blue");
+    }
+  else
+    {
+      change_download_colour (de, "green");
+    }
+  return de;
+}
+
+
+/**
+ * Setup a new download entry.
+ *
+ * @param de existing download entry for the download, or NULL
+ * @param pde parent download entry, or NULL
+ * @param sr search result, or NULL
+ * @param dc download context (for stopping)
+ * @param uri the URI
+ * @param meta metadata
+ * @param size total size
+ * @param completed current progress
+ */
+static struct DownloadEntry *
+setup_download (struct DownloadEntry *de,
+               struct DownloadEntry *pde,
+               struct SearchResult *sr,
+               struct GNUNET_FS_DownloadContext *dc,
+               const struct GNUNET_FS_Uri *uri,
+               const struct GNUNET_CONTAINER_MetaData *meta,
+               uint64_t size,
+               uint64_t completed)
+{
+  GtkTreeIter iter;
+  GtkTreePath *path;
+
+  if (de == NULL)
+    {
+      de = GNUNET_malloc (sizeof (struct DownloadEntry));
+      GNUNET_assert (sr->download == NULL);
+      sr->download = de;
+      de->sr = sr;
+      de->dc = dc;
+      de->uri = GNUNET_FS_uri_dup (uri);
+    }
+  de->pde = pde;
+  if ( (meta != NULL) &&
+       (de->meta == NULL) )
+    de->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
+  if (sr != NULL)
+    {
+      de->rr = gtk_tree_row_reference_copy (sr->rr);      
+      de->ts = sr->tab->ts;
+      de->tab = sr->tab;
+    }
+  else if (de->rr == NULL)
+    {
+      de->tab = GNUNET_GTK_add_to_uri_tab (&iter,
+                                          NULL,
+                                          meta,
+                                          uri);
+      de->ts = de->tab->ts;
+      path = gtk_tree_model_get_path (GTK_TREE_MODEL (de->ts),
+                                     &iter);
+      de->rr = gtk_tree_row_reference_new (GTK_TREE_MODEL (de->ts),
+                                          path);
+      gtk_tree_path_free (path);
+    }    
+  path = gtk_tree_row_reference_get_path (de->rr);
+  if (TRUE != gtk_tree_model_get_iter (GTK_TREE_MODEL (de->ts), 
+                                      &iter, path))
+    {
+      GNUNET_break (0);
+      gtk_tree_path_free (path);
+      return de;
+    }
+  gtk_tree_path_free (path);
+  gtk_tree_store_set (de->ts, &iter,
+                     4, (guint) ((size > 0) ? (100 * completed / size) : 100) 
/* progress */,
+                     8, "blue" /* status colour: pending */,
+                     -1);
+  return de;
+}
+
+
+/**
+ * Tell FS to start a download.  Begins by opening the
+ * "save as" window.
+ */
+static void
+start_download (GtkTreeView *tree_view,
+               GtkTreePath *path,
+               GtkTreeViewColumn *column,
+               gpointer user_data)
+{
+  struct SearchTab *tab = user_data;
+  GtkTreeModel *tm;
+  GtkTreeIter iter;
+  struct GNUNET_FS_Uri *uri;
+  struct GNUNET_CONTAINER_MetaData *meta;
+  struct SearchResult *sr;
+  gchar *mime;
+  struct DownloadContext *dlc;
+
+  GNUNET_assert (tab != NULL);
+  tm = gtk_tree_view_get_model (tree_view);
+  if (TRUE != gtk_tree_model_get_iter (tm, &iter, path))
+    {
+      GNUNET_break (0);
+      return;
+    }
+  gtk_tree_model_get (tm, &iter, 
+                     0, &meta,
+                     1, &uri,                
+                     9, &sr,
+                     10, &mime,
+                     -1);
+  dlc = GNUNET_malloc (sizeof (struct DownloadContext));
+  dlc->uri = GNUNET_FS_uri_dup (uri);
+  dlc->mime = (NULL != mime) ? GNUNET_strdup (mime) : NULL;
+  dlc->filename = GNUNET_FS_meta_data_suggest_filename (meta);
+  dlc->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
+  dlc->rr = gtk_tree_row_reference_new (tm, path);
+  dlc->sr = sr->result;
+  dlc->anonymity = -1;
+  GNUNET_GTK_open_download_as_dialog (dlc);
+  g_free (mime);
+}
+
+
+
+/**
+ * Row reference for the current search context menu.
+ */
+static GtkTreeRowReference *current_context_row_reference;
+
+/**
+ * Search tab used for the current search context menu.
+ */
+static struct SearchTab *current_context_search_tab;
+
+/**
+ * Download was selected in the current search context menu.
+ */
+static void
+start_download_ctx_menu (gpointer user_data,
+                        guint unused,
+                        GtkWidget *widget)
+{
+  GtkTreePath *path;
+  GtkTreeView *tv;
+
+  if (current_context_row_reference == NULL)
+    {
+      GNUNET_break (0);
+      return;
+    }
+  path = gtk_tree_row_reference_get_path (current_context_row_reference);
+  gtk_tree_row_reference_free (current_context_row_reference);
+  current_context_row_reference = NULL;
+  tv = GTK_TREE_VIEW (gtk_builder_get_object 
(current_context_search_tab->builder,
+                                             "_search_result_frame"));
+  start_download (tv, path, NULL, current_context_search_tab);
+  gtk_tree_path_free (path);
+  current_context_search_tab = NULL;
+}
+
+
+/**
+ * Download was selected in the current search context menu.
+ */
+static void
+abort_download_ctx_menu (gpointer user_data,
+                        guint unused,
+                        GtkWidget *widget)
+{
+  struct DownloadEntry *de = user_data;
+
+  GNUNET_assert (de->dc != NULL);
+  GNUNET_FS_download_stop (de->dc, 
+                          GNUNET_YES);
+  current_context_search_tab = NULL;
+}
+
+
+/**
+ * Copy current URI to clipboard.
+ */
+static void
+copy_uri_to_clipboard_ctx_menu (gpointer user_data,
+                               guint unused,
+                               GtkWidget *widget)
+{
+  GtkTreePath *path;
+  GtkTreeView *tv;
+  GtkTreeModel *tm;
+  GtkTreeIter iter;
+  struct GNUNET_FS_Uri *uri;
+  char *uris;
+  GtkClipboard *cb;
+
+  if (current_context_row_reference == NULL)
+    {
+      GNUNET_break (0);
+      return;
+    }
+  path = gtk_tree_row_reference_get_path (current_context_row_reference);
+  gtk_tree_row_reference_free (current_context_row_reference);
+  current_context_row_reference = NULL;
+  tv = GTK_TREE_VIEW (gtk_builder_get_object 
(current_context_search_tab->builder,
+                                             "_search_result_frame"));
+  tm = gtk_tree_view_get_model (tv);
+  if (TRUE != gtk_tree_model_get_iter (tm, &iter, path))
+    {
+      GNUNET_break (0);
+      gtk_tree_path_free (path);
+      return;
+    }
+  gtk_tree_model_get (tm, &iter, 
+                     1, &uri, -1);
+  gtk_tree_path_free (path);
+  current_context_search_tab = NULL;
+  if (uri == NULL)
+    {
+      GNUNET_break (0);
+      return;
+    }
+  uris = GNUNET_FS_uri_to_string (uri);
+  cb = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+  gtk_clipboard_set_text (cb,
+                         uris,
+                         -1);
+  gtk_clipboard_store (cb);
+  GNUNET_free (uris);
+}
+
+
+/**
+ * We got a right-click on the search result list. Display the context
+ * menu.
+ */
+static int 
+search_list_on_menu(GtkWidget *widget, 
+                   GdkEvent *event,
+                   gpointer user_data)
+{
+  GdkEventButton *event_button;
+  struct SearchTab *tab = user_data;
+  GtkTreeView *tv;
+  GtkMenu *menu;
+  GtkWidget *child;
+  GtkTreePath *path;
+  GtkTreeModel *tm;
+  GtkTreeIter iter;
+  struct SearchResult *sr;
+ 
+  tv = GTK_TREE_VIEW (widget);
+  if (event->type == GDK_BUTTON_PRESS)
+    {
+      event_button = (GdkEventButton *) event;      
+      if (event_button->button == 3)
+       {
+         current_context_search_tab = tab;
+         if (current_context_row_reference != NULL)
+           {
+             gtk_tree_row_reference_free (current_context_row_reference);
+             current_context_row_reference = NULL;
+           }
+         path = NULL;
+         if (FALSE == gtk_tree_view_get_path_at_pos (tv,
+                                                     event_button->x,
+                                                     event_button->y,
+                                                     &path, NULL, NULL, NULL))
+           {
+             /* nothing selected */
+             current_context_search_tab = NULL;
+             return FALSE;
+           }
+         tm = gtk_tree_view_get_model (tv);
+         gtk_tree_model_get_iter (tm, &iter, path);
+         gtk_tree_model_get (tm, &iter,
+                             9, &sr,
+                             -1);        
+         current_context_row_reference = gtk_tree_row_reference_new (tm,
+                                                                     path);
+         gtk_tree_path_free (path);
+
+         /*
+           FIXME: have additional options, depending on status:
+            - view full meta data (in new window)
+            - copy URI to clipboard
+            - start recursive download
+            - abort active download (!) 
+            => need to know download status before creating menu! 
+         */
+         menu = GTK_MENU (gtk_menu_new ());
+         if (sr->download == NULL)
+           {
+             child = gtk_menu_item_new_with_label (_("_Download"));
+             g_signal_connect (child,
+                               "activate",
+                               G_CALLBACK (start_download_ctx_menu),
+                               NULL);
+             gtk_label_set_use_underline (GTK_LABEL
+                                          (gtk_bin_get_child (GTK_BIN 
(child))), 
+                                          TRUE);
+             gtk_widget_show (child);
+           }
+         else
+           {
+             child = gtk_menu_item_new_with_label (_("_Abort download"));
+             g_signal_connect (child,
+                               "activate",
+                               G_CALLBACK (abort_download_ctx_menu),
+                               sr->download);
+             gtk_label_set_use_underline (GTK_LABEL
+                                          (gtk_bin_get_child (GTK_BIN 
(child))), 
+                                          TRUE);
+             gtk_widget_show (child);
+
+           }
+
+         child = gtk_menu_item_new_with_label (_("_Copy URI to Clipboard"));
+         g_signal_connect (child,
+                           "activate",
+                           G_CALLBACK (copy_uri_to_clipboard_ctx_menu),
+                           NULL);
+         gtk_label_set_use_underline (GTK_LABEL
+                                      (gtk_bin_get_child (GTK_BIN (child))), 
+                                      TRUE);
+         gtk_widget_show (child);
+
+         
+         
+         gtk_menu_shell_append (GTK_MENU_SHELL (menu), child);
+         gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
+                         event_button->button,
+                         event_button->time);      
+       }
+    }
+  return FALSE;
+}
+
+
+/**
+ * Selected row has changed, update preview and metadata
+ * areas.
+ */
+static void
+update_meta_data_views (GtkTreeView *tv,
+                       gpointer user_data)
+{
+  struct SearchTab *tab = user_data;
+  GtkImage *image;
+  GtkListStore *ms;
+  GtkTreeSelection *sel;
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+  struct GNUNET_CONTAINER_MetaData *meta;
+  GdkPixbuf *pixbuf;
+
+  GNUNET_assert (tab->query_txt != NULL);
+  image = GTK_IMAGE (GNUNET_FS_GTK_get_main_window_object 
("GNUNET_GTK_main_window_preview_image"));
+  ms = GTK_LIST_STORE (GNUNET_FS_GTK_get_main_window_object 
("GNUNET_GTK_meta_data_list_store"));
+
+  sel = gtk_tree_view_get_selection (tv);
+  gtk_list_store_clear (ms);
+  if (TRUE != gtk_tree_selection_get_selected (sel,
+                                              &model,
+                                              &iter))
+    {
+      gtk_image_clear (image);
+      return;
+    }
+  meta = NULL;
+  pixbuf = NULL;
+  gtk_tree_model_get (model,
+                     &iter,
+                     0, &meta,
+                     3, &pixbuf,                     
+                     -1);
+  if (pixbuf != NULL)
+    {
+      gtk_image_set_from_pixbuf (image, pixbuf);
+      g_object_unref (G_OBJECT (pixbuf));
+    }
+  if (meta != NULL)
+    {
+      GNUNET_CONTAINER_meta_data_iterate (meta,
+                                         
&GNUNET_FS_GTK_add_meta_data_to_list_store,
+                                         ms);
+    }
+}
+
+
+/**
+ * Update the label for a search
+ */
+static void
+update_search_label (struct SearchTab *tab)
+{
+  char *name;
+ 
+  while (tab->parent != NULL)
+    tab = tab->parent->tab;
+  if (tab->num_results > 0)
+    GNUNET_asprintf (&name,
+                    "%.*s%s (%u)",
+                    20,
+                    tab->query_txt,
+                    strlen (tab->query_txt) > 20 ? "..." : "",
+                    tab->num_results);
+  else
+    GNUNET_asprintf (&name,
+                    "%.*s%s",
+                    20,
+                    tab->query_txt,
+                    strlen (tab->query_txt) > 20 ? "..." : "");
+  gtk_label_set_text (tab->label, name);
+  GNUNET_free (name);
+}
+
+
+/**
+ * Close a search tab and free associated state.
+ */
+static void
+close_search_tab (struct SearchTab *tab)
+{
+  GtkNotebook *notebook;
+  int index;
+  int i;
+  
+  if (tab->parent != NULL)
+    {
+      /* not a top-level search, do not close tab here! */
+      GNUNET_free (tab);
+      return;
+    }
+
+  notebook = GTK_NOTEBOOK (GNUNET_FS_GTK_get_main_window_object 
("GNUNET_GTK_main_window_notebook"));
+  index = -1;
+  for (i = gtk_notebook_get_n_pages (notebook) - 1; i >= 0; i--)
+    if (tab->frame == gtk_notebook_get_nth_page (notebook, i))
+      index = i;
+  gtk_notebook_remove_page (notebook, index);
+  g_object_unref (tab->builder);
+  GNUNET_free (tab->query_txt);
+  GNUNET_CONTAINER_DLL_remove (search_tab_head,
+                              search_tab_tail,
+                              tab);
+  GNUNET_free (tab);
+}
+
+
+/**
+ * Close a publish tab and free associated state.
+ */
+static struct PublishEntry *
+handle_publish_completed (struct PublishEntry *ent,
+                         const struct GNUNET_FS_Uri *uri)
+{
+  ent->uri = GNUNET_FS_uri_dup (uri);
+  return change_publish_colour (ent,
+                               "green");
+}
+
+
+
+/**
+ * Handle error.
+ */
+static struct PublishEntry *
+handle_publish_error (struct PublishEntry *ent,
+                     const char *emsg)
+{
+  GNUNET_break (0);
+  return change_publish_colour (ent,
+                               "red");
+}
+
+
+/**
+ * Close a publish tab and free associated state.
+ */
+static void
+close_publish_tab (struct PublishEntry *ent)
+{
+  struct PublishTab *tab;
+  GtkNotebook *notebook;
+  int index;
+  int i;
+
+  if (ent == NULL)
+    {
+      GNUNET_break (0);
+      return;
+    }
+  gtk_tree_row_reference_free (ent->rr);
+  if (GNUNET_YES != ent->is_top)
+    {
+      GNUNET_free (ent);
+      return;
+    }
+  tab = ent->tab;
+  if (ent->uri != NULL)
+    GNUNET_FS_uri_destroy (ent->uri);
+  GNUNET_free (ent);
+  notebook = GTK_NOTEBOOK (GNUNET_FS_GTK_get_main_window_object 
("GNUNET_GTK_main_window_notebook"));
+  index = -1;
+  for (i = gtk_notebook_get_n_pages (notebook) - 1; i >= 0; i--)
+    if (tab->frame == gtk_notebook_get_nth_page (notebook, i))
+      index = i;
+  gtk_notebook_remove_page (notebook, index);
+  g_object_unref (tab->builder);
+  GNUNET_CONTAINER_DLL_remove (publish_tab_head,
+                              publish_tab_tail,
+                              tab);
+  GNUNET_free (tab);
+}
+
+
+/**
+ * Tell FS to stop a search.
+ */
+static void
+stop_search (GtkButton *button,
+            gpointer user_data)
+{
+  struct SearchTab *tab = user_data;
+  if (tab->sc != NULL)
+    {
+      GNUNET_FS_search_stop (tab->sc);
+      tab->sc = NULL;
+    }
+}
+
+
+/**
+ * Stop completed downloads (or those that failed).  Should
+ * iterate over the underlying tree store and stop all
+ * completed entries.  Furthermore, if the resulting tree
+ * store is empty and has no search associated with it, 
+ * the tab should be closed.
+ */
+static void
+clear_downloads (GtkButton *button,
+                gpointer user_data)
+{
+  struct SearchTab *tab = user_data;
+  struct SearchResult *sr;
+  GtkTreeModel *tm;
+  GtkTreeIter iter;
+
+  tm = GTK_TREE_MODEL (tab->ts);
+  if (TRUE != gtk_tree_model_get_iter_first (tm, &iter))
+    return;
+  do
+    {
+      gtk_tree_model_get (tm, &iter,
+                         9, &sr,
+                         -1);
+      if ( (sr->download != NULL) &&
+          (sr->download->is_done == GNUNET_YES) )
+       GNUNET_FS_download_stop (sr->download->dc, 
+                                GNUNET_YES);   
+    }
+  while (TRUE == gtk_tree_model_iter_next (tm, &iter));
+}
+
+
+
+/**
+ * Tell FS to pause a search.
+ */
+static void
+pause_search (GtkButton *button,
+             gpointer user_data)
+{
+  struct SearchTab *tab = user_data;
+  if (tab->sc != NULL)
+    {
+      GNUNET_FS_search_pause (tab->sc);
+      gtk_widget_show (tab->play_button);
+      gtk_widget_hide (tab->pause_button);
+    }
+}
+
+
+/**
+ * Tell FS to resume a search.
+ */
+static void
+continue_search (GtkButton *button,
+              gpointer user_data)
+{
+  struct SearchTab *tab = user_data;
+  if (tab->sc != NULL)
+    {
+      GNUNET_FS_search_continue (tab->sc);
+      gtk_widget_show (tab->pause_button);
+      gtk_widget_hide (tab->play_button);
+    }
+}
+
+
+/**
+ * Setup a new search tab.
+ *
+ * @param sc context with FS for the search
+ * @param query the query
+ * @param anonymity anonymity level
+ */
+static struct SearchTab *
+setup_search (struct GNUNET_FS_SearchContext *sc,
+             const struct GNUNET_FS_Uri *query)
+{
+  struct SearchTab *tab;
+  GtkTreeView *tv;
+  GtkNotebook *notebook;
+  GtkWindow *sf;
+  gint pages;
+
+  tab = GNUNET_malloc (sizeof (struct SearchTab));
+  GNUNET_CONTAINER_DLL_insert (search_tab_head,
+                              search_tab_tail,
+                              tab);
+  tab->sc = sc;
+  if (query == NULL)
+    {
+      tab->query_txt = GNUNET_strdup ("*");
+    }
+  else
+    {
+      if (GNUNET_FS_uri_test_ksk (query))
+       tab->query_txt = GNUNET_FS_uri_ksk_to_string_fancy (query);
+      else
+       tab->query_txt = GNUNET_FS_uri_to_string (query);
+    }
+  tab->builder = GNUNET_GTK_get_new_builder ("search_tab.glade");
+  tab->ts = GTK_TREE_STORE (gtk_builder_get_object (tab->builder,
+                                                   
"GNUNET_GTK_file_sharing_result_tree_store"));
+  /* load frame */
+  sf = GTK_WINDOW (gtk_builder_get_object (tab->builder,
+                                          "_search_result_frame_window"));
+  tab->frame = gtk_bin_get_child (GTK_BIN (sf));
+  gtk_widget_ref (tab->frame);
+  gtk_container_remove (GTK_CONTAINER (sf), tab->frame);
+  gtk_widget_destroy (GTK_WIDGET (sf));
+
+  /* load tab_label */
+  sf = GTK_WINDOW (gtk_builder_get_object (tab->builder,
+                                          "_search_result_label_window"));
+  tab->tab_label = gtk_bin_get_child (GTK_BIN (sf));
+  gtk_widget_ref (tab->tab_label);
+  gtk_container_remove (GTK_CONTAINER (sf), tab->tab_label);
+  gtk_widget_destroy (GTK_WIDGET (sf));
+  
+  /* get refs to widgets */
+  tab->label = GTK_LABEL (gtk_builder_get_object (tab->builder,
+                                                 
"_search_result_label_window_label"));
+  
+  tab->close_button = GTK_WIDGET (gtk_builder_get_object (tab->builder,
+                                                         
"_search_result_label_close_button"));
+  g_signal_connect(G_OBJECT(tab->close_button), "clicked", 
+                  G_CALLBACK(stop_search), tab);
+  tab->clear_button = GTK_WIDGET (gtk_builder_get_object (tab->builder,
+                                                         
"_search_result_label_clear_button"));
+  g_signal_connect(G_OBJECT(tab->clear_button), "clicked", 
+                  G_CALLBACK(clear_downloads), tab);
+  tab->play_button = GTK_WIDGET (gtk_builder_get_object (tab->builder,
+                                                        
"_search_result_label_play_button"));
+  g_signal_connect(G_OBJECT(tab->play_button), "clicked", 
+                  G_CALLBACK(continue_search), tab);
+  tab->pause_button = GTK_WIDGET (gtk_builder_get_object (tab->builder,
+                                                         
"_search_result_label_pause_button"));
+  g_signal_connect(G_OBJECT(tab->pause_button), "clicked", 
+                  G_CALLBACK(pause_search), tab);
+  /* patch text */
+  update_search_label (tab);
+
+  /* add signal handlers */
+  tv = GTK_TREE_VIEW (gtk_builder_get_object (tab->builder,
+                                             "_search_result_frame"));
+  g_signal_connect(G_OBJECT(tv), "row-activated", 
+                  G_CALLBACK(start_download), tab);
+  g_signal_connect(G_OBJECT(tv), "cursor-changed", 
+                  G_CALLBACK(update_meta_data_views), tab);
+  g_signal_connect (G_OBJECT(tv), 
+                   "button_press_event",
+                   G_CALLBACK(search_list_on_menu), 
+                   tab);
+  
+
+  /* make visible */
+  notebook = GTK_NOTEBOOK (GNUNET_FS_GTK_get_main_window_object 
("GNUNET_GTK_main_window_notebook"));
+  pages = gtk_notebook_get_n_pages (notebook);
+  gtk_notebook_insert_page (notebook, 
+                           tab->frame,
+                           tab->tab_label,
+                           pages - 1);
+  gtk_notebook_set_current_page (notebook, 
+                                pages - 1);
+  gtk_widget_show (GTK_WIDGET (notebook));   
+  return tab;
+}
+
+
+/**
+ * Setup an inner search.
+ *
+ * @param sc context with FS for the search
+ * @param parent parent search tab
+ * @param anonymity anonymity level
+ */
+static struct SearchTab *
+setup_inner_search (struct GNUNET_FS_SearchContext *sc,
+                   struct SearchResult *parent)
+{
+  struct SearchTab *ret;
+
+  ret = GNUNET_malloc (sizeof (struct SearchTab));
+  ret->parent = parent;
+  ret->sc = sc;
+  ret->query_txt = parent->tab->query_txt;
+  ret->builder = parent->tab->builder;
+  ret->frame = parent->tab->frame;
+  ret->tab_label = parent->tab->tab_label;
+  ret->close_button = parent->tab->close_button;
+  ret->clear_button = parent->tab->clear_button;
+  ret->play_button = parent->tab->play_button;
+  ret->label = parent->tab->label;
+
+  return ret;
+}
+
+
+
+/**
+ * Add a search result to the given search tab.
+ *
+ * @param tab search tab to extend
+ * @param iter set to position where search result is added
+ * @param parent_rr reference to parent entry in search tab
+ * @param uri uri to add
+ * @param meta metadata of the entry
+ * @param result associated FS search result (can be NULL)
+ * @param applicability_rank how relevant is the result
+ * @return entry for the search result
+ */
+struct SearchResult *
+GNUNET_GTK_add_search_result (struct SearchTab *tab,
+                             GtkTreeIter *iter,
+                             GtkTreeRowReference *parent_rr,
+                             const struct GNUNET_FS_Uri *uri,
+                             const struct GNUNET_CONTAINER_MetaData *meta,
+                             struct GNUNET_FS_SearchResult *result,
+                             uint32_t applicability_rank)
+{
+  struct SearchResult *sr;
+  GtkTreePath *tp;
+  const char *status_colour;
+  char *desc;
+  char *mime;
+  char *uris;
+  GdkPixbuf *pixbuf;
+  GtkTreeIter *pitr;
+  GtkTreeIter pmem;
+  GtkTreePath *path;
+  GtkTreeModel *tm;  
+  GtkTreeStore *ts;
+  uint64_t fsize;
+
+  if ( (uri != NULL) &&
+       (!GNUNET_FS_uri_test_loc (uri)) &&
+       (!GNUNET_FS_uri_test_chk (uri)) )
+    {
+      fsize = 0;
+      mime = GNUNET_strdup ("GNUnet namespace");
+      status_colour = "lightgreen";
+    }      
+  else if (uri != NULL)
+    {
+      fsize = GNUNET_FS_uri_chk_get_file_size (uri);
+      mime = GNUNET_CONTAINER_meta_data_get_first_by_types (meta,
+                                                           
EXTRACTOR_METATYPE_MIMETYPE,
+                                                           
EXTRACTOR_METATYPE_FORMAT,
+                                                           -1);
+      status_colour = "white";
+    }
+  else
+    {
+      fsize = 0;
+      status_colour = "gray";
+      mime = NULL;
+    }
+  desc = GNUNET_CONTAINER_meta_data_get_first_by_types (meta,
+                                                       
EXTRACTOR_METATYPE_PACKAGE_NAME,
+                                                       
EXTRACTOR_METATYPE_TITLE,
+                                                       
EXTRACTOR_METATYPE_BOOK_TITLE,
+                                                       
EXTRACTOR_METATYPE_FILENAME,
+                                                       
EXTRACTOR_METATYPE_DESCRIPTION,
+                                                       
EXTRACTOR_METATYPE_SUMMARY,
+                                                       
EXTRACTOR_METATYPE_ALBUM,
+                                                       
EXTRACTOR_METATYPE_COMMENT,
+                                                       
EXTRACTOR_METATYPE_SUBJECT,
+                                                       
EXTRACTOR_METATYPE_KEYWORDS,
+                                                       -1);
+  if (desc == NULL)
+    desc = GNUNET_strdup (_("no description supplied"));
+  if (uri == NULL)
+    uris = GNUNET_strdup (_("no URI"));
+  else
+    uris = GNUNET_FS_uri_to_string (uri);
+
+  pixbuf = GNUNET_FS_GTK_get_thumbnail_from_meta_data (meta);
+  sr = GNUNET_malloc (sizeof (struct SearchResult));
+  sr->result = result;
+  sr->tab = tab;
+  if (parent_rr != NULL)
+    {
+      /* get piter from parent */
+      path = gtk_tree_row_reference_get_path (parent_rr);
+      tm = gtk_tree_row_reference_get_model (parent_rr);
+      if (TRUE != gtk_tree_model_get_iter (GTK_TREE_MODEL (tm), 
+                                          &pmem, path))
+       {
+         GNUNET_break (0);
+         gtk_tree_path_free (path);
+         /* desperate measure: make top-level entry */
+         pitr = NULL;
+       }
+      else
+       {
+         pitr = &pmem;
+       }
+      ts = GTK_TREE_STORE (tm);
+    }
+  else
+    {
+      /* top-level result */
+      pitr = NULL;
+      ts = tab->ts;
+    }
+  gtk_tree_store_insert_with_values (ts,
+                                    iter,
+                                    pitr,
+                                    G_MAXINT,
+                                    0, GNUNET_CONTAINER_meta_data_duplicate 
(meta),
+                                    1, (uri == NULL) ? NULL : 
GNUNET_FS_uri_dup (uri),
+                                    2, (uri == NULL) ? 0 : fsize,
+                                    3, pixbuf /* preview */,
+                                    4, 0 /* percent progress */,
+                                    5, 0 /* percent availability */,
+                                    6, desc /* filename/description */,
+                                    7, uris,
+                                    8, status_colour,
+                                    9, sr,
+                                    10, mime,
+                                    11, applicability_rank,
+                                    12, 0 /* avail-cert */,
+                                    13, 0 /* avail-rank */,
+                                    -1);
+  if (tab != NULL)
+    {
+      while (tab->parent != NULL)
+       tab = tab->parent->tab;
+      tab->num_results++;
+    }
+  if (pixbuf != NULL)
+    g_object_unref (pixbuf);
+  GNUNET_free (uris);
+  GNUNET_free (desc);
+  GNUNET_free_non_null (mime);
+  tp = gtk_tree_model_get_path (GTK_TREE_MODEL (ts),
+                               iter);
+  sr->rr = gtk_tree_row_reference_new (GTK_TREE_MODEL (ts),
+                                      tp);
+  gtk_tree_path_free (tp);
+  return sr;
+}
+
+
+static struct SearchResult *
+process_search_result (void *cls,
+                      struct SearchResult *parent,
+                      const struct GNUNET_FS_Uri *uri,
+                      const struct GNUNET_CONTAINER_MetaData *meta,
+                      struct GNUNET_FS_SearchResult *result,
+                      uint32_t applicability_rank)
+{
+  struct SearchTab *tab = cls;  
+  struct SearchResult *sr;
+  GtkTreeIter iter;
+
+  sr = GNUNET_GTK_add_search_result (tab, &iter, 
+                                    (parent != NULL) ? parent->rr : NULL, 
+                                    uri,
+                                    meta, result, applicability_rank);
+  update_search_label (tab);
+  return sr;
+}
+
+
+/**
+ * Setup a new top-level entry in the URI tab.  If necessary, create
+ * the URI tab first.
+ *
+ * @param iter set to the new entry
+ * @param srp set to search result 
+ * @param meta metadata for the new entry
+ * @param uri URI for the new entry
+ * @return NULL on error, otherwise tree store matching iter
+ */
+struct SearchTab *
+GNUNET_GTK_add_to_uri_tab (GtkTreeIter *iter,
+                          struct SearchResult **srp,
+                          const struct GNUNET_CONTAINER_MetaData *meta,
+                          const struct GNUNET_FS_Uri *uri)
+{
+  struct SearchTab *utab;
+  struct SearchResult *sr;
+  GtkNotebook *notebook;
+  gint page;
+
+  utab = search_tab_head;
+  while (utab != NULL)
+    {
+      if (utab->sc == NULL)
+       break;
+      utab = utab->next;
+    }
+  if (utab == NULL)
+    {
+      utab = setup_search (NULL, NULL);
+      gtk_widget_set_visible (utab->close_button,
+                             FALSE);
+      gtk_widget_set_visible (utab->pause_button,
+                             FALSE);
+    }
+  else
+    {
+      /* make 'utab' the current page */
+      notebook = GTK_NOTEBOOK (GNUNET_FS_GTK_get_main_window_object 
("GNUNET_GTK_main_window_notebook"));
+      for (page=0;page<gtk_notebook_get_n_pages (notebook);page++)
+       if (utab->frame ==
+           gtk_notebook_get_nth_page (notebook,
+                                      page))
+         {
+           gtk_notebook_set_current_page (notebook,
+                                          page);
+           break;
+         }
+    }
+  sr = GNUNET_GTK_add_search_result (utab, iter,
+                                    NULL, uri, meta,
+                                    NULL, 0);
+  
+  if (NULL != srp)
+    *srp = sr;
+  return utab;
+}
+
+
+static struct SearchTab *
+handle_search_error (struct SearchTab *sr,
+                    const char *emsg)
+{
+  /* FIXME: implement error handler */
+  GNUNET_break (0);
+  return sr;
+}
+
+
+static struct SearchResult *
+update_search_result (struct SearchResult *sr,
+                     const struct GNUNET_CONTAINER_MetaData *meta,
+                     int32_t availability_rank,
+                     uint32_t availability_certainty,
+                     uint32_t applicability_rank)
+{
+  GtkTreeIter iter;
+  struct GNUNET_CONTAINER_MetaData *ometa;
+  GtkTreeView *tv;
+  GtkTreePath *tp;
+  GtkTreeStore *ts;
+  GtkTreeModel *tm;
+  char *desc;
+  char *mime;
+  GdkPixbuf *pixbuf;
+  guint percent_avail;
+  GtkNotebook *notebook;
+  gint page;
+
+  if (sr == NULL)
+    return NULL;
+  desc = GNUNET_CONTAINER_meta_data_get_first_by_types (meta,
+                                                       
EXTRACTOR_METATYPE_PACKAGE_NAME,
+                                                       
EXTRACTOR_METATYPE_TITLE,
+                                                       
EXTRACTOR_METATYPE_BOOK_TITLE,
+                                                       
EXTRACTOR_METATYPE_FILENAME,
+                                                       
EXTRACTOR_METATYPE_DESCRIPTION,
+                                                       
EXTRACTOR_METATYPE_SUMMARY,
+                                                       
EXTRACTOR_METATYPE_ALBUM,
+                                                       
EXTRACTOR_METATYPE_COMMENT,
+                                                       
EXTRACTOR_METATYPE_SUBJECT,
+                                                       
EXTRACTOR_METATYPE_KEYWORDS,
+                                                       -1);
+  if (desc == NULL)
+    desc = GNUNET_strdup (_("no description supplied"));
+  mime = GNUNET_CONTAINER_meta_data_get_first_by_types (meta,
+                                                       
EXTRACTOR_METATYPE_MIMETYPE,
+                                                       
EXTRACTOR_METATYPE_FORMAT,
+                                                       -1);
+  pixbuf = GNUNET_FS_GTK_get_thumbnail_from_meta_data (meta);
+  tp = gtk_tree_row_reference_get_path (sr->rr);
+  tm = gtk_tree_row_reference_get_model (sr->rr);
+  ts = GTK_TREE_STORE (tm);  
+  gtk_tree_model_get_iter (tm, &iter, tp);
+  gtk_tree_path_free (tp);
+  gtk_tree_model_get (tm,
+                     &iter,
+                     0, &ometa,
+                     -1);
+  if (meta != NULL)
+    GNUNET_CONTAINER_meta_data_destroy (ometa);
+  if (availability_certainty > 0)
+    percent_avail = (availability_certainty + availability_rank) * 50 / 
availability_certainty;
+  else
+    percent_avail = 0;
+  gtk_tree_store_set (ts,
+                     &iter,
+                     0, GNUNET_CONTAINER_meta_data_duplicate (meta),
+                     3, pixbuf /* preview */,
+                     5, (guint) percent_avail /* percent availability */,
+                     6, desc /* filename/description */,
+                     10, mime,
+                     11, (guint) applicability_rank,
+                     12, (guint) availability_certainty,
+                     13, (gint) availability_rank,
+                     -1);
+  if (pixbuf != NULL)
+    g_object_unref (pixbuf);
+  GNUNET_free (desc);
+  GNUNET_free_non_null (mime);
+
+  notebook = GTK_NOTEBOOK (GNUNET_FS_GTK_get_main_window_object 
("GNUNET_GTK_main_window_notebook"));
+  page = gtk_notebook_get_current_page (notebook);
+  if (gtk_notebook_get_nth_page (notebook, 
+                                page) == sr->tab->frame)
+    {
+      tv = GTK_TREE_VIEW (gtk_builder_get_object (sr->tab->builder,
+                                                 "_search_result_frame"));
+
+      update_meta_data_views (tv,
+                             sr->tab);
+    }
+  return sr;
+}
+
+
+static void
+free_search_result (struct SearchResult *sr)
+{
+  GtkTreePath *tp;
+  GtkTreeModel *tm;
+  GtkTreeIter iter;
+  struct GNUNET_FS_Uri *uri;
+  struct GNUNET_CONTAINER_MetaData *meta;
+
+  if (sr == NULL)
+    {
+      GNUNET_break (0);
+      return;
+    }
+  tp = gtk_tree_row_reference_get_path (sr->rr);
+  tm = gtk_tree_row_reference_get_model (sr->rr);
+  gtk_tree_model_get_iter (tm, &iter, tp);
+  gtk_tree_path_free (tp);
+  gtk_tree_model_get (tm,
+                     &iter,
+                     0, &meta,
+                     1, &uri,
+                     -1);
+  if (uri != NULL)
+    GNUNET_FS_uri_destroy (uri);
+  if (meta != NULL)
+    GNUNET_CONTAINER_meta_data_destroy (meta);
+  gtk_tree_row_reference_free (sr->rr);
+  gtk_tree_store_remove (GTK_TREE_STORE (tm),
+                        &iter);
+  GNUNET_free (sr);
+}
+
+
+/**
+ * Tell FS to stop publishing.
+ */
+static void
+stop_publishing (GtkButton *button,
+                gpointer user_data)
+{
+  struct PublishTab *tab = user_data;
+  struct GNUNET_FS_PublishContext *pc;
+
+  if (NULL != (pc = tab->pc))
+    {
+      tab->pc = NULL;
+      GNUNET_FS_publish_stop (pc);
+    }
+}
+
+
+static struct PublishEntry *
+setup_publish (struct GNUNET_FS_PublishContext *pc,
+              const char *fn,
+              uint64_t fsize,
+              struct PublishEntry *parent)
+{
+  struct PublishTab *tab;
+  struct PublishEntry *ent;
+  GtkTreeIter *pitrptr;
+  GtkTreeIter iter;
+  GtkTreeIter piter;
+  GtkTreePath *path;
+  GtkWindow *df;
+  GtkWidget *tab_label;
+  GtkLabel *fn_label;
+  GtkWidget *close_button;
+  GtkNotebook *notebook;
+  gint pages;
+  char *size_fancy;
+
+  if (NULL == parent)
+    {
+      /* create new tab */
+      tab = GNUNET_malloc (sizeof (struct PublishTab));
+      tab->pc = pc;
+      GNUNET_CONTAINER_DLL_insert (publish_tab_head,
+                                  publish_tab_tail,
+                                  tab);
+      tab->builder = GNUNET_GTK_get_new_builder ("publish_tab.glade");
+      df = GTK_WINDOW (gtk_builder_get_object (tab->builder,
+                                              "_publish_frame_window"));
+      tab->frame = gtk_bin_get_child (GTK_BIN (df));
+      gtk_widget_ref (tab->frame);
+      gtk_container_remove (GTK_CONTAINER (df), tab->frame);
+      gtk_widget_destroy (GTK_WIDGET (df));
+      
+      /* load tab_label */
+      df = GTK_WINDOW (gtk_builder_get_object (tab->builder,
+                                              "_publish_label_window"));
+      tab_label = gtk_bin_get_child (GTK_BIN (df));
+      gtk_widget_ref (tab_label);
+      gtk_container_remove (GTK_CONTAINER (df), tab_label);
+      gtk_widget_destroy (GTK_WIDGET (df));
+      
+      /* get refs to widgets */
+      fn_label = GTK_LABEL (gtk_builder_get_object (tab->builder,
+                                                   
"_publish_label_window_label")); 
+      gtk_label_set_text (fn_label, fn);
+      close_button = GTK_WIDGET (gtk_builder_get_object (tab->builder,
+                                                        
"_publish_label_close_button"));
+      g_signal_connect(G_OBJECT(close_button), "clicked", 
+                      G_CALLBACK(stop_publishing), tab);
+      /* make visible */
+      notebook = GTK_NOTEBOOK (GNUNET_FS_GTK_get_main_window_object 
("GNUNET_GTK_main_window_notebook"));
+      pages = gtk_notebook_get_n_pages (notebook);
+      gtk_notebook_insert_page (notebook, 
+                               tab->frame,
+                               tab_label,
+                               pages - 1);
+      gtk_widget_show (GTK_WIDGET (notebook));
+      tab->ts = GTK_TREE_STORE (gtk_builder_get_object (tab->builder,
+                                                       
"_publish_frame_tree_store")); 
+      pitrptr = NULL;
+    }
+  else
+    {
+      /* create new iter from parent */
+      tab = parent->tab;
+      path = gtk_tree_row_reference_get_path (parent->rr);
+      if (TRUE != gtk_tree_model_get_iter (GTK_TREE_MODEL (tab->ts), 
+                                          &piter, path))
+       {
+         GNUNET_break (0);
+         return NULL;
+       }
+      pitrptr = &piter;
+    }
+  size_fancy = GNUNET_STRINGS_byte_size_fancy (fsize);
+  gtk_tree_store_insert_with_values (tab->ts,
+                                    &iter,
+                                    pitrptr,
+                                    G_MAXINT,
+                                    0, fn,
+                                    1, size_fancy,
+                                    2, "white",
+                                    3, (guint) 0 /* progress */,
+                                    -1);
+  GNUNET_free (size_fancy);
+  ent = GNUNET_malloc (sizeof (struct PublishEntry));
+  ent->is_top = (parent == NULL) ? GNUNET_YES : GNUNET_NO;
+  ent->tab = tab;
+  path = gtk_tree_model_get_path (GTK_TREE_MODEL (tab->ts), &iter);
+  ent->rr = gtk_tree_row_reference_new (GTK_TREE_MODEL (tab->ts),
+                                       path);
+  gtk_tree_path_free (path);
+  ent->pc = pc;
+  return ent;
+}
+
+
+/**
+ * Notification of FS to a client about the progress of an 
+ * operation.  Callbacks of this type will be used for uploads,
+ * downloads and searches.  Some of the arguments depend a bit 
+ * in their meaning on the context in which the callback is used.
+ *
+ * @param cls closure
+ * @param info details about the event, specifying the event type
+ *        and various bits about the event
+ * @return client-context (for the next progress call
+ *         for this operation; should be set to NULL for
+ *         SUSPEND and STOPPED events).  The value returned
+ *         will be passed to future callbacks in the respective
+ *         field in the GNUNET_FS_ProgressInfo struct.
+ */
+void* 
+GNUNET_GTK_fs_event_handler (void *cls,
+                            const struct GNUNET_FS_ProgressInfo *info)
+{
+  void *ret;  
+
+  switch (info->status)
+    {
+    case GNUNET_FS_STATUS_PUBLISH_START: 
+      return setup_publish (info->value.publish.pc,
+                           info->value.publish.filename,
+                           info->value.publish.size,
+                           info->value.publish.pctx);
+    case GNUNET_FS_STATUS_PUBLISH_RESUME:
+      ret = setup_publish (info->value.publish.pc,
+                          info->value.publish.filename,
+                          info->value.publish.size,
+                          info->value.publish.pctx);
+      if (ret == NULL)
+       return ret;
+      if (info->value.publish.specifics.resume.message != NULL)
+       {
+         ret = handle_publish_error (ret,
+                                     
info->value.publish.specifics.resume.message);
+       }
+      else if (info->value.publish.specifics.resume.chk_uri != NULL)
+       {
+         ret = handle_publish_completed (ret,
+                                         
info->value.publish.specifics.resume.chk_uri);
+       }
+      return ret;
+    case GNUNET_FS_STATUS_PUBLISH_SUSPEND:
+      close_publish_tab (info->value.publish.cctx);
+      return NULL;
+    case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
+      return mark_publish_progress (info->value.publish.cctx,
+                                   info->value.publish.size,
+                                   info->value.publish.completed);
+    case GNUNET_FS_STATUS_PUBLISH_ERROR: 
+      return handle_publish_error (info->value.publish.cctx,
+                                  info->value.publish.specifics.error.message);
+    case GNUNET_FS_STATUS_PUBLISH_COMPLETED: 
+      return handle_publish_completed (info->value.publish.cctx,
+                                      
info->value.publish.specifics.completed.chk_uri);
+    case GNUNET_FS_STATUS_PUBLISH_STOPPED: 
+      close_publish_tab (info->value.publish.cctx);
+      return NULL;
+    case GNUNET_FS_STATUS_DOWNLOAD_START: 
+      return setup_download (info->value.download.cctx,
+                            info->value.download.pctx,
+                            info->value.download.sctx,
+                            info->value.download.dc,
+                            info->value.download.uri,
+                            info->value.download.specifics.start.meta,
+                            info->value.download.size,
+                            info->value.download.completed);
+    case GNUNET_FS_STATUS_DOWNLOAD_RESUME:
+      ret = setup_download (info->value.download.cctx,
+                           info->value.download.pctx,
+                           info->value.download.sctx,
+                           info->value.download.dc,
+                           info->value.download.uri,
+                           info->value.download.specifics.resume.meta,
+                           info->value.download.size,
+                           info->value.download.completed);
+      if (info->value.download.specifics.resume.message != NULL)
+       {
+         ret = mark_download_error (ret,
+                                    
info->value.download.specifics.resume.message);
+       }
+      return ret;
+    case GNUNET_FS_STATUS_DOWNLOAD_SUSPEND: 
+      stop_download (info->value.download.cctx, GNUNET_YES);
+      return NULL;
+    case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:       
+      return mark_download_progress (info->value.download.cctx,
+                                    info->value.download.size,
+                                    info->value.download.completed,
+                                    
info->value.download.specifics.progress.data,
+                                    
info->value.download.specifics.progress.offset,
+                                    
info->value.download.specifics.progress.data_len,
+                                    
info->value.download.specifics.progress.depth);
+    case GNUNET_FS_STATUS_DOWNLOAD_ERROR: 
+      return mark_download_error (info->value.download.cctx,
+                                 info->value.download.specifics.error.message);
+    case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED: 
+      return mark_download_completed (info->value.download.cctx,
+                                     info->value.download.size,
+                                     info->value.download.filename);
+    case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
+      stop_download (info->value.download.cctx, GNUNET_NO);
+      return NULL;
+    case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE: 
+      return change_download_colour (info->value.download.cctx,
+                                    "yellow");
+    case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE: 
+      return change_download_colour (info->value.download.cctx,
+                                    "blue");
+    case GNUNET_FS_STATUS_DOWNLOAD_LOST_PARENT: 
+      return download_lost_parent (info->value.download.cctx,
+                                  info->value.download.size,
+                                  info->value.download.completed,
+                                  info->value.download.is_active);
+    case GNUNET_FS_STATUS_SEARCH_START: 
+      if (info->value.search.pctx != NULL)
+       return setup_inner_search (info->value.search.sc,
+                                  info->value.search.pctx);
+      return setup_search (info->value.search.sc,
+                          info->value.search.query);
+    case GNUNET_FS_STATUS_SEARCH_RESUME: 
+      ret = setup_search (info->value.search.sc,
+                         info->value.search.query);
+      if (info->value.search.specifics.resume.message)
+       ret = handle_search_error (ret, 
+                                  info->value.search.specifics.resume.message);
+      return ret;
+    case GNUNET_FS_STATUS_SEARCH_RESUME_RESULT: 
+      ret = process_search_result (info->value.search.cctx,
+                                  info->value.search.pctx,
+                                  
info->value.search.specifics.resume_result.uri,
+                                  
info->value.search.specifics.resume_result.meta,
+                                  
info->value.search.specifics.resume_result.result,
+                                  
info->value.search.specifics.resume_result.applicability_rank);
+      return update_search_result (ret,
+                                  
info->value.search.specifics.resume_result.meta,
+                                  
info->value.search.specifics.resume_result.applicability_rank,
+                                  
info->value.search.specifics.resume_result.availability_certainty,
+                                  
info->value.search.specifics.resume_result.availability_rank);
+    case GNUNET_FS_STATUS_SEARCH_SUSPEND: 
+      close_search_tab (info->value.search.cctx);
+      return NULL;
+    case GNUNET_FS_STATUS_SEARCH_RESULT: 
+      return process_search_result (info->value.search.cctx,
+                                   info->value.search.pctx,
+                                   info->value.search.specifics.result.uri,
+                                   info->value.search.specifics.result.meta,
+                                   info->value.search.specifics.result.result,
+                                   
info->value.search.specifics.result.applicability_rank);
+    case GNUNET_FS_STATUS_SEARCH_RESULT_NAMESPACE: 
+      GNUNET_break (0); 
+      break;
+    case GNUNET_FS_STATUS_SEARCH_UPDATE:
+      return update_search_result (info->value.search.specifics.update.cctx,
+                                  info->value.search.specifics.update.meta,
+                                  
info->value.search.specifics.update.applicability_rank,
+                                  
info->value.search.specifics.update.availability_certainty,
+                                  
info->value.search.specifics.update.availability_rank);
+    case GNUNET_FS_STATUS_SEARCH_ERROR: 
+      return handle_search_error (info->value.search.cctx,
+                                 info->value.search.specifics.error.message);
+    case GNUNET_FS_STATUS_SEARCH_PAUSED: 
+      return info->value.search.cctx;
+    case GNUNET_FS_STATUS_SEARCH_CONTINUED: 
+      return info->value.search.cctx;
+    case GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED: 
+      free_search_result (info->value.search.specifics.result_suspend.cctx);
+      return NULL;
+    case GNUNET_FS_STATUS_SEARCH_RESULT_SUSPEND: 
+      free_search_result (info->value.search.specifics.result_suspend.cctx);
+      return NULL;
+    case GNUNET_FS_STATUS_SEARCH_STOPPED:
+      close_search_tab (info->value.search.cctx);
+      return NULL;
+    case GNUNET_FS_STATUS_UNINDEX_START:
+      GNUNET_break (0); 
+      break;
+    case GNUNET_FS_STATUS_UNINDEX_RESUME: 
+      GNUNET_break (0); 
+      break;
+    case GNUNET_FS_STATUS_UNINDEX_SUSPEND: 
+      GNUNET_break (0); 
+      break;
+    case GNUNET_FS_STATUS_UNINDEX_PROGRESS: 
+      GNUNET_break (0); 
+      break;
+    case GNUNET_FS_STATUS_UNINDEX_ERROR: 
+      GNUNET_break (0);
+      break;
+    case GNUNET_FS_STATUS_UNINDEX_COMPLETED: 
+      GNUNET_break (0); 
+      break;
+    case GNUNET_FS_STATUS_UNINDEX_STOPPED: 
+      GNUNET_break (0); 
+      break;
+    default:
+      GNUNET_break (0);
+      break;
+    }
+  return NULL;
+}
+
+
+/**
+ * Page switched in main notebook, update thumbnail and
+ * metadata views.
+ */
+void
+GNUNET_GTK_main_window_notebook_switch_page_cb (GtkWidget * dummy, 
+                                               gpointer data)
+{
+  GtkNotebook *notebook;
+  gint page;
+  GtkWidget *w;
+  struct SearchTab *tab;
+  GtkImage *image;
+  GtkListStore *ms;
+  GtkTreeView *tv;
+
+  notebook = GTK_NOTEBOOK (GNUNET_FS_GTK_get_main_window_object 
("GNUNET_GTK_main_window_notebook"));
+  page = gtk_notebook_get_current_page (notebook);
+  w = gtk_notebook_get_nth_page (notebook, page);
+  tab = search_tab_head;
+  while (tab != NULL)
+    {
+      if (tab->frame == w)
+       {
+         tv = GTK_TREE_VIEW (gtk_builder_get_object (tab->builder,
+                                                     "_search_result_frame"));
+         update_meta_data_views (tv, tab);
+         return;
+       }
+      tab = tab->next;
+    }
+  image = GTK_IMAGE (GNUNET_FS_GTK_get_main_window_object 
("GNUNET_GTK_main_window_preview_image"));
+  gtk_image_clear (image);
+  ms = GTK_LIST_STORE (GNUNET_FS_GTK_get_main_window_object 
("GNUNET_GTK_meta_data_list_store"));
+  gtk_list_store_clear (ms);
+}
+
+
+
+/* end of fs_event_handler.c */

Copied: gnunet-gtk/src/fs/fs_event_handler.h (from rev 16478, 
gnunet-gtk/src/fs_event_handler.h)
===================================================================
--- gnunet-gtk/src/fs/fs_event_handler.h                                (rev 0)
+++ gnunet-gtk/src/fs/fs_event_handler.h        2011-08-15 14:11:40 UTC (rev 
16543)
@@ -0,0 +1,198 @@
+/*
+     This file is part of GNUnet.
+     (C) 2010 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/fs_event_handler.h
+ * @brief Main event handler for file-sharing
+ * @author Christian Grothoff
+ */
+#include "common.h"
+
+
+struct SearchResult;
+
+
+/**
+ * Context we keep for a search tab.
+ */
+struct SearchTab
+{
+  /**
+   * This is a doubly-linked list.
+   */
+  struct SearchTab *next;
+
+  /**
+   * This is a doubly-linked list.
+   */
+  struct SearchTab *prev;
+
+  /**
+   * Set in case this is an inner search, otherwise NULL.
+   */
+  struct SearchResult *parent;
+
+  /**
+   * Handle for this search with FS library.
+   */
+  struct GNUNET_FS_SearchContext *sc;
+
+  char *query_txt;
+
+  GtkBuilder *builder;
+
+  GtkWidget *frame;
+
+  GtkWidget *tab_label;
+
+  GtkWidget *close_button;
+
+  GtkWidget *clear_button;
+
+  GtkWidget *play_button;
+
+  GtkWidget *pause_button;
+
+  GtkLabel *label;
+
+  GtkTreeStore *ts;
+
+  unsigned int num_results;
+
+};
+
+
+struct DownloadEntry
+{
+
+  /**
+   * Download entry of the parent (for recursive downloads),
+   * NULL if we are either a top-level download (from URI,
+   * from opened directory, orphaned from search or direct
+   * search result).
+   */
+  struct DownloadEntry *pde;
+
+  /**
+   * Associated search result, or NULL if we don't belong
+   * to a search directly (download entry).
+   */
+  struct SearchResult *sr;
+
+  /**
+   * FS handle to control the download.
+   */
+  struct GNUNET_FS_DownloadContext *dc;
+
+  /**
+   * URI for the download.
+   */
+  struct GNUNET_FS_Uri *uri;
+
+  /**
+   * Meta data for the download.
+   */
+  struct GNUNET_CONTAINER_MetaData *meta;
+
+  /**
+   * Where in the tree view is this download being displayed.
+   */
+  GtkTreeRowReference *rr;
+
+  /**
+   * Tree store where we are stored.
+   */
+  GtkTreeStore *ts;
+  
+  /**
+   * Tab where this download is currently on display.
+   */
+  struct SearchTab *tab;
+
+  /**
+   * Has the download completed (or errored)?
+   */
+  int is_done;
+
+};
+
+
+
+
+/**
+ * Setup a new top-level entry in the URI tab.  If necessary, create
+ * the URI tab first.
+ *
+ * @param iter set to the new entry
+ * @param srp set to search result 
+ * @param meta metadata for the new entry
+ * @param uri URI for the new entry
+ * @return NULL on error, otherwise search tab with the new entry
+ */
+struct SearchTab *
+GNUNET_GTK_add_to_uri_tab (GtkTreeIter *iter,
+                          struct SearchResult **sr,
+                          const struct GNUNET_CONTAINER_MetaData *meta,
+                          const struct GNUNET_FS_Uri *uri);
+
+
+
+/**
+ * Add a search result to the given search tab.
+ *
+ * @param tab search tab to extend
+ * @param iter set to position where search result is added
+ * @param parent_rr reference to parent entry in search tab
+ * @param uri uri to add
+ * @param meta metadata of the entry
+ * @param result associated FS search result (can be NULL)
+ * @param applicability_rank how relevant is the result
+ * @return entry for the search result
+ */
+struct SearchResult *
+GNUNET_GTK_add_search_result (struct SearchTab *tab,
+                             GtkTreeIter *iter,
+                             GtkTreeRowReference *parent_rr,
+                             const struct GNUNET_FS_Uri *uri,
+                             const struct GNUNET_CONTAINER_MetaData *meta,
+                             struct GNUNET_FS_SearchResult *result,
+                             uint32_t applicability_rank);
+
+
+/**
+ * Notification of FS to a client about the progress of an 
+ * operation.  Callbacks of this type will be used for uploads,
+ * downloads and searches.  Some of the arguments depend a bit 
+ * in their meaning on the context in which the callback is used.
+ *
+ * @param cls closure
+ * @param info details about the event, specifying the event type
+ *        and various bits about the event
+ * @return client-context (for the next progress call
+ *         for this operation; should be set to NULL for
+ *         SUSPEND and STOPPED events).  The value returned
+ *         will be passed to future callbacks in the respective
+ *         field in the GNUNET_FS_ProgressInfo struct.
+ */
+void* GNUNET_GTK_fs_event_handler (void *cls,
+                                  const struct GNUNET_FS_ProgressInfo *info);
+
+
+/* end of fs_event_handler.h */

Copied: gnunet-gtk/src/fs/gnunet-fs-gtk.c (from rev 16519, 
gnunet-gtk/src/gnunet-gtk.c)
===================================================================
--- gnunet-gtk/src/fs/gnunet-fs-gtk.c                           (rev 0)
+++ gnunet-gtk/src/fs/gnunet-fs-gtk.c   2011-08-15 14:11:40 UTC (rev 16543)
@@ -0,0 +1,249 @@
+/*
+     This file is part of GNUnet.
+     (C) 2010 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/gnunet-gtk.c
+ * @brief Main function of gnunet-gtk
+ * @author Christian Grothoff
+ */
+#include "common.h"
+#include "fs_event_handler.h"
+
+/**
+ * Should gnunet-gtk start in tray mode?
+ */
+static int tray_only;
+
+/**
+ * Handle to our main loop.
+ */
+static struct GNUNET_GTK_MainLoop *ml;
+
+/**
+ * Handle for file-sharing operations.
+ */
+static struct GNUNET_FS_Handle *fs;
+
+/**
+ * List of plugins for meta data extraction.
+ */
+static struct EXTRACTOR_PluginList *plugins;
+
+
+/**
+ * Return handle for file-sharing operations.
+ *
+ * @return NULL on error
+ */
+struct GNUNET_FS_Handle *
+GNUNET_FS_GTK_get_fs_handle ()
+{
+  return fs;
+}
+
+
+/**
+ * Get LE plugin list.
+ */
+struct EXTRACTOR_PluginList *
+GNUNET_FS_GTK_get_le_plugins ()
+{
+  return plugins;
+}
+
+
+/**
+ * Get cfg.
+ */
+const struct GNUNET_CONFIGURATION_Handle *
+GNUNET_FS_GTK_get_configuration (void)
+{
+  return GNUNET_GTK_main_loop_get_configuration (ml);
+}
+
+
+/**
+ * Get an object from the main window.
+ *
+ * @param name name of the object
+ * @return NULL on error
+ */
+GObject *
+GNUNET_FS_GTK_get_main_window_object (const char *name)
+{
+  return GNUNET_GTK_main_loop_get_object (ml, name);
+}
+
+
+/**
+ * Task run on shutdown.
+ * FIXME: does this need to be a separate task!?
+ */
+static void
+shutdown_task (void *cls,
+              const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  if (fs != NULL)
+    {
+      GNUNET_FS_stop (fs);
+      fs = NULL;
+    }
+  EXTRACTOR_plugin_remove_all (plugins);
+  plugins = NULL;
+}
+
+
+/**
+ * Callback invoked if the application is supposed to exit.
+ */
+void 
+GNUNET_GTK_quit_cb (GtkObject *object, 
+                   gpointer user_data)
+{
+  GNUNET_GTK_tray_icon_destroy ();
+  GNUNET_GTK_main_loop_quit (ml);
+  GNUNET_SCHEDULER_add_now (&shutdown_task,
+                           NULL);
+}
+
+
+/**
+ * Search selected in 'file' menu. (from main_window_file_search.c)
+ */
+void
+GNUNET_GTK_main_menu_file_search_activate_cb (GtkWidget * dummy, 
+                                             gpointer data);
+
+
+/**
+ * Add the tab with the 'new' icon for starting a search.
+ */ 
+static void 
+add_new_tab ()
+{
+  GtkNotebook *notebook;
+  GtkWindow *sf;
+  gint pages;
+  GtkBuilder *builder;
+  GtkWidget *label;
+  GtkWidget *frame;
+
+  builder = GNUNET_GTK_get_new_builder 
("gnunet_fs_gtk_main_tab_new_frame.glade");
+
+  /* load frame */
+  sf = GTK_WINDOW (gtk_builder_get_object (builder,
+                                          "_main_tab_new_frame"));
+  label = gtk_bin_get_child (GTK_BIN (sf));
+  gtk_widget_ref (label);
+  gtk_container_remove (GTK_CONTAINER (sf), label);
+  gtk_widget_destroy (GTK_WIDGET (sf));
+  g_object_unref (builder);
+  g_signal_connect(G_OBJECT(label), "clicked", 
+                  G_CALLBACK(&GNUNET_GTK_main_menu_file_search_activate_cb), 
NULL);
+
+  notebook = GTK_NOTEBOOK (GNUNET_FS_GTK_get_main_window_object 
("GNUNET_GTK_main_window_notebook"));
+  pages = gtk_notebook_get_n_pages (notebook);
+  frame = gtk_label_new ("");
+  gtk_widget_show (frame);
+  gtk_notebook_append_page (notebook, 
+                           frame,
+                           label);
+  gtk_notebook_set_current_page (notebook, 
+                                pages);
+  gtk_widget_show (GTK_WIDGET (notebook));
+}
+
+
+/**
+ * Actual main function run right after GNUnet's scheduler
+ * is initialized.  Initializes up GTK and Glade.
+ */                  
+static void
+run (void *cls,
+     const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  GtkWidget *main_window;
+  ml = cls;
+
+  GNUNET_GTK_set_icon_search_path ();
+  GNUNET_GTK_setup_nls ();
+ 
+  /* setup main window */
+  main_window = GTK_WIDGET (GNUNET_FS_GTK_get_main_window_object 
("GNUNET_GTK_main_window"));
+  gtk_window_maximize (GTK_WINDOW (main_window));
+  GNUNET_GTK_tray_icon_create (GTK_WINDOW (main_window),
+                              "gnunet-gtk" /* FIXME: rename icon? */,
+                              "gnunet-fs-gtk");
+
+  /* initialize file-sharing */
+  plugins = EXTRACTOR_plugin_add_defaults (EXTRACTOR_OPTION_DEFAULT_POLICY);
+  fs = GNUNET_FS_start (GNUNET_GTK_main_loop_get_configuration (ml),
+                       "gnunet-gtk",
+                       &GNUNET_GTK_fs_event_handler,
+                       NULL,
+                       GNUNET_FS_FLAGS_NONE /* fixme later for 
persistence/probes */,
+                       /* set other options here later! */
+                       GNUNET_FS_OPTIONS_END);  
+  if (fs != NULL)    
+    {
+      add_new_tab ();
+    }
+  else
+    {
+      gtk_widget_hide (GTK_WIDGET (GNUNET_FS_GTK_get_main_window_object 
("GNUNET_GTK_main_menu_file_create_pseudonym")));
+      gtk_widget_hide (GTK_WIDGET (GNUNET_FS_GTK_get_main_window_object 
("GNUNET_GTK_main_menu_file_advertise_pseudonym")));
+      gtk_widget_hide (GTK_WIDGET (GNUNET_FS_GTK_get_main_window_object 
("GNUNET_GTK_main_menu_file_publish")));
+      gtk_widget_hide (GTK_WIDGET (GNUNET_FS_GTK_get_main_window_object 
("GNUNET_GTK_main_menu_file_search")));
+      gtk_widget_hide (GTK_WIDGET (GNUNET_FS_GTK_get_main_window_object 
("GNUNET_GTK_main_menu_file_download_uri")));
+      gtk_widget_hide (GTK_WIDGET (GNUNET_FS_GTK_get_main_window_object 
("GNUNET_GTK_main_menu_file_open_gnunet_directory")));
+      /* FIXME: set warning in status bar... */
+    }
+  /* make GUI visible */
+  if (!tray_only)
+    {
+      gtk_widget_show (main_window);
+      gtk_window_present (GTK_WINDOW (main_window));
+    }
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  static struct GNUNET_GETOPT_CommandLineOption options[] = {
+    {'t', "tray", NULL,
+     gettext_noop ("start in tray mode"), 0,
+     &GNUNET_GETOPT_set_one, &tray_only},
+    GNUNET_GETOPT_OPTION_END
+  };
+
+  if (GNUNET_OK !=
+      GNUNET_GTK_main_loop_start ("gnunet-gtk",
+                                 "GTK GUI for GNUnet",
+                                 argc, argv,
+                                 options,
+                                 "gnunet_fs_gtk_main_window.glade",
+                                 &run))
+    return 1;
+  return 0;
+}
+
+
+/* end of gnunet-fs-gtk.c */

Added: gnunet-gtk/src/fs/gnunet-fs-gtk.h
===================================================================
--- gnunet-gtk/src/fs/gnunet-fs-gtk.h                           (rev 0)
+++ gnunet-gtk/src/fs/gnunet-fs-gtk.h   2011-08-15 14:11:40 UTC (rev 16543)
@@ -0,0 +1,68 @@
+/*
+     This file is part of GNUnet.
+     (C) 2010 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/fs/gnunet-fs-gtk.h
+ * @brief Globals for gnunet-fs-gtk
+ * @author Christian Grothoff
+ */
+#ifndef GNUNET_FS_GTK_H
+#define GNUNET_FS_GTK_H
+
+#include "gnunet_gtk.h"
+#include <gnunet/gnunet_fs_service.h>
+#include <extractor.h>
+
+
+/**
+ * Get GNU libextractor plugin list.
+ */
+struct EXTRACTOR_PluginList *
+GNUNET_FS_GTK_get_le_plugins (void);
+
+
+/**
+ * Get our configuration.
+ */
+const struct GNUNET_CONFIGURATION_Handle *
+GNUNET_FS_GTK_get_configuration (void);
+
+
+/**
+ * Return our handle for file-sharing operations.
+ *
+ * @return NULL on error
+ */
+struct GNUNET_FS_Handle *
+GNUNET_FS_GTK_get_fs_handle (void);
+
+/**
+ * Get an object from the main window.
+ *
+ * @param name name of the object
+ * @return NULL on error
+ */
+GObject *
+GNUNET_FS_GTK_get_main_window_object (const char *name);
+
+
+
+#endif
+/* end of gnunet-fs-gtk.h */

Copied: gnunet-gtk/src/fs/main_window_adv_pseudonym.c (from rev 16478, 
gnunet-gtk/src/main_window_adv_pseudonym.c)
===================================================================
--- gnunet-gtk/src/fs/main_window_adv_pseudonym.c                               
(rev 0)
+++ gnunet-gtk/src/fs/main_window_adv_pseudonym.c       2011-08-15 14:11:40 UTC 
(rev 16543)
@@ -0,0 +1,588 @@
+/*
+     This file is part of GNUnet
+     (C) 2005, 2006, 2010 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/main_window_adv_pseudonym.c
+ * @author Christian Grothoff
+ */
+#include "common.h"
+#include "gnunet-fs-gtk.h"
+
+static GtkBuilder *builder;
+
+
+/**
+ * Canonicalize spelling of currently selected keyword.
+ */
+void
+GNUNET_GTK_advertise_pseudonym_keyword_list_normalize_button_clicked_cb ()
+{
+  GtkTreeView *tv;
+  GtkTreeSelection *sel;
+  GtkTreeModel *tm;
+  GtkTreeIter iter;
+  gchar *value;
+  struct GNUNET_FS_Uri *uri;
+  struct GNUNET_FS_Uri *nuri;
+  char *nvalue;
+  char *emsg;
+
+  tv = GTK_TREE_VIEW (gtk_builder_get_object (builder,
+                                             
"GNUNET_GTK_advertise_pseudonym_keyword_list_tree_view"));
+  sel = gtk_tree_view_get_selection (tv);
+  if (TRUE != gtk_tree_selection_get_selected (sel, &tm, &iter))
+    {
+      GNUNET_break (0);
+      return;
+    }
+  gtk_tree_model_get (tm, &iter,
+                     0, &value,
+                     -1);
+  emsg = NULL;
+  uri = GNUNET_FS_uri_ksk_create (value, &emsg);
+  if (uri == NULL)
+    {
+      GNUNET_break (0);
+      GNUNET_free (emsg);
+      g_free (value);
+      return;
+    }
+  nuri = GNUNET_FS_uri_ksk_canonicalize (uri);
+  nvalue = GNUNET_FS_uri_ksk_to_string_fancy (nuri);
+  gtk_list_store_set (GTK_LIST_STORE (tm), &iter,
+                     0, nvalue,
+                     -1);
+  GNUNET_FS_uri_destroy (nuri);
+  GNUNET_FS_uri_destroy (uri);
+  GNUNET_free (nvalue);
+  g_free (value);
+}
+
+
+/**
+ * Remove the currently selected keyword from the list.
+ */
+void
+GNUNET_GTK_advertise_pseudonym_keyword_list_del_button_clicked_cb ()
+{
+  GtkTreeView *tv;
+  GtkTreeSelection *sel;
+  GtkTreeModel *tm;
+  GtkTreeIter iter;
+
+  tv = GTK_TREE_VIEW (gtk_builder_get_object (builder,
+                                             
"GNUNET_GTK_advertise_pseudonym_keyword_list_tree_view"));
+  sel = gtk_tree_view_get_selection (tv);
+  if (TRUE != gtk_tree_selection_get_selected (sel, &tm, &iter))
+    {
+      GNUNET_break (0);
+      return;
+    }
+  gtk_list_store_remove (GTK_LIST_STORE (tm),
+                        &iter);
+}
+
+
+/**
+ * Add keyword from entry line to keyword list.
+ */ 
+void
+GNUNET_GTK_advertise_pseudonym_keyword_list_add_button_clicked_cb ()
+{
+  const char *keyword;
+  GtkEntry *entry;
+  GtkListStore *ls;
+  GtkTreeIter iter;
+
+  ls = GTK_LIST_STORE (gtk_builder_get_object (builder,
+                                              
"GNUNET_GTK_keyword_list_store"));
+  entry = GTK_ENTRY (gtk_builder_get_object (builder,                          
                 
+                                            
"GNUNET_GTK_advertise_pseudonym_keyword_entry"));
+  keyword = gtk_entry_get_text (entry);
+  if (strlen (keyword) > 0)
+    gtk_list_store_insert_with_values (ls, &iter, G_MAXINT, 0, keyword, -1);
+  gtk_entry_set_text (entry, "");
+}
+
+
+/**
+ * Update sensitivity of keyword add button.
+ */
+void
+GNUNET_GTK_advertise_pseudonym_keyword_entry_changed_cb ()
+{
+  const char *keyword;
+  GtkEntry *entry;
+  GtkWidget *button;
+
+  button = GTK_WIDGET (gtk_builder_get_object (builder,                        
                         
+                                              
"GNUNET_GTK_advertise_pseudonym_keyword_list_add_button"));
+  entry = GTK_ENTRY (gtk_builder_get_object (builder,                          
                 
+                                            
"GNUNET_GTK_advertise_pseudonym_keyword_entry"));
+  keyword = gtk_entry_get_text (entry);
+  gtk_widget_set_sensitive (button,
+                           (strlen (keyword) > 0) ? TRUE : FALSE);
+}
+
+/**
+ * Update sensitivity of metadata delete button.
+ */
+static void
+metadata_selection_changed_cb (GtkTreeSelection *ts,
+                              gpointer user_data)
+{
+  GtkTreeView *tv;
+  GtkTreeSelection *sel;
+  GtkWidget *button;
+
+  tv = GTK_TREE_VIEW (gtk_builder_get_object (builder,
+                                             
"GNUNET_GTK_advertise_pseudonym_metadata_tree_view"));
+  sel = gtk_tree_view_get_selection (tv);
+  button = GTK_WIDGET (gtk_builder_get_object (builder,                        
                         
+                                              
"GNUNET_GTK_advertise_pseudonym_metadata_delete_button"));
+  gtk_widget_set_sensitive (button,
+                           gtk_tree_selection_get_selected (sel, NULL, NULL));
+}
+
+
+/**
+ * Update sensitivity of keyword delete and normalize button.
+ */
+static void
+keyword_selection_changed_cb (GtkTreeSelection *ts,
+                             gpointer user_data)
+{
+  GtkTreeView *tv;
+  GtkTreeSelection *sel;
+  GtkWidget *button;
+
+  tv = GTK_TREE_VIEW (gtk_builder_get_object (builder,
+                                             
"GNUNET_GTK_advertise_pseudonym_keyword_list_tree_view"));
+  sel = gtk_tree_view_get_selection (tv);
+  button = GTK_WIDGET (gtk_builder_get_object (builder,                        
                         
+                                              
"GNUNET_GTK_advertise_pseudonym_keyword_list_del_button"));
+
+  gtk_widget_set_sensitive (button,
+                           gtk_tree_selection_get_selected (sel, NULL, NULL));
+  button = GTK_WIDGET (gtk_builder_get_object (builder,                        
                         
+                                              
"GNUNET_GTK_advertise_pseudonym_keyword_list_normalize_button"));
+  gtk_widget_set_sensitive (button,
+                           gtk_tree_selection_get_selected (sel, NULL, NULL));
+}
+
+
+/**
+ * Update sensitivity of metadata add button.
+ */
+void
+GNUNET_GTK_advertise_pseudonym_metadata_value_entry_changed_cb ()
+{  
+  GtkTreeView *tv;
+  GtkTreeSelection *sel;
+  const char *value;
+  GtkEntry *entry;
+  GtkWidget *button;
+
+  entry = GTK_ENTRY (gtk_builder_get_object (builder,                          
                 
+                                            
"GNUNET_GTK_advertise_pseudonym_metadata_value_entry"));
+  value = gtk_entry_get_text (entry);
+  tv = GTK_TREE_VIEW (gtk_builder_get_object (builder,
+                                             
"GNUNET_GTK_advertise_pseudonym_metadata_type_tree_view"));
+  sel = gtk_tree_view_get_selection (tv);
+  button = GTK_WIDGET (gtk_builder_get_object (builder,                        
                         
+                                              
"GNUNET_GTK_advertise_pseudonym_metadata_add_button"));  
+  gtk_widget_set_sensitive (button,
+                           (strlen (value) > 0)
+                           ? gtk_tree_selection_get_selected (sel, NULL, NULL) 
+                           : FALSE);
+}
+
+
+/**
+ * Add keyword to keyword list.
+ */
+void
+GNUNET_GTK_advertise_pseudonym_keyword_entry_activate_cb ()
+{
+  GNUNET_GTK_advertise_pseudonym_keyword_list_add_button_clicked_cb ();
+}
+
+
+void
+GNUNET_GTK_advertise_pseudonym_metadata_preview_file_chooser_button_file_set_cb
 ()
+{
+  GNUNET_break (0);
+}
+
+/**
+ * Delete currently selected item from meta data list.
+ */
+void
+GNUNET_GTK_advertise_pseudonym_metadata_delete_button_clicked_cb()
+{
+  GtkTreeView *tv;
+  GtkTreeSelection *sel;
+  GtkTreeModel *tm;
+  GtkTreeIter iter;
+
+  tv = GTK_TREE_VIEW (gtk_builder_get_object (builder,
+                                             
"GNUNET_GTK_advertise_pseudonym_metadata_tree_view"));
+  sel = gtk_tree_view_get_selection (tv);
+  if (TRUE != gtk_tree_selection_get_selected (sel, &tm, &iter))
+    {
+      GNUNET_break (0);
+      return;
+    }
+  gtk_list_store_remove (GTK_LIST_STORE (tm),
+                        &iter);
+}
+
+
+/**
+ * Add metadata to metadata list.
+ */
+void
+GNUNET_GTK_advertise_pseudonym_metadata_add_button_clicked_cb ()
+{
+  const char *value;
+  char *avalue;
+  const char *ivalue;
+  GtkEntry *entry;
+  GtkListStore *ls;
+  GtkTreeModel *tm;
+  GtkTreeView *tv;
+  GtkTreeSelection *sel;
+  GtkTreeIter iter;
+  guint type;
+  size_t slen;
+  char *pos;
+
+  entry = GTK_ENTRY (gtk_builder_get_object (builder,
+                                            
"GNUNET_GTK_advertise_pseudonym_metadata_value_entry"));
+  value = gtk_entry_get_text (entry);
+  if ((value == NULL) || (strlen (value) == 0))
+    {
+      GNUNET_break (0);
+      return;
+    }
+  tv = GTK_TREE_VIEW (gtk_builder_get_object (builder,
+                                             
"GNUNET_GTK_advertise_pseudonym_metadata_type_tree_view"));
+  tm = gtk_tree_view_get_model (tv);
+  sel = gtk_tree_view_get_selection (tv);
+  if (TRUE != gtk_tree_selection_get_selected (sel,
+                                              &tm,
+                                              &iter))
+    {
+      GNUNET_break (0);
+      return;
+    }
+  gtk_tree_model_get (tm,
+                      &iter, 
+                     1, &type, -1);
+  ls = GTK_LIST_STORE (gtk_builder_get_object (builder,
+                                              
"GNUNET_GTK_meta_data_list_store"));
+  if ( (type == EXTRACTOR_METATYPE_FILENAME) &&
+       (value[strlen(value)-1] != '/') )
+    {      
+      GNUNET_asprintf (&avalue,
+                      "%s/",
+                      value);
+      /* if user typed '\' instead of '/', change it! */
+      slen = strlen (avalue);
+      while ( (slen > 1) &&
+             (avalue[slen-2] == '\\') )
+       {
+         avalue[slen-2] = '/';
+         avalue[slen-1] = '\0';
+         slen--;
+       }
+      while (NULL != (pos = strstr (avalue, "\\")))
+       *pos = '/';
+      /* remove '../' everywhere */
+      while (NULL != (pos = strstr (avalue, "../")))
+       {
+         pos[0] = '_';
+         pos[1] = '_';
+         pos[2] = '_';
+       }
+      ivalue = avalue;
+    }
+  else
+    {
+      ivalue = value;
+      avalue = NULL;
+    }
+
+  gtk_list_store_insert_with_values (ls, &iter, G_MAXINT,
+                                    0, type,
+                                    1, (guint) EXTRACTOR_METAFORMAT_UTF8,
+                                    2, EXTRACTOR_metatype_to_string (type),
+                                    3, ivalue,
+                                    -1);
+  GNUNET_free_non_null (avalue);   
+  gtk_entry_set_text (GTK_ENTRY (entry), "");
+}
+
+
+/**
+ * Callback with information about local (!) namespaces.
+ * Contains the names of the local namespace and the global
+ * ID.  Adds the to the list store.
+ *
+ * @param cls closure, the list store.
+ * @param name human-readable identifier of the namespace
+ * @param id hash identifier for the namespace
+ */
+static void
+add_to_list (void *cls,
+            const char *name,
+            const GNUNET_HashCode *id)
+{
+  GtkListStore *ls = cls;
+  GtkTreeIter iter;
+
+  gtk_list_store_insert_with_values (ls,
+                                    &iter,
+                                    -1,
+                                    0, name,
+                                    1, GNUNET_FS_namespace_create 
(GNUNET_FS_GTK_get_fs_handle (),
+                                                                   name),
+                                    -1);
+  
+}
+
+
+static void
+selection_changed_cb (GtkTreeSelection *ts,
+                     gpointer user_data)
+{
+  GtkTreeIter iter;
+  GtkWidget *ok_button;
+  
+  ok_button = GTK_WIDGET (gtk_builder_get_object (builder,
+                                                 
"GNUNET_GTK_select_pseudonym_ok_button"));
+  if (TRUE == gtk_tree_selection_get_selected (ts, NULL, &iter))
+    gtk_widget_set_sensitive (ok_button, TRUE);
+  else
+    gtk_widget_set_sensitive (ok_button, FALSE);
+}
+
+
+/**
+ * Advertise pseudonym selected.
+ */
+void
+GNUNET_GTK_main_menu_file_advertise_pseudonym_activate_cb (GtkWidget * dummy, 
+                                                          gpointer data)
+{
+  GtkWidget *ad;
+  struct GNUNET_FS_Namespace *ns;
+  struct GNUNET_FS_Namespace *nso;
+  GtkListStore *ls;
+  GtkTreeView *tv;
+  GtkTreeIter iter;
+  GtkTreeSelection *sel;
+  GtkTreeModel *tm;
+  GtkSpinButton *sb;
+  struct GNUNET_FS_BlockOptions bo;
+  struct GNUNET_FS_Uri *nxt;
+  struct GNUNET_FS_Uri *mrg;
+  struct GNUNET_FS_Uri *uri;
+  struct GNUNET_CONTAINER_MetaData *nm;
+  const char *root;
+  char *value;
+  guint ntype;  
+  guint nformat;
+  guint type;
+  guint max_type;
+ 
+  builder = GNUNET_GTK_get_new_builder ("select_pseudonym_dialog.glade");
+  if (builder == NULL)
+    {
+      GNUNET_break (0);     
+      return;
+    }
+  ad = GTK_WIDGET (gtk_builder_get_object (builder,
+                                          
"GNUNET_GTK_select_pseudonym_dialog"));
+  ls = GTK_LIST_STORE (gtk_builder_get_object (builder,
+                                              
"GNUNET_GTK_select_pseudonym_liststore"));
+  GNUNET_FS_namespace_list (GNUNET_FS_GTK_get_fs_handle (),
+                           &add_to_list,
+                           ls);
+  tv = GTK_TREE_VIEW (gtk_builder_get_object (builder,
+                                             
"GNUNET_GTK_select_pseudonym_tree_view"));
+  sel = gtk_tree_view_get_selection (tv);
+  g_signal_connect(G_OBJECT(sel), "changed", 
+                  G_CALLBACK(selection_changed_cb), NULL); 
+  if (GTK_RESPONSE_OK != gtk_dialog_run (GTK_DIALOG (ad)))
+    {
+      gtk_widget_destroy (ad);
+      g_object_unref (G_OBJECT (builder));
+      builder = NULL;
+      return;
+    }
+  GNUNET_assert (TRUE == gtk_tree_selection_get_selected (sel, 
+                                                         &tm,
+                                                         &iter));
+  gtk_tree_model_get (tm,
+                     &iter,
+                     1, &ns,
+                     -1);
+  /* free all namespaces from list store except "ns" */
+  if (TRUE ==
+      gtk_tree_model_get_iter_first (tm, &iter))
+    {
+      do
+       {
+         gtk_tree_model_get (tm, &iter,
+                             1, &nso,
+                             -1);
+         if (ns != nso)
+           GNUNET_FS_namespace_delete (nso, GNUNET_NO);
+       }
+      while (TRUE == gtk_tree_model_iter_next (tm, &iter));
+    }
+  gtk_widget_destroy (ad);
+  g_object_unref (G_OBJECT (builder));
+  builder = NULL;
+
+
+  builder = GNUNET_GTK_get_new_builder ("advertise_pseudonym_dialog.glade");
+  if (builder == NULL)
+    {
+      GNUNET_break (0);
+      return;
+    }
+  GNUNET_FS_GTK_setup_expiration_year_adjustment (builder);
+  ad = GTK_WIDGET (gtk_builder_get_object (builder,
+                                          
"GNUNET_GTK_advertise_pseudonym_dialog"));
+  ls = GTK_LIST_STORE (gtk_builder_get_object (builder,
+                                              
"GNUNET_GTK_metatype_list_store"));
+  max_type = EXTRACTOR_metatype_get_max ();
+  type = 1; 
+  while (type < max_type - 1)
+    {
+      gtk_list_store_insert_with_values (ls, &iter, G_MAXINT,
+                                        0, EXTRACTOR_metatype_to_string (type),
+                                        1, type,
+                                        2, EXTRACTOR_metatype_to_description 
(type),
+                                        -1);
+      type++;
+    }
+  tv = GTK_TREE_VIEW (gtk_builder_get_object (builder,
+                                             
"GNUNET_GTK_advertise_pseudonym_metadata_type_tree_view"));
+  sel = gtk_tree_view_get_selection (tv);
+  g_signal_connect(G_OBJECT(sel), "changed", 
+                  
G_CALLBACK(GNUNET_GTK_advertise_pseudonym_metadata_value_entry_changed_cb), 
NULL); 
+
+  tv = GTK_TREE_VIEW (gtk_builder_get_object (builder,
+                                             
"GNUNET_GTK_advertise_pseudonym_metadata_tree_view"));
+  sel = gtk_tree_view_get_selection (tv);
+  g_signal_connect(G_OBJECT(sel), "changed", 
+                  G_CALLBACK(metadata_selection_changed_cb), NULL); 
+
+  tv = GTK_TREE_VIEW (gtk_builder_get_object (builder,
+                                             
"GNUNET_GTK_advertise_pseudonym_keyword_list_tree_view"));
+  sel = gtk_tree_view_get_selection (tv);
+  g_signal_connect(G_OBJECT(sel), "changed", 
+                  G_CALLBACK(keyword_selection_changed_cb), NULL); 
+  if (GTK_RESPONSE_OK != gtk_dialog_run (GTK_DIALOG (ad)))
+    {
+      gtk_widget_destroy (ad);
+      g_object_unref (G_OBJECT (builder));
+      builder = NULL;
+      return;
+    }
+  /* get keywords from list store */
+  nxt = NULL;
+  mrg = NULL;
+  uri = NULL;
+  tm = GTK_TREE_MODEL (gtk_builder_get_object (builder,
+                                              
"GNUNET_GTK_keyword_list_store"));
+  if (TRUE ==
+      gtk_tree_model_get_iter_first (tm, &iter))
+    {
+      do
+       {
+         gtk_tree_model_get (tm, &iter,
+                             0, &value,
+                             -1);
+         nxt = GNUNET_FS_uri_ksk_create_from_args (1, (const char**) &value);
+         mrg = GNUNET_FS_uri_ksk_merge (nxt, uri);
+         GNUNET_FS_uri_destroy (nxt);
+         if (NULL != uri)
+           GNUNET_FS_uri_destroy (uri);
+         uri = mrg;
+         g_free (value);
+       }
+      while (TRUE == gtk_tree_model_iter_next (tm, &iter));
+    }
+  /* get meta */
+  nm = GNUNET_CONTAINER_meta_data_create ();
+  tm = GTK_TREE_MODEL (gtk_builder_get_object (builder,
+                                              
"GNUNET_GTK_meta_data_list_store"));
+  if (TRUE ==
+      gtk_tree_model_get_iter_first (tm, &iter))
+    {
+      do
+       {
+         gtk_tree_model_get (tm, &iter,
+                             0, &ntype,
+                             1, &nformat,
+                             3, &value,
+                             -1);
+         GNUNET_CONTAINER_meta_data_insert (nm,
+                                            "<user>",
+                                            ntype,
+                                            nformat,
+                                            "text/plain",
+                                            value,
+                                            strlen (value)+1);
+         g_free (value);
+       }
+      while (TRUE == gtk_tree_model_iter_next (tm, &iter));
+    }
+  bo.anonymity_level = gtk_spin_button_get_value (GTK_SPIN_BUTTON 
(gtk_builder_get_object (builder,
+                                                                               
           "GNUNET_GTK_advertise_pseudonym_anonymity_spin_button")));
+  bo.content_priority = gtk_spin_button_get_value (GTK_SPIN_BUTTON 
(gtk_builder_get_object (builder,
+                                                                               
            "GNUNET_GTK_advertise_pseudonym_priority_spin_button")));
+  sb = GTK_SPIN_BUTTON (gtk_builder_get_object (builder,
+                                              
"GNUNET_GTK_advertise_pseudonym_expiration_year_spin_button"));
+  bo.expiration_time = GNUNET_FS_GTK_get_expiration_time (sb);
+  root = gtk_entry_get_text (GTK_ENTRY (gtk_builder_get_object (builder,
+                                                               
"GNUNET_GTK_advertise_pseudonym_root_entry")));
+  GNUNET_FS_namespace_advertise (GNUNET_FS_GTK_get_fs_handle (),
+                                uri,
+                                ns,
+                                nm,
+                                &bo,
+                                root,
+                                NULL, NULL);
+  GNUNET_FS_namespace_delete (ns, GNUNET_NO);
+  GNUNET_CONTAINER_meta_data_destroy (nm);
+  if (NULL != uri)
+    GNUNET_FS_uri_destroy (uri);
+  gtk_widget_destroy (ad);
+  g_object_unref (G_OBJECT (builder));
+  builder = NULL;
+}
+
+
+/* end of main_window_adv_pseudonym.c */

Copied: gnunet-gtk/src/fs/main_window_create_pseudonym.c (from rev 16478, 
gnunet-gtk/src/main_window_create_pseudonym.c)
===================================================================
--- gnunet-gtk/src/fs/main_window_create_pseudonym.c                            
(rev 0)
+++ gnunet-gtk/src/fs/main_window_create_pseudonym.c    2011-08-15 14:11:40 UTC 
(rev 16543)
@@ -0,0 +1,65 @@
+/*
+     This file is part of GNUnet
+     (C) 2005, 2006, 2010 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/main_window_create_pseudonym.c
+ * @author Christian Grothoff
+ *
+ */
+#include "common.h"
+#include "gnunet-fs-gtk.h"
+
+/**
+ */
+void
+GNUNET_GTK_main_menu_create_pseudonym_activate_cb (GtkWidget * dummy, 
+                                                  gpointer data)
+{
+  GtkWidget *ad;
+  GtkBuilder *builder;
+  const char *name;
+  struct GNUNET_FS_Namespace *ns;
+
+  builder = GNUNET_GTK_get_new_builder ("create_namespace_dialog.glade");
+  if (builder == NULL)
+    {
+      GNUNET_break (0);
+      return;
+    }
+  ad = GTK_WIDGET (gtk_builder_get_object (builder,
+                                          
"GNUNET_GTK_create_namespace_dialog"));
+  if (GTK_RESPONSE_OK != gtk_dialog_run (GTK_DIALOG (ad)))
+    {
+      gtk_widget_destroy (ad);
+      g_object_unref (G_OBJECT (builder));
+      return;
+    }
+  name = gtk_entry_get_text (GTK_ENTRY (gtk_builder_get_object (builder,
+                                                               
"GNUNET_GTK_create_namespace_name_entry")));
+  /* FIXME: show busy dialog while doing key creation */
+  ns = GNUNET_FS_namespace_create (GNUNET_FS_GTK_get_fs_handle (),
+                                  name);
+  GNUNET_FS_namespace_delete (ns, GNUNET_NO);
+  gtk_widget_destroy (ad);
+  g_object_unref (G_OBJECT (builder));
+}
+
+
+/* end of main_window_create_pseudonym.c */

Copied: gnunet-gtk/src/fs/main_window_file_download.c (from rev 16478, 
gnunet-gtk/src/main_window_file_download.c)
===================================================================
--- gnunet-gtk/src/fs/main_window_file_download.c                               
(rev 0)
+++ gnunet-gtk/src/fs/main_window_file_download.c       2011-08-15 14:11:40 UTC 
(rev 16543)
@@ -0,0 +1,130 @@
+/*
+     This file is part of GNUnet
+     (C) 2005, 2006, 2010 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/main_window_file_download.c
+ * @author Christian Grothoff
+ */
+#include "common.h"
+#include "download.h"
+#include "gnunet-fs-gtk.h"
+
+static GtkBuilder *builder;
+
+void
+GNUNET_GTK_open_url_dialog_url_entry_changed_cb (GtkEditable *editable,
+                                                gpointer user_data)
+{
+  struct GNUNET_FS_Uri *uri;
+  char *perr;
+  const char *uris;
+  
+  perr = NULL;
+  uris = gtk_entry_get_text (GTK_ENTRY (gtk_builder_get_object (builder,
+                                                               
"GNUNET_GTK_open_url_dialog_url_entry")));
+  if (uris != NULL)
+    uri = GNUNET_FS_uri_parse (uris, &perr);
+  else
+    uri = NULL;
+  gtk_widget_set_sensitive (GTK_WIDGET (gtk_builder_get_object (builder,
+                                                               
"GNUNET_GTK_open_url_dialog_execute_button")),
+                           (uri == NULL) ? FALSE : TRUE);
+  if (uri != NULL)
+    GNUNET_FS_uri_destroy (uri);
+  else
+    GNUNET_free_non_null (perr);   
+}
+
+
+/**
+ * User selected "Open URI" in main window.
+ */
+void
+GNUNET_GTK_main_menu_file_download_uri_activate_cb (GtkWidget * dummy, 
+                                                   gpointer data)
+{
+  GtkWidget *ad;
+  const char *uris;
+  uint32_t anonymity;
+  struct GNUNET_FS_Uri *uri;
+  char *perr;
+  struct DownloadContext *dc;
+
+  GNUNET_assert (builder == NULL);
+  builder = GNUNET_GTK_get_new_builder ("open_url_dialog.glade");
+  if (builder == NULL)
+    {
+      GNUNET_break (0);
+      return;
+    }
+  ad = GTK_WIDGET (gtk_builder_get_object (builder,
+                                          "GNUNET_GTK_open_url_dialog"));
+  if (GTK_RESPONSE_OK != gtk_dialog_run (GTK_DIALOG (ad)))
+    {
+      gtk_widget_destroy (ad);
+      g_object_unref (G_OBJECT (builder));
+      builder = NULL;
+      fprintf (stderr,
+              "Dialog closed!\n");
+      return;
+    }
+  uris = gtk_entry_get_text (GTK_ENTRY (gtk_builder_get_object (builder,
+                                                               
"GNUNET_GTK_open_url_dialog_url_entry")));
+  anonymity = gtk_spin_button_get_value (GTK_SPIN_BUTTON 
(gtk_builder_get_object (builder,
+                                                                               
  "GNUNET_GTK_open_url_dialog_anonymity_spin_button")));
+  uri = GNUNET_FS_uri_parse (uris, &perr);
+  gtk_widget_destroy (ad);
+  g_object_unref (G_OBJECT (builder));
+  builder = NULL;
+  if (uri == NULL)
+    {
+      GNUNET_free (perr);
+      /* Why was "execute" button sensitive!? */     
+      GNUNET_break (0);
+      return;
+    }
+  if (GNUNET_FS_uri_test_sks (uri) ||
+      GNUNET_FS_uri_test_ksk (uri))
+    {
+      fprintf (stderr,
+              "Starting search!\n");
+      GNUNET_break (NULL !=
+                   GNUNET_FS_search_start (GNUNET_FS_GTK_get_fs_handle (),
+                                           uri,
+                                           anonymity,
+                                           GNUNET_FS_SEARCH_OPTION_NONE,
+                                           NULL));
+      GNUNET_FS_uri_destroy (uri);
+      return;
+    }
+  if (GNUNET_FS_uri_test_chk (uri) ||
+          GNUNET_FS_uri_test_loc (uri))
+    {
+      dc = GNUNET_malloc (sizeof (struct DownloadContext));
+      dc->uri = uri;
+      dc->anonymity = anonymity;
+      GNUNET_GTK_open_download_as_dialog (dc);
+      return;
+    }
+  GNUNET_break (0);
+  GNUNET_FS_uri_destroy (uri);
+}
+
+/* end of main_window_file_download.c */

Copied: gnunet-gtk/src/fs/main_window_file_publish.c (from rev 16478, 
gnunet-gtk/src/main_window_file_publish.c)
===================================================================
--- gnunet-gtk/src/fs/main_window_file_publish.c                                
(rev 0)
+++ gnunet-gtk/src/fs/main_window_file_publish.c        2011-08-15 14:11:40 UTC 
(rev 16543)
@@ -0,0 +1,1933 @@
+/*
+     This file is part of GNUnet
+     (C) 2005, 2006, 2010 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/main_window_file_publish.c
+ * @author Christian Grothoff
+ */
+#include "common.h"
+#include "gnunet-fs-gtk.h"
+#include "edit_publish_dialog.h"
+#include <gnunet/gnunet_util_lib.h>
+
+#define MARKER_DIR_FILE_SIZE "-"
+
+/**
+ * Builder used for the master publish dialog.
+ */
+static GtkBuilder *master_builder;
+
+
+/**
+ * Check if two GtkTreeIters refer to the same element.
+ *
+ * @param tm tree model of the iterators
+ * @param i1 first iterator
+ * @param i2 second iterator
+ * @return GNUNET_YES if they are equal
+ */
+static int
+gtk_tree_iter_equals (GtkTreeModel *tm,
+                     GtkTreeIter *i1,
+                     GtkTreeIter *i2)
+{
+  GtkTreePath *p1;
+  GtkTreePath *p2;
+  int ret;
+
+  p1 = gtk_tree_model_get_path (tm, i1);
+  p2 = gtk_tree_model_get_path (tm, i2);
+  ret = gtk_tree_path_compare (p1, p2);
+  gtk_tree_path_free (p1);
+  gtk_tree_path_free (p2);
+  return (0 == ret) ? GNUNET_YES : GNUNET_NO;
+}
+
+
+/**
+ * Update selectivity in the master dialog.
+ */
+static void
+update_selectivity ()
+{
+  GtkTreeView *tv;
+  GtkTreeModel *tm;
+  GtkTreeModel *ptm;
+  GtkTreeSelection *sel;
+  GtkTreeIter iter;
+  GtkTreeIter parent;
+  GtkTreeIter pred;
+  GtkWidget *up_button;
+  GtkWidget *down_button;
+  GtkWidget *left_button;
+  GtkWidget *right_button;
+  GtkWidget *delete_button;
+  GtkWidget *edit_button;
+  GtkWidget *execute_button;
+  int is_dir;
+  struct GNUNET_FS_FileInformation *fip;
+  int ns_ok;
+  gchar *namespace_id;
+  
+  tm = GTK_TREE_MODEL (gtk_builder_get_object (master_builder,
+                                              
"GNUNET_GTK_file_sharing_publishing_tree_store"));
+  tv = GTK_TREE_VIEW (gtk_builder_get_object (master_builder,
+                                             
"GNUNET_GTK_master_publish_dialog_pseudonym_tree_view"));
+  sel = gtk_tree_view_get_selection (tv);
+  ns_ok = GNUNET_YES;
+  if (TRUE == gtk_tree_selection_get_selected (sel, &ptm, &iter))
+    {
+      gtk_tree_model_get (ptm,
+                         &iter,
+                         2, &namespace_id,
+                         -1);
+      if (namespace_id == NULL)
+         ns_ok = GNUNET_NO;
+      else
+       g_free (namespace_id);
+    }
+  up_button = GTK_WIDGET (gtk_builder_get_object (master_builder,
+                                                 
"GNUNET_GTK_master_publish_dialog_up_button"));
+  down_button = GTK_WIDGET (gtk_builder_get_object (master_builder,
+                                                 
"GNUNET_GTK_master_publish_dialog_down_button"));
+  left_button = GTK_WIDGET (gtk_builder_get_object (master_builder,
+                                                 
"GNUNET_GTK_master_publish_dialog_left_button"));
+  right_button = GTK_WIDGET (gtk_builder_get_object (master_builder,
+                                                 
"GNUNET_GTK_master_publish_dialog_right_button"));
+  delete_button = GTK_WIDGET (gtk_builder_get_object (master_builder,
+                                                 
"GNUNET_GTK_master_publish_dialog_delete_button"));
+  edit_button = GTK_WIDGET (gtk_builder_get_object (master_builder,
+                                                 
"GNUNET_GTK_master_publish_dialog_edit_button"));
+  execute_button = GTK_WIDGET (gtk_builder_get_object (master_builder,
+                                                      
"GNUNET_GTK_master_publish_dialog_execute_button"));
+  tv = GTK_TREE_VIEW (gtk_builder_get_object (master_builder,
+                                             
"GNUNET_GTK_master_publish_dialog_file_information_tree_view"));
+  sel = gtk_tree_view_get_selection (tv);
+  tm = gtk_tree_view_get_model (tv);
+  if ( (gtk_tree_model_get_iter_first (tm, &iter)) &&
+       (ns_ok == GNUNET_YES) )
+    gtk_widget_set_sensitive (execute_button, TRUE);
+  else
+    gtk_widget_set_sensitive (execute_button, FALSE);
+  if (TRUE != gtk_tree_selection_get_selected (sel,
+                                              &tm,
+                                              &iter))
+    {
+      gtk_widget_set_sensitive (up_button, FALSE);
+      gtk_widget_set_sensitive (down_button, FALSE);
+      gtk_widget_set_sensitive (left_button, FALSE);
+      gtk_widget_set_sensitive (right_button, FALSE);
+      gtk_widget_set_sensitive (delete_button, FALSE);
+      gtk_widget_set_sensitive (edit_button, FALSE);
+      return;
+    }
+  gtk_widget_set_sensitive (delete_button, TRUE);
+  gtk_widget_set_sensitive (edit_button, TRUE);
+
+  /* now figure out which move operations are currently legal */
+  GNUNET_assert (TRUE == gtk_tree_selection_get_selected (sel, NULL, &iter));
+  if (TRUE == gtk_tree_model_iter_next (tm, &iter))
+    {
+      gtk_widget_set_sensitive (down_button, TRUE);      
+    }
+  else
+    {
+      gtk_widget_set_sensitive (down_button, FALSE);
+    }
+  GNUNET_assert (TRUE == gtk_tree_selection_get_selected (sel, NULL, &iter));
+  if (TRUE == gtk_tree_model_iter_parent (tm, &parent, &iter))
+    {
+      gtk_widget_set_sensitive (left_button, TRUE);      
+      GNUNET_assert (TRUE ==
+                    gtk_tree_model_iter_children (tm, &pred, &parent));
+    }
+  else
+    {
+      gtk_widget_set_sensitive (left_button, FALSE);
+      GNUNET_assert (TRUE ==
+                    gtk_tree_model_get_iter_first (tm, &pred));
+    }
+  /* iterate over 'next' of pred to find out if our
+     predecessor is a directory! */
+  is_dir = GNUNET_SYSERR;
+  while (GNUNET_YES != gtk_tree_iter_equals (tm, &pred, &iter))
+    {
+      gtk_tree_model_get (tm, &pred, 
+                         5, &fip, -1);
+      is_dir = GNUNET_FS_file_information_is_directory (fip);
+      GNUNET_assert (TRUE == gtk_tree_model_iter_next (tm, &pred));
+    }
+  if (GNUNET_YES == is_dir)
+    {
+      gtk_widget_set_sensitive (right_button, TRUE);
+    }
+  else
+    {
+      gtk_widget_set_sensitive (right_button, FALSE);
+    }
+  if (GNUNET_SYSERR != is_dir)
+    {
+      gtk_widget_set_sensitive (up_button, TRUE);
+    }
+  else
+    {
+      gtk_widget_set_sensitive (up_button, FALSE);
+    }
+}
+
+
+/**
+ * Add a file to the tree model.
+ *
+ * @param filename file to add
+ * @param bo block options to use
+ * @param do_index should we index or insert?
+ * @param iter parent entry, or NULL for top-level addition
+ */
+static void
+add_file_at_iter (const char *filename,
+                 const struct GNUNET_FS_BlockOptions *bo,
+                 int do_index,
+                 GtkTreeIter *iter)
+{
+  struct GNUNET_FS_FileInformation *fi;
+  GtkTreeRowReference *row_reference;
+  GtkTreePath *path;
+  uint64_t file_size;
+  const char *short_fn;
+  struct GNUNET_CONTAINER_MetaData *meta;
+  struct GNUNET_FS_Uri *ksk_uri;
+  GtkTreeStore *ts;
+  GtkTreeIter pos;
+  char *file_size_fancy;
+  const char *ss;
+  struct stat sbuf;
+
+  if (0 != STAT (filename, &sbuf))
+    return;
+  if (S_ISDIR (sbuf.st_mode))
+    {
+      file_size = 0;
+    }
+  else
+    {
+      if (GNUNET_OK != 
+         GNUNET_DISK_file_size (filename,
+                                &file_size,
+                                GNUNET_YES))
+       {
+         GNUNET_break (0);
+         return;
+       }
+    }
+  ts = GTK_TREE_STORE (gtk_builder_get_object (master_builder,
+                                              
"GNUNET_GTK_file_sharing_publishing_tree_store"));
+
+  meta = GNUNET_CONTAINER_meta_data_create ();
+  GNUNET_FS_meta_data_extract_from_file (meta,
+                                        filename,
+                                        GNUNET_FS_GTK_get_le_plugins());
+  GNUNET_CONTAINER_meta_data_delete (meta,
+                                    EXTRACTOR_METATYPE_FILENAME,
+                                    NULL, 0);
+  short_fn = filename;
+  while (NULL != (ss = strstr (short_fn, DIR_SEPARATOR_STR)))
+    short_fn = 1 + ss;
+  GNUNET_CONTAINER_meta_data_insert (meta,
+                                    "<gnunet-gtk>",
+                                    EXTRACTOR_METATYPE_FILENAME,
+                                    EXTRACTOR_METAFORMAT_UTF8,
+                                    "text/plain",
+                                    short_fn,
+                                    strlen(short_fn)+1);
+  ksk_uri = GNUNET_FS_uri_ksk_create_from_meta_data (meta);
+  gtk_tree_store_insert_before (ts,
+                               &pos,
+                               iter,
+                               NULL);
+  path = gtk_tree_model_get_path (GTK_TREE_MODEL (ts),
+                                 &pos);
+  row_reference = gtk_tree_row_reference_new (GTK_TREE_MODEL (ts),
+                                             path);
+  gtk_tree_path_free (path);
+  fi = GNUNET_FS_file_information_create_from_file 
(GNUNET_FS_GTK_get_fs_handle (),
+                                                   row_reference,
+                                                   filename,
+                                                   ksk_uri,
+                                                   meta,
+                                                   do_index,
+                                                   bo);
+  GNUNET_CONTAINER_meta_data_destroy (meta);
+  GNUNET_FS_uri_destroy (ksk_uri);
+  if (S_ISDIR (sbuf.st_mode))
+    file_size_fancy = GNUNET_strdup (MARKER_DIR_FILE_SIZE);
+  else
+    file_size_fancy = GNUNET_STRINGS_byte_size_fancy (file_size);
+  gtk_tree_store_set (ts, &pos,
+                     0, file_size_fancy,
+                     1, (gboolean) do_index,
+                     2, short_fn,
+                     3, (guint) bo->anonymity_level,
+                     4, (guint) bo->content_priority,
+                     5, fi,
+                     -1);
+  GNUNET_free (file_size_fancy);
+  update_selectivity ();  
+}
+
+
+
+/**
+ * Add an empty directory to the tree model.
+ *
+ * @param name name for the directory
+ * @param bo block options
+ * @param iter parent entry, or NULL for top-level addition
+ * @param pos iterator to set to the location of the new element
+ */
+static void
+create_dir_at_iter (const char *name,
+                   const struct GNUNET_FS_BlockOptions *bo,
+                   GtkTreeIter *iter,
+                   GtkTreeIter *pos)
+{
+  struct GNUNET_FS_FileInformation *fi;
+  GtkTreeRowReference *row_reference;
+  GtkTreePath *path;
+  struct GNUNET_CONTAINER_MetaData *meta;
+  GtkTreeStore *ts;
+
+  ts = GTK_TREE_STORE (gtk_builder_get_object (master_builder,
+                                              
"GNUNET_GTK_file_sharing_publishing_tree_store"));
+  meta = GNUNET_CONTAINER_meta_data_create ();
+  GNUNET_FS_meta_data_make_directory (meta);
+  GNUNET_CONTAINER_meta_data_insert (meta,
+                                    "<gnunet-gtk>",
+                                    EXTRACTOR_METATYPE_FILENAME,
+                                    EXTRACTOR_METAFORMAT_UTF8,
+                                    "text/plain",
+                                    name,
+                                    strlen(name)+1);
+  gtk_tree_store_insert_before (ts,
+                               pos,
+                               iter,
+                               NULL);
+  path = gtk_tree_model_get_path (GTK_TREE_MODEL (ts),
+                                 pos);
+  row_reference = gtk_tree_row_reference_new (GTK_TREE_MODEL (ts),
+                                             path);
+  gtk_tree_path_free (path);
+  fi = GNUNET_FS_file_information_create_empty_directory 
(GNUNET_FS_GTK_get_fs_handle (),
+                                                         row_reference,
+                                                         NULL,
+                                                         meta,
+                                                         bo);
+  GNUNET_CONTAINER_meta_data_destroy (meta);
+  gtk_tree_store_set (ts, pos,
+                     0, MARKER_DIR_FILE_SIZE,
+                     1, (gboolean) GNUNET_NO,
+                     2, name,
+                     3, (guint) bo->anonymity_level,
+                     4, (guint) bo->content_priority,
+                     5, fi,
+                     -1);
+  update_selectivity ();
+}
+
+
+/* ************ code for adding directories starts ************* */
+
+
+/**
+ * Data we keep when calculating the publication details for a file.
+ */
+struct PublishData
+{
+  /**
+   * Metadata for the file.
+   */
+  struct GNUNET_CONTAINER_MetaData *meta;
+
+  /**
+   * Iterator for the entry.
+   */
+  GtkTreeIter iter;
+};
+
+
+/**
+ * Entry for each unique meta data entry to track how often
+ * it occured.  Contains the keyword and the counter.
+ */
+struct MetaCounter
+{
+
+  /**
+   * Keyword that was found.
+   */
+  const char *value;
+
+  /**
+   * Mimetype of the value.
+   */
+  const char *value_mimetype;
+
+  /**
+   * Type of the value.
+   */
+  enum EXTRACTOR_MetaType type;
+  
+  /**
+   * Format of the value.
+   */
+  enum EXTRACTOR_MetaFormat format;
+
+  /**
+   * How many files have meta entries matching this value?
+   * (type and format do not have to match).
+   */
+  unsigned int count;
+
+};
+
+
+/**
+ * Execution context for 'add_dir'
+ */
+struct AddDirContext
+{
+  /**
+   * While scanning, 'parent' is the iter entry for the
+   * parent, or NULL for top-level.
+   */
+  GtkTreeIter *parent;
+
+  /**
+   * Tree store to manipulate.
+   */
+  GtkTreeStore *ts;
+
+  /**
+   * Map from the hash over the meta value to an 'struct MetaCounter'
+   * counter that says how often this value was
+   * encountered in the current directory.
+   */
+  struct GNUNET_CONTAINER_MultiHashMap *metacounter;
+
+  /**
+   * Map from the hash of a filename in the current directory
+   * to the 'struct PublishData*' for the file.
+   */
+  struct GNUNET_CONTAINER_MultiHashMap *metamap;
+
+  /**
+   * Metadata to exclude from using for KSK since it'll be associated
+   * with the parent as well.  NULL for nothing blocked.
+   */
+  struct GNUNET_CONTAINER_MetaData *no_ksk;
+
+  /**
+   * Block options to use.
+   */
+  struct GNUNET_FS_BlockOptions bo;
+
+  /**
+   * Index or insert?
+   */
+  int do_index;
+
+  /**
+   * Number of files in the current directory.
+   */
+  unsigned int dir_entry_count;
+};
+
+
+/**
+ * Add the given meta data item to the
+ * meta data statistics tracker.
+ *
+ * @param cls closure (user-defined)
+ * @param plugin_name name of the plugin that produced this value;
+ *        special values can be used (i.e. '<zlib>' for zlib being
+ *        used in the main libextractor library and yielding
+ *        meta data).
+ * @param type libextractor-type describing the meta data
+ * @param format basic format information about data 
+ * @param data_mime_type mime-type of data (not of the original file);
+ *        can be NULL (if mime-type is not known)
+ * @param data actual meta-data found
+ * @param data_len number of bytes in data
+ * @return 0 to continue extracting, 1 to abort
+ */
+static int
+add_to_meta_counter (void *cls, 
+                    const char *plugin_name,
+                    enum EXTRACTOR_MetaType type,
+                    enum EXTRACTOR_MetaFormat format,
+                    const char *data_mime_type,
+                    const char *data,
+                    size_t data_len)
+{
+  struct GNUNET_CONTAINER_MultiHashMap *mcm = cls;
+  struct MetaCounter *cnt;
+  GNUNET_HashCode hc;
+  size_t mlen;
+  size_t dlen;
+
+  if ( (format != EXTRACTOR_METAFORMAT_UTF8) &&
+       (format != EXTRACTOR_METAFORMAT_C_STRING) )
+    return 0;
+  dlen = strlen (data) + 1;
+  GNUNET_CRYPTO_hash (data,
+                     dlen - 1,
+                     &hc);
+  cnt = GNUNET_CONTAINER_multihashmap_get (mcm, &hc);
+  if (cnt == NULL)
+    {
+      mlen = strlen (data_mime_type) + 1;
+      cnt = GNUNET_malloc (sizeof (struct MetaCounter) + 
+                          dlen + mlen);
+      cnt->count = 1;
+      cnt->value = (const char *) &cnt[1];
+      cnt->value_mimetype = &cnt->value[dlen];
+      memcpy (&cnt[1],
+             data,
+             dlen);
+      memcpy ((char*) cnt->value_mimetype,
+             data_mime_type,
+             mlen);
+      cnt->type = type;
+      cnt->format = format;
+      GNUNET_CONTAINER_multihashmap_put (mcm, 
+                                        &hc,
+                                        cnt,
+                                        
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
+
+    }
+  else
+    {
+      cnt->count++;
+      if (cnt->format == EXTRACTOR_METAFORMAT_C_STRING)
+       cnt->format = format; /* possibly improve to UTF8 */
+      if (cnt->type == EXTRACTOR_METATYPE_UNKNOWN)
+       cnt->type = type;
+    }
+  return 0;
+}
+
+
+/**
+ * Extract metadata from a file and add it to the metamap and
+ * the metacounter.
+ *
+ * @param adc context to modify
+ * @param filename name of the file to process
+ */
+static void
+extract_file (struct AddDirContext *adc,
+             const char *filename)
+{
+  struct PublishData *pd;
+  GNUNET_HashCode hc;
+  const char *short_fn;
+  const char *ss;
+
+  adc->dir_entry_count++;
+  pd = GNUNET_malloc (sizeof (struct PublishData));
+  pd->meta = GNUNET_CONTAINER_meta_data_create ();
+  GNUNET_FS_meta_data_extract_from_file (pd->meta,
+                                        filename,
+                                        GNUNET_FS_GTK_get_le_plugins());
+  GNUNET_CONTAINER_meta_data_delete (pd->meta,
+                                    EXTRACTOR_METATYPE_FILENAME,
+                                    NULL, 0);
+  short_fn = filename;
+  while (NULL != (ss = strstr (short_fn, DIR_SEPARATOR_STR)))
+    short_fn = 1 + ss;
+  GNUNET_CONTAINER_meta_data_insert (pd->meta,
+                                    "<gnunet-gtk>",
+                                    EXTRACTOR_METATYPE_FILENAME,
+                                    EXTRACTOR_METAFORMAT_UTF8,
+                                    "text/plain",
+                                    short_fn,
+                                    strlen(short_fn)+1);
+
+
+  gtk_tree_store_insert_before (adc->ts,                               
+                               &pd->iter,
+                               adc->parent,
+                               NULL);
+  GNUNET_CRYPTO_hash (filename,
+                     strlen (filename),
+                     &hc);
+  GNUNET_CONTAINER_multihashmap_put (adc->metamap,
+                                    &hc,
+                                    pd,
+                                    
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);  
+  GNUNET_CONTAINER_meta_data_iterate (pd->meta,
+                                     &add_to_meta_counter,
+                                     adc->metacounter);
+}
+
+
+/**
+ * Remove  the keyword from the ksk URI.
+ *
+ * @param cls the ksk uri
+ * @param keyword the word to remove
+ * @param is_mandatory ignored
+ * @return always GNUNET_OK
+ */
+static int
+remove_keyword (void *cls,
+               const char *keyword,
+               int is_mandatory)
+{
+  struct GNUNET_FS_Uri *ksk = cls;
+  
+  GNUNET_FS_uri_ksk_remove_keyword (ksk, keyword);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Add the specifics of the given entry to the tree store.
+ * Derive KSK from the given meta data, but exclude meta
+ * data given in "md_no_ksk" for keyword generation.
+ *
+ * @param ts tree store to modify
+ * @param iter position in the tree store for this file
+ * @param filename file to add
+ * @param bo block options
+ * @param do_index should we index or insert?
+ * @param md_no_ksk metadata with keywords NOT to add
+ * @param meta metadata for the file
+ */
+static void
+add_entry_to_ts (GtkTreeStore *ts,
+                GtkTreeIter *iter,
+                const char *filename,
+                const struct GNUNET_FS_BlockOptions *bo,
+                int do_index,
+                struct GNUNET_CONTAINER_MetaData *md_no_ksk,
+                struct GNUNET_CONTAINER_MetaData *meta)
+{  
+  char *file_size_fancy;
+  struct GNUNET_FS_FileInformation *fi;
+  GtkTreeRowReference *row_reference;
+  GtkTreePath *path;
+  uint64_t file_size;
+  struct GNUNET_FS_Uri *ksk_uri;
+  struct GNUNET_FS_Uri *kill_ksk;
+  const char *ss;
+  const char *short_fn;
+  struct stat sbuf;
+
+  if (0 != STAT (filename, &sbuf))
+    return;
+  if (S_ISDIR (sbuf.st_mode))
+    {
+      file_size = 0;
+    }
+  else
+    {
+      if (GNUNET_OK != 
+         GNUNET_DISK_file_size (filename,
+                                &file_size,
+                                GNUNET_YES))
+       {
+         GNUNET_break (0);
+         return;
+       }
+    }
+  ksk_uri = GNUNET_FS_uri_ksk_create_from_meta_data (meta);
+  kill_ksk = GNUNET_FS_uri_ksk_create_from_meta_data (md_no_ksk);
+  if (kill_ksk != NULL)
+    {
+      GNUNET_FS_uri_ksk_get_keywords (kill_ksk,
+                                     &remove_keyword,
+                                     ksk_uri);
+      GNUNET_FS_uri_destroy (kill_ksk);
+    }
+  path = gtk_tree_model_get_path (GTK_TREE_MODEL (ts),
+                                 iter);
+  row_reference = gtk_tree_row_reference_new (GTK_TREE_MODEL (ts),
+                                             path);
+  gtk_tree_path_free (path);
+  if (S_ISDIR (sbuf.st_mode))
+    {
+      GNUNET_CONTAINER_meta_data_delete (meta,
+                                        EXTRACTOR_METATYPE_MIMETYPE,
+                                        NULL, 0);
+      GNUNET_FS_meta_data_make_directory (meta);
+      GNUNET_FS_uri_ksk_add_keyword (ksk_uri,
+                                    GNUNET_FS_DIRECTORY_MIME,
+                                    GNUNET_NO);
+      fi = GNUNET_FS_file_information_create_empty_directory 
(GNUNET_FS_GTK_get_fs_handle (),
+                                                             row_reference,
+                                                             ksk_uri,
+                                                             meta,
+                                                             bo);
+    }
+  else
+    {
+      fi = GNUNET_FS_file_information_create_from_file 
(GNUNET_FS_GTK_get_fs_handle (),
+                                                       row_reference,
+                                                       filename,
+                                                       ksk_uri,
+                                                       meta,
+                                                       do_index,
+                                                       bo);
+    }
+  GNUNET_CONTAINER_meta_data_destroy (meta);
+  GNUNET_FS_uri_destroy (ksk_uri);
+  if (S_ISDIR (sbuf.st_mode))
+    file_size_fancy = GNUNET_strdup (MARKER_DIR_FILE_SIZE);
+  else
+    file_size_fancy = GNUNET_STRINGS_byte_size_fancy (file_size);
+  short_fn = filename;
+  while (NULL != (ss = strstr (short_fn, DIR_SEPARATOR_STR)))
+    short_fn = 1 + ss;
+  gtk_tree_store_set (ts, iter,
+                     0, file_size_fancy,
+                     1, (gboolean) do_index,
+                     2, short_fn,
+                     3, (guint) bo->anonymity_level,
+                     4, (guint) bo->content_priority,
+                     5, fi,
+                     -1);
+  GNUNET_free (file_size_fancy);
+}
+
+
+/**
+ * Function called by the directory iterator to
+ * (recursively) add all of the files in the
+ * directory to the tree.
+ *
+ * @param cls the 'struct AddDirContext*' we're in
+ * @param filename file or directory to scan
+ */
+static int
+publish_entry (void *cls,
+              const char *filename)            
+{
+  struct AddDirContext *adc = cls;
+  struct PublishData *pd;
+  GNUNET_HashCode hc;
+
+  GNUNET_CRYPTO_hash (filename,
+                     strlen (filename),
+                     &hc);
+  pd = GNUNET_CONTAINER_multihashmap_get (adc->metamap,
+                                         &hc);
+  add_entry_to_ts (adc->ts,
+                  &pd->iter,
+                  filename,
+                  &adc->bo,
+                  adc->do_index,
+                  adc->no_ksk,
+                  pd->meta);
+  GNUNET_CONTAINER_multihashmap_remove (adc->metamap,
+                                       &hc,
+                                       pd);
+  GNUNET_free (pd);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Context passed to 'migrate_and_drop'.
+ */
+struct MetaProcessContext
+{
+  /**
+   * Metadata with all the keywords we migrated to the parent.
+   */
+  struct GNUNET_CONTAINER_MetaData *md;
+
+  /**
+   * How often does a keyword have to occur to be 
+   * migrated to the parent?
+   */
+  unsigned int threshold;
+};
+
+
+/**
+ * Copy "frequent" meta data entries over to the
+ * target meta data struct, free the counters.
+ *
+ */
+static int
+migrate_and_drop (void *cls,
+                 const GNUNET_HashCode *key,
+                 void *value)
+{
+  struct MetaProcessContext *mpc = cls;
+  struct MetaCounter *counter = value;
+
+  if (counter->count >= mpc->threshold)
+    {
+      GNUNET_CONTAINER_meta_data_insert (mpc->md,
+                                        "<gnunet-gtk>",
+                                        counter->type,
+                                        counter->format,
+                                        counter->value_mimetype,
+                                        counter->value,
+                                        strlen (counter->value)+1);    
+    }
+  GNUNET_free (counter);
+  return GNUNET_YES;
+}
+
+
+/**
+ * Go over the collected meta data from all entries in the
+ * directory and push common meta data up one level (by
+ * adding it to the returned struct).
+ * 
+ * @param adc collection of child meta data
+ * @return meta data to moved to parent
+ */
+static struct GNUNET_CONTAINER_MetaData *
+process_metadata (struct AddDirContext *adc)
+{
+  struct MetaProcessContext mpc;
+
+  mpc.md = GNUNET_CONTAINER_meta_data_create ();
+  mpc.threshold = (adc->dir_entry_count + 1) / 2; /* 50% */
+  GNUNET_CONTAINER_multihashmap_iterate (adc->metacounter,
+                                        &migrate_and_drop,
+                                        &mpc);
+  GNUNET_CONTAINER_multihashmap_destroy (adc->metacounter);
+  return mpc.md;
+}
+
+
+/**
+ * Function called by the directory iterator to
+ * (recursively) add all of the files in the
+ * directory to the tree.
+ *
+ * @param cls the 'struct AddDirContext*' we're in
+ * @param filename file or directory to scan
+ */
+static int
+scan_directory (void *cls,
+               const char *filename)
+               
+{
+  struct AddDirContext *adc = cls;
+  struct stat sbuf;
+  GtkTreeIter *parent;
+  struct PublishData *pd;
+  GNUNET_HashCode hc;
+  struct GNUNET_CONTAINER_MultiHashMap *mhm;
+  struct GNUNET_CONTAINER_MultiHashMap *mcm;
+  unsigned int pc;
+
+  if (0 != STAT (filename, &sbuf))
+    return GNUNET_OK;
+  if (S_ISDIR (sbuf.st_mode))
+    {
+      parent = adc->parent;
+      mhm = adc->metamap;
+      mcm = adc->metacounter;
+      pc = adc->dir_entry_count;
+      adc->metamap = GNUNET_CONTAINER_multihashmap_create (1024);
+      adc->metacounter = GNUNET_CONTAINER_multihashmap_create (1024);
+      adc->dir_entry_count = 0;
+      pd = GNUNET_malloc (sizeof (struct PublishData));
+      gtk_tree_store_insert_before (adc->ts,
+                                   &pd->iter,
+                                   parent,
+                                   NULL);
+      adc->parent = &pd->iter;
+      GNUNET_DISK_directory_scan (filename,
+                                 &scan_directory,
+                                 adc);
+      pd->meta = process_metadata (adc);
+      adc->no_ksk = pd->meta;
+      GNUNET_DISK_directory_scan (filename,
+                                 &publish_entry,
+                                 adc);      
+      GNUNET_CONTAINER_multihashmap_destroy (adc->metamap);
+      adc->metamap = mhm;
+      adc->metacounter = mcm;
+      adc->parent = parent;
+      adc->dir_entry_count = pc + 1;
+      if (adc->metamap != NULL)
+       {
+         GNUNET_CRYPTO_hash (filename,
+                             strlen (filename),
+                             &hc);
+         GNUNET_CONTAINER_multihashmap_put (adc->metamap,
+                                            &hc,
+                                            pd,
+                                            
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);  
+         GNUNET_CONTAINER_meta_data_iterate (pd->meta,
+                                             &add_to_meta_counter,
+                                             mcm);       
+       }
+      else
+       {
+         GNUNET_assert (mcm == NULL);
+         /* we're top-level */
+         add_entry_to_ts (adc->ts,
+                          &pd->iter,
+                          filename,
+                          &adc->bo,
+                          adc->do_index,
+                          NULL,
+                          pd->meta);
+       }
+    }
+  else
+    {
+      GNUNET_assert (adc->metamap != NULL);
+      extract_file (adc, filename);      
+    }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Add a directory to the tree model.
+ *
+ * @param filename directory name to add
+ * @param bo block options
+ * @param do_index should we index?
+ */
+static void
+add_dir (const char *filename,
+        const struct GNUNET_FS_BlockOptions *bo,
+        int do_index)
+{
+  struct stat sbuf;
+  struct AddDirContext scan_ctx;
+
+  if (0 != STAT (filename, &sbuf))
+    return;
+  if (! S_ISDIR (sbuf.st_mode))
+    {
+      GNUNET_break (0);
+      return;
+    }
+  memset (&scan_ctx, 0, sizeof (scan_ctx));
+  scan_ctx.bo = *bo;
+  scan_ctx.do_index = do_index;
+  scan_ctx.ts = GTK_TREE_STORE (gtk_builder_get_object (master_builder,
+                                                       
"GNUNET_GTK_file_sharing_publishing_tree_store"));
+  scan_directory (&scan_ctx, filename);
+}
+
+
+/* ************ code for adding directories ends here ************* */
+
+
+static void
+selection_changed_cb (GtkTreeSelection *ts,
+                     gpointer user_data)
+{
+  update_selectivity ();
+}
+
+
+static void
+remove_old_entry (GtkTreeStore *ts,
+                 GtkTreeIter *root)
+{
+  GtkTreeIter child;
+  
+  while (TRUE == gtk_tree_model_iter_children (GTK_TREE_MODEL (ts), 
+                                              &child, root))
+    remove_old_entry (ts, &child);    
+  gtk_tree_store_remove (ts, root);
+}
+
+
+/**
+ * Move an entry in the tree.
+ */
+static void
+move_entry (GtkTreeModel *tm,
+           GtkTreeIter *old,
+           GtkTreeIter *newpos,
+           int dsel)
+{
+  struct GNUNET_FS_FileInformation *fip;
+  GtkTreeView *tv;
+  gint do_index;
+  gchar *short_fn;
+  guint anonymity_level;
+  guint priority;
+  char *fsf;
+  GtkTreePath *path;
+  GtkTreeSelection *sel;
+  GtkTreeIter child;
+  GtkTreeIter cnewpos;
+  GtkTreeRowReference *rr;
+  GtkTreeRowReference *rr2;
+ 
+  gtk_tree_model_get (tm,
+                     old,
+                     0, &fsf,
+                     1, &do_index,
+                     2, &short_fn,
+                     3, &anonymity_level,
+                     4, &priority,
+                     5, &fip, 
+                    -1);
+  gtk_tree_store_set (GTK_TREE_STORE (tm), newpos,
+                    0, fsf,
+                    1, do_index,
+                    2, short_fn,
+                    3, (guint)anonymity_level,
+                    4, (guint) priority,
+                    5, fip,
+                    -1);  
+  sel = NULL;
+  tv = NULL;
+  if (dsel == GNUNET_YES)
+    {
+      tv = GTK_TREE_VIEW (gtk_builder_get_object (master_builder,
+                                                 
"GNUNET_GTK_master_publish_dialog_file_information_tree_view"));
+      sel = gtk_tree_view_get_selection (tv);
+      path = gtk_tree_model_get_path (tm, newpos);
+      rr = gtk_tree_row_reference_new (tm, path);
+      gtk_tree_path_free (path);
+    }
+  else
+    {
+      rr = NULL;
+    }
+  if (TRUE == gtk_tree_model_iter_children (tm, &child, old))
+    {
+      do
+       {
+         path = gtk_tree_model_get_path (tm, &child);
+         rr2 = gtk_tree_row_reference_new (tm, path);
+         gtk_tree_path_free (path);
+         gtk_tree_store_insert_before (GTK_TREE_STORE (tm),
+                                       &cnewpos, newpos, NULL);
+         move_entry (tm, &child, &cnewpos, GNUNET_NO);
+         path = gtk_tree_row_reference_get_path (rr2);
+         gtk_tree_row_reference_free (rr2);
+         GNUNET_assert (TRUE == gtk_tree_model_get_iter (tm,
+                                                         &child,
+                                                         path));
+         gtk_tree_path_free (path);      
+       }
+      while (TRUE == gtk_tree_model_iter_next (tm, &child));
+    }
+  g_free (short_fn);
+  g_free (fsf);
+  if (dsel == GNUNET_YES)
+    {
+      path = gtk_tree_row_reference_get_path (rr);
+      gtk_tree_row_reference_free (rr);
+      gtk_tree_view_expand_to_path (tv, path);
+      GNUNET_assert (TRUE == gtk_tree_model_get_iter (tm,
+                                                     newpos,
+                                                     path));
+      gtk_tree_path_free (path);      
+      gtk_tree_selection_select_iter (sel,
+                                     newpos);
+    }
+  update_selectivity ();
+}
+
+/**
+ * User has changed the "current" identifier for the content in
+ * the GtkTreeView.  Update the model.
+ */
+void
+GNUNET_GTK_master_publish_dialog_pseudonym_updates_renderer_edited_cb 
(GtkCellRendererText *renderer,
+                                                                      gchar 
*cpath,
+                                                                      gchar 
*new_text,
+                                                                      gpointer 
user_data)
+{
+  GtkTreeIter iter;
+  GtkTreeStore *ts;
+
+  ts = GTK_TREE_STORE (gtk_builder_get_object (master_builder,
+                                              
"GNUNET_GTK_pseudonym_tree_store"));
+  
+  if (TRUE !=
+      gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (ts), &iter, cpath))
+    {
+      GNUNET_break (0);
+      return;
+    }
+  gtk_tree_store_set (ts, &iter, 5, new_text, -1);
+  update_selectivity ();
+}
+
+
+/**
+ * User has changed the "current" identifier for the content in
+ * the GtkTreeView.  Update the model.
+ */
+void
+GNUNET_GTK_master_publish_dialog_pseudonym_identifier_renderer_edited_cb 
(GtkCellRendererText *renderer,
+                                                                         gchar 
*cpath,
+                                                                         gchar 
*new_text,
+                                                                         
gpointer user_data)
+{
+  GtkTreeIter iter;
+  GtkTreeStore *ts;
+
+  ts = GTK_TREE_STORE (gtk_builder_get_object (master_builder,
+                                              
"GNUNET_GTK_pseudonym_tree_store"));
+  
+  if (TRUE !=
+      gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (ts), &iter, cpath))
+    {
+      GNUNET_break (0);
+      return;
+    }
+  gtk_tree_store_set (ts, &iter, 2, new_text, -1);
+  update_selectivity ();
+}
+
+
+void
+GNUNET_GTK_master_publish_dialog_right_button_clicked_cb (GtkWidget * dummy, 
+                                                         gpointer data)
+{
+  GtkTreeView *tv;
+  GtkTreeModel *tm;
+  GtkTreeSelection *sel;
+  GtkTreeIter iter;
+  GtkTreeIter parent;
+  GtkTreeIter pred;
+  GtkTreeIter prev;
+  GtkTreeIter pos;
+
+  tv = GTK_TREE_VIEW (gtk_builder_get_object (master_builder,
+                                             
"GNUNET_GTK_master_publish_dialog_file_information_tree_view"));
+  sel = gtk_tree_view_get_selection (tv);
+  if (TRUE != gtk_tree_selection_get_selected (sel,
+                                              &tm,
+                                              &iter))
+    {
+      GNUNET_break (0);
+      return;
+    }
+  if (TRUE == gtk_tree_model_iter_parent (tm, &parent, &iter))
+    {
+      GNUNET_assert (TRUE == gtk_tree_model_iter_children (tm, &pred, 
&parent));
+    }
+  else if (TRUE != gtk_tree_model_get_iter_first (tm, &pred))
+    {
+      GNUNET_break (0);
+      return;
+    }
+  /* iterate over 'next' of pred to find out who our predecessor is! */
+  memset (&prev, 0, sizeof (GtkTreeIter));
+  while (GNUNET_YES != gtk_tree_iter_equals (tm, &pred, &iter))
+    {
+      prev = pred;
+      GNUNET_assert (TRUE == gtk_tree_model_iter_next (tm, &pred));
+    }
+  gtk_tree_store_insert_before (GTK_TREE_STORE (tm),
+                               &pos, &prev, NULL);
+  if (TRUE != gtk_tree_selection_get_selected (sel,
+                                              &tm,
+                                              &iter))
+    {
+      GNUNET_break (0);
+      return;
+    }
+  move_entry (tm, &iter, &pos, GNUNET_YES);
+  remove_old_entry (GTK_TREE_STORE (tm), &iter);
+}
+
+
+void
+GNUNET_GTK_master_publish_dialog_left_button_clicked_cb (GtkWidget * dummy, 
+                                                        gpointer data)
+{
+  GtkTreeView *tv;
+  GtkTreeModel *tm;
+  GtkTreeSelection *sel;
+  GtkTreeIter iter;
+  GtkTreeIter parent;
+  GtkTreeIter pos;
+
+  tv = GTK_TREE_VIEW (gtk_builder_get_object (master_builder,
+                                             
"GNUNET_GTK_master_publish_dialog_file_information_tree_view"));
+  sel = gtk_tree_view_get_selection (tv);
+  if (TRUE != gtk_tree_selection_get_selected (sel,
+                                              &tm,
+                                              &iter))
+    {
+      GNUNET_break (0);
+      return;
+    }
+  if (TRUE != gtk_tree_model_iter_parent (tm, &parent, &iter))
+    {
+      GNUNET_break (0);
+      return;
+    }
+  gtk_tree_store_insert_after (GTK_TREE_STORE (tm),
+                              &pos, NULL, &parent);
+  if (TRUE != gtk_tree_selection_get_selected (sel,
+                                              &tm,
+                                              &iter))
+    {
+      GNUNET_break (0);
+      return;
+    }
+  move_entry (tm, &iter, &pos, GNUNET_YES);
+  remove_old_entry (GTK_TREE_STORE (tm), &iter);
+}
+
+
+void
+GNUNET_GTK_master_publish_dialog_up_button_clicked_cb (GtkWidget * dummy, 
+                                                      gpointer data)
+{
+  GtkTreeView *tv;
+  GtkTreeModel *tm;
+  GtkTreeSelection *sel;
+  GtkTreeIter iter;
+  GtkTreeIter parent;
+  GtkTreeIter pred;
+  GtkTreeIter prev;
+  GtkTreeIter *pprev;
+  GtkTreeIter pos;
+
+  tv = GTK_TREE_VIEW (gtk_builder_get_object (master_builder,
+                                             
"GNUNET_GTK_master_publish_dialog_file_information_tree_view"));
+  sel = gtk_tree_view_get_selection (tv);
+  if (TRUE != gtk_tree_selection_get_selected (sel,
+                                              &tm,
+                                              &iter))
+   {
+      GNUNET_break (0);
+      return;
+    }
+  if (TRUE == gtk_tree_model_iter_parent (tm, &parent, &iter))
+    {
+      GNUNET_assert (TRUE == gtk_tree_model_iter_children (tm, &pred, 
&parent));
+      pprev = &parent;
+    }
+  else if (TRUE == gtk_tree_model_get_iter_first (tm, &pred))
+    {
+      pprev = NULL;
+    }
+  else
+    {
+      GNUNET_break (0);
+      return;
+    }
+  /* iterate over 'next' of pred to find out who our predecessor is! */
+  while (GNUNET_YES != gtk_tree_iter_equals (tm, &pred, &iter))
+    {
+      prev = pred;
+      pprev = &prev;
+      GNUNET_assert (TRUE == gtk_tree_model_iter_next (tm, &pred));
+    }
+  gtk_tree_store_insert_before (GTK_TREE_STORE (tm),
+                              &pos, NULL, pprev);
+  if (TRUE != gtk_tree_selection_get_selected (sel,
+                                              &tm,
+                                              &iter))
+    {
+      GNUNET_break (0);
+      return;
+    }
+  move_entry (tm, &iter, &pos, GNUNET_YES);
+  remove_old_entry (GTK_TREE_STORE (tm), &iter);
+}
+
+
+void
+GNUNET_GTK_master_publish_dialog_down_button_clicked_cb (GtkWidget * dummy, 
+                                                        gpointer data)
+{
+  GtkTreeView *tv;
+  GtkTreeModel *tm;
+  GtkTreeSelection *sel;
+  GtkTreeIter iter;
+  GtkTreeIter next;
+  GtkTreeIter pos;
+
+  tv = GTK_TREE_VIEW (gtk_builder_get_object (master_builder,
+                                             
"GNUNET_GTK_master_publish_dialog_file_information_tree_view"));
+  sel = gtk_tree_view_get_selection (tv);
+  if (TRUE != gtk_tree_selection_get_selected (sel,
+                                              &tm,
+                                              &iter))
+    {
+      GNUNET_break (0);
+      return;
+    }
+  if (TRUE != gtk_tree_selection_get_selected (sel,
+                                              &tm,
+                                              &next))
+    {
+      GNUNET_break (0);
+      return;
+    }
+  GNUNET_assert (TRUE == gtk_tree_model_iter_next (tm, &next));
+  gtk_tree_store_insert_after (GTK_TREE_STORE (tm),
+                              &pos, NULL, &next);
+  if (TRUE != gtk_tree_selection_get_selected (sel,
+                                              &tm,
+                                              &iter))
+    {
+      GNUNET_break (0);
+      return;
+    }
+  move_entry (tm, &iter, &pos, GNUNET_YES);
+  remove_old_entry (GTK_TREE_STORE (tm), &iter);
+}
+
+
+void
+GNUNET_GTK_master_publish_dialog_new_button_clicked_cb (GtkWidget * dummy, 
+                                                       gpointer data)
+{
+  GtkTreeView *tv;
+  GtkTreeSelection *sel;
+  GtkTreeIter iter;
+  GtkTreeIter pos;
+  struct GNUNET_FS_BlockOptions bo;
+
+  tv = GTK_TREE_VIEW (gtk_builder_get_object (master_builder,
+                                             
"GNUNET_GTK_master_publish_dialog_file_information_tree_view"));
+  sel = gtk_tree_view_get_selection (tv);
+  /* FIXME: consider opening a dialog to get
+   * anonymity, priority and expiration prior
+   * to calling this function (currently we
+   * use default values for those).
+   */
+  bo.anonymity_level = 1;
+  bo.content_priority = 1000;
+  bo.expiration_time = GNUNET_TIME_relative_to_absolute 
(GNUNET_TIME_UNIT_YEARS);
+  bo.replication_level = 1;
+  if (TRUE != gtk_tree_selection_get_selected (sel,
+                                              NULL,
+                                              &iter))
+    {
+      create_dir_at_iter ("unnamed/", 
+                         &bo,                    
+                         NULL, &pos);
+      return;
+    }
+  create_dir_at_iter ("unnamed/",
+                     &bo,
+                     &iter, &pos);
+}
+
+
+void
+GNUNET_GTK_master_publish_dialog_add_button_clicked_cb (GtkWidget * dummy, 
+                                                       gpointer data)
+{
+  GtkWidget *ad;
+  GtkBuilder *builder;
+  char *filename;
+  struct GNUNET_FS_BlockOptions bo;
+  int do_index;
+  GtkSpinButton *sb;
+
+  builder = GNUNET_GTK_get_new_builder ("publish-file-dialog.glade");
+  if (builder == NULL)
+    {
+      GNUNET_break (0);
+      return;
+    }
+  ad = GTK_WIDGET (gtk_builder_get_object (builder,
+                                          "GNUNET_GTK_publish_file_dialog"));
+  GNUNET_FS_GTK_setup_expiration_year_adjustment (builder);
+  if (GTK_RESPONSE_OK != gtk_dialog_run (GTK_DIALOG (ad)))
+    {
+      gtk_widget_destroy (ad);
+      g_object_unref (G_OBJECT (builder));
+      return;
+    }
+  filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(ad));
+  sb = GTK_SPIN_BUTTON (gtk_builder_get_object (builder,
+                                               
"GNUNET_GTK_publish_file_dialog_expiration_year_spin_button"));
+  bo.anonymity_level = gtk_spin_button_get_value (GTK_SPIN_BUTTON 
(gtk_builder_get_object (builder,
+                                                                               
           "GNUNET_GTK_publish_file_dialog_anonymity_spin_button")));
+  bo.content_priority = gtk_spin_button_get_value (GTK_SPIN_BUTTON 
(gtk_builder_get_object (builder,
+                                                                               
            "GNUNET_GTK_publish_file_dialog_priority_spin_button")));
+  bo.expiration_time = GNUNET_FS_GTK_get_expiration_time (sb);
+  bo.replication_level = 1; /* FIXME... */
+  do_index = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON 
(gtk_builder_get_object (builder,
+                                                                               
      "GNUNET_GTK_publish_file_dialog_do_index_checkbutton")));
+  add_file_at_iter (filename,
+                   &bo, do_index, 
+                   NULL);
+  gtk_widget_destroy (ad);
+  g_object_unref (G_OBJECT (builder));
+  g_free (filename);
+  update_selectivity ();
+}
+
+
+void
+GNUNET_GTK_master_publish_dialog_edit_button_clicked_cb (GtkWidget * dummy, 
+                                                        gpointer data)
+{
+  GtkTreeView *tv;
+  GtkTreeModel *tm;
+  GtkTreeSelection *sel;
+  GtkTreeIter iter;
+  int do_index;
+  guint anonymity_level;
+  guint priority;
+  gchar *short_fn;
+  struct GNUNET_FS_FileInformation *fip;
+
+  tv = GTK_TREE_VIEW (gtk_builder_get_object (master_builder,
+                                             
"GNUNET_GTK_master_publish_dialog_file_information_tree_view"));
+  sel = gtk_tree_view_get_selection (tv);
+  if (TRUE != gtk_tree_selection_get_selected (sel,
+                                              &tm,
+                                              &iter))
+    {
+      GNUNET_break (0);
+      return;
+    }
+  gtk_tree_model_get (tm,
+                     &iter,
+                     1, &do_index,
+                     2, &short_fn,
+                     3, &anonymity_level,
+                     4, &priority,
+                     5, &fip,
+                     -1);
+  GNUNET_GTK_edit_publish_dialog (&do_index,
+                                 &short_fn,
+                                 &anonymity_level,
+                                 &priority,
+                                 fip);
+  gtk_tree_store_set (GTK_TREE_STORE (tm),
+                     &iter,
+                     1, do_index,
+                     2, short_fn,
+                     3, anonymity_level,
+                     4, priority,
+                     -1);
+  g_free (short_fn);
+}
+
+
+/**
+ * Free row reference stored in the file information's
+ * client-info pointer.
+ */
+static int
+free_fi_row_reference (void *cls,
+                      struct GNUNET_FS_FileInformation *fi,
+                      uint64_t length,
+                      struct GNUNET_CONTAINER_MetaData *meta,
+                      struct GNUNET_FS_Uri **uri,
+                      struct GNUNET_FS_BlockOptions *bo,
+                      int *do_index,
+                      void **client_info)
+{
+  GtkTreeRowReference *row = *client_info;
+
+  if (row == NULL)
+    {
+      GNUNET_break (0);
+      return GNUNET_OK;
+    }
+  gtk_tree_row_reference_free (row);
+  return GNUNET_OK;
+}
+
+
+
+void
+GNUNET_GTK_master_publish_dialog_delete_button_clicked_cb (GtkWidget * dummy, 
+                                                          gpointer data)
+{
+  GtkTreeView *tv;
+  GtkTreeModel *tm;
+  GtkTreeSelection *sel;
+  GtkTreeIter iter;
+  struct GNUNET_FS_FileInformation *fip;
+
+  tv = GTK_TREE_VIEW (gtk_builder_get_object (master_builder,
+                                             
"GNUNET_GTK_master_publish_dialog_file_information_tree_view"));
+  sel = gtk_tree_view_get_selection (tv);
+  if (TRUE != gtk_tree_selection_get_selected (sel,
+                                              &tm,
+                                              &iter))
+    {
+      GNUNET_break (0);
+      return;
+    }
+  gtk_tree_model_get (tm,
+                     &iter,
+                     5, &fip,
+                     -1);
+  GNUNET_FS_file_information_destroy (fip,
+                                     &free_fi_row_reference,
+                                     NULL);
+  gtk_tree_store_remove (GTK_TREE_STORE (tm),
+                        &iter);
+  update_selectivity ();
+}
+
+
+void
+GNUNET_GTK_master_publish_dialog_open_button_clicked_cb (GtkWidget * dummy, 
+                                                        gpointer data)
+{
+  GtkWidget *ad;
+  GtkBuilder *builder;
+  char *filename;
+  int do_index;
+  GtkSpinButton *sb;
+  struct GNUNET_FS_BlockOptions bo;
+
+  builder = GNUNET_GTK_get_new_builder ("publish-directory-dialog.glade");
+  if (builder == NULL)
+    {
+      GNUNET_break (0);
+      return;
+    }
+  GNUNET_FS_GTK_setup_expiration_year_adjustment (builder);
+  ad = GTK_WIDGET (gtk_builder_get_object (builder,
+                                          
"GNUNET_GTK_publish_directory_dialog"));
+  if (GTK_RESPONSE_OK != gtk_dialog_run (GTK_DIALOG (ad)))
+    {
+      gtk_widget_destroy (ad);
+      g_object_unref (G_OBJECT (builder));
+      return;
+    }
+  filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(ad));
+  sb = GTK_SPIN_BUTTON (gtk_builder_get_object (builder,
+                                               
"GNUNET_GTK_publish_directory_dialog_expiration_year_spin_button"));
+  bo.anonymity_level = gtk_spin_button_get_value (GTK_SPIN_BUTTON 
(gtk_builder_get_object (builder,
+                                                                               
           "GNUNET_GTK_publish_directory_dialog_anonymity_spin_button")));
+  bo.content_priority = gtk_spin_button_get_value (GTK_SPIN_BUTTON 
(gtk_builder_get_object (builder,
+                                                                               
            "GNUNET_GTK_publish_directory_dialog_priority_spin_button")));
+  bo.expiration_time = GNUNET_FS_GTK_get_expiration_time (sb);
+  do_index = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON 
(gtk_builder_get_object (builder,
+                                                                               
      "GNUNET_GTK_publish_directory_dialog_do_index_checkbutton")));
+  
+  gtk_widget_destroy (ad);
+  g_object_unref (G_OBJECT (builder));
+  /* FIXME: open progress dialog here... */
+  add_dir (filename, &bo, do_index);
+  g_free (filename);
+  update_selectivity ();
+}
+
+
+/**
+ * Get the file information struct corresponding to the
+ * given iter in the publish dialog tree model.  Recursively
+ * builds the file information struct from the subtree.
+ *
+ * @param tm model to grab fi from
+ * @param iter position to grab fi from
+ * @return file information from the given position (never NULL)
+ */
+static struct GNUNET_FS_FileInformation *
+get_file_information (GtkTreeModel *tm,
+                     GtkTreeIter *iter)
+{
+  struct GNUNET_FS_FileInformation *fi;
+  struct GNUNET_FS_FileInformation *fic;
+  GtkTreeIter child;
+  
+  gtk_tree_model_get (tm, iter,
+                     5, &fi,
+                     -1);
+  gtk_tree_store_set (GTK_TREE_STORE (tm), iter,
+                     5, NULL,
+                     -1);
+  GNUNET_assert (fi != NULL);
+  if (gtk_tree_model_iter_children (tm, &child, iter))
+    {
+      GNUNET_break (GNUNET_YES ==
+                   GNUNET_FS_file_information_is_directory (fi));
+      do
+       {
+         fic = get_file_information (tm, &child);
+         GNUNET_break (GNUNET_OK ==
+                       GNUNET_FS_file_information_add (fi, fic));
+       }
+      while (gtk_tree_model_iter_next (tm, &child));
+    }
+  return fi;
+}
+
+
+/**
+ * Closure for 'add_updateable_to_ts'.
+ */
+struct UpdateableContext
+{
+  /**
+   * Parent of current insertion.
+   */ 
+  GtkTreeIter *parent;
+
+  /**
+   * Tree store we are modifying.
+   */
+  GtkTreeStore *ts;
+
+  /**
+   * Name of the namespace.
+   */
+  const char *namespace_name;
+
+  /**
+   * Handle to the namespace.
+   */
+  struct GNUNET_FS_Namespace *ns;
+
+  /**
+   * Hash codes of identifiers already added to tree store.
+   */
+  struct GNUNET_CONTAINER_MultiHashMap *seen;
+
+  /**
+   * Did the iterator get called?
+   */
+  int update_called;
+};
+
+
+/**
+ * Add updateable entries to the tree view.
+ *
+ * @param cls closure
+ * @param last_id ID to add
+ * @param last_uri associated URI
+ * @param last_meta associate meta data
+ * @param next_id ID for future updates
+ */ 
+static void
+add_updateable_to_ts (void *cls,
+                     const char *last_id,
+                     const struct GNUNET_FS_Uri *last_uri,
+                     const struct GNUNET_CONTAINER_MetaData *last_meta,
+                     const char *next_id)
+{
+  struct UpdateableContext *uc = cls;
+  struct UpdateableContext sc;
+  GtkTreeIter iter;
+  GtkTreeIter titer;
+  char *desc;
+  GNUNET_HashCode hc;
+
+  uc->update_called = GNUNET_YES;
+  GNUNET_CRYPTO_hash (last_id,
+                     strlen (last_id),
+                     &hc);
+  if (NULL !=
+      GNUNET_CONTAINER_multihashmap_get (uc->seen,
+                                        &hc))
+    return;
+  GNUNET_CONTAINER_multihashmap_put (uc->seen,
+                                    &hc,
+                                    "dummy",
+                                    
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);  
+  desc = GNUNET_CONTAINER_meta_data_get_first_by_types (last_meta,
+                                                       
EXTRACTOR_METATYPE_DESCRIPTION,
+                                                       
EXTRACTOR_METATYPE_TITLE,
+                                                       
EXTRACTOR_METATYPE_BOOK_TITLE,
+                                                       
EXTRACTOR_METATYPE_FILENAME,
+                                                       
EXTRACTOR_METATYPE_SUMMARY,
+                                                       
EXTRACTOR_METATYPE_ALBUM,
+                                                       
EXTRACTOR_METATYPE_COMMENT,
+                                                       
EXTRACTOR_METATYPE_SUBJECT,
+                                                       -1);
+  gtk_tree_store_insert_with_values (uc->ts, 
+                                    &iter,
+                                    uc->parent,
+                                    G_MAXINT,
+                                    0, uc->namespace_name,
+                                    1, uc->ns,
+                                    2, last_id,
+                                    3, GNUNET_FS_uri_dup (last_uri),
+                                    4, GNUNET_CONTAINER_meta_data_duplicate 
(last_meta),
+                                    5, "",
+                                    6, desc,
+                                    7, TRUE /* update editable (always) */,
+                                    8, FALSE /* current not editable (only for 
top-level) */,
+                                    -1);  
+  GNUNET_free_non_null (desc);
+  sc.parent = &iter;
+  sc.ts = uc->ts;
+  sc.namespace_name = uc->namespace_name;
+  sc.ns = uc->ns;
+  sc.seen = uc->seen;
+  sc.update_called = GNUNET_NO;
+  GNUNET_FS_namespace_list_updateable (uc->ns, 
+                                      next_id,
+                                      &add_updateable_to_ts, 
+                                      &sc);
+  if ( (sc.update_called == GNUNET_NO) &&
+       (next_id != NULL) &&
+       (strlen (next_id) > 0) )
+    {
+      /* add leaf */
+      gtk_tree_store_insert_with_values (uc->ts, 
+                                        &titer,
+                                        &iter,
+                                        G_MAXINT,
+                                        0, uc->namespace_name,
+                                        1, uc->ns,
+                                        2, next_id,
+                                        3, NULL,
+                                        4, NULL,
+                                        5, "",
+                                        6, "",
+                                        7, TRUE /* update editable (always) */,
+                                        8, FALSE /* current not editable (only 
for top-level) */,
+                                        -1);  
+    }  
+}
+
+
+/**
+ * Add all updateable entries of the current namespace to the
+ * tree store.
+ *
+ * @param cls the 'GtkTreeStore' to update
+ * @param name name of the namespace to add
+ * @param id identity of the namespace to add
+ */
+static void
+add_namespace_to_ts (void *cls,
+                    const char *name,
+                    const GNUNET_HashCode *id)
+{
+  GtkTreeStore *ts = cls;
+  struct UpdateableContext uc;
+  GtkTreeIter iter;
+
+  uc.parent = &iter;
+  uc.namespace_name = name;
+  uc.ts = ts;
+  uc.ns = GNUNET_FS_namespace_create (GNUNET_FS_GTK_get_fs_handle (),
+                                     name);
+  uc.update_called = GNUNET_NO;
+  gtk_tree_store_insert_with_values (ts, &iter,
+                                    NULL,
+                                    G_MAXINT,
+                                    0, name,
+                                    1, uc.ns,
+                                    2, NULL /* last-id */,
+                                    3, NULL /* last-uri */,
+                                    4, NULL /* meta */,
+                                    5, NULL /* next-ID */,
+                                    6, NULL /* last-description */,
+                                    7, TRUE /* update editable */,
+                                    8, TRUE /* current editable */,
+                                    -1);  
+  uc.seen = GNUNET_CONTAINER_multihashmap_create (128);
+  GNUNET_FS_namespace_list_updateable (uc.ns, NULL,
+                                      &add_updateable_to_ts, &uc);
+  GNUNET_CONTAINER_multihashmap_destroy (uc.seen);
+}
+
+
+static void
+free_pseudonym_tree_store (GtkTreeModel *tm,
+                          GtkTreeIter *iter)
+{
+  struct GNUNET_FS_Uri *uri;
+  struct GNUNET_CONTAINER_MetaData *meta;
+  struct GNUNET_FS_Namespace *ns;
+  GtkTreeIter child;
+
+  gtk_tree_model_get (tm,
+                     iter,
+                     1, &ns,
+                     3, &uri,
+                     4, &meta,
+                     -1);
+  if (uri != NULL)
+    GNUNET_FS_uri_destroy (uri);
+  if (meta != NULL)
+    GNUNET_CONTAINER_meta_data_destroy (meta);
+  if (ns != NULL)
+    {
+      // FIXME: delete ns?
+      // GNUNET_FS_namespace_delete (nso, GNUNET_NO);
+    }
+  if (TRUE ==
+      gtk_tree_model_iter_children (tm, &child, iter))
+    {
+      do 
+       {
+         free_pseudonym_tree_store (tm,
+                                    &child);
+       }
+      while (TRUE == gtk_tree_model_iter_next (tm,
+                                              &child));
+    }
+}
+
+
+static void
+free_file_information_tree_store (GtkTreeModel *tm,
+                                 GtkTreeIter *iter)
+{
+  GtkTreeIter child;
+  struct GNUNET_FS_FileInformation *fip;
+
+  gtk_tree_model_get (tm,
+                     iter,
+                     5, &fip,
+                     -1);
+  if (fip != NULL)
+    GNUNET_FS_file_information_destroy (fip, NULL, NULL);
+  if (TRUE ==
+      gtk_tree_model_iter_children (tm, &child, iter))
+    {
+      do 
+       {
+         free_file_information_tree_store (tm,
+                                           &child);
+       }
+      while (TRUE == gtk_tree_model_iter_next (tm,
+                                              &child));
+    }
+}
+
+
+/**
+ */
+void
+GNUNET_GTK_main_menu_file_publish_activate_cb (GtkWidget * dummy, 
+                                              gpointer data)
+{
+  GtkWidget *ad;
+  GtkTreeStore *ts;
+  gint ret;
+  GtkTreeView *tv;
+  GtkTreeSelection *sel;
+  GtkTreeIter iter;
+  struct GNUNET_FS_FileInformation *fi;
+  GtkTreeModel *tm;
+  GtkTreeModel *ptm;
+  struct GNUNET_FS_Namespace *namespace;
+  gchar *namespace_id;
+  gchar *namespace_uid;
+  
+  GNUNET_assert (master_builder == NULL);
+  master_builder = GNUNET_GTK_get_new_builder ("publish_dialog.glade");
+  if (master_builder == NULL)
+    return;
+  tv = GTK_TREE_VIEW (gtk_builder_get_object (master_builder,
+                                             
"GNUNET_GTK_master_publish_dialog_file_information_tree_view"));
+  sel = gtk_tree_view_get_selection (tv);
+  g_signal_connect(G_OBJECT(sel), "changed", 
+                  G_CALLBACK(selection_changed_cb), NULL); 
+  ad = GTK_WIDGET (gtk_builder_get_object (master_builder,
+                                          "GNUNET_GTK_master_publish_dialog"));
+  ts = GTK_TREE_STORE (gtk_builder_get_object (master_builder,
+                                              
"GNUNET_GTK_pseudonym_tree_store"));
+  GNUNET_FS_namespace_list (GNUNET_FS_GTK_get_fs_handle (),
+                           &add_namespace_to_ts,
+                           ts);
+  tm = GTK_TREE_MODEL (gtk_builder_get_object (master_builder,
+                                              
"GNUNET_GTK_file_sharing_publishing_tree_store"));
+  tv = GTK_TREE_VIEW (gtk_builder_get_object (master_builder,
+                                             
"GNUNET_GTK_master_publish_dialog_pseudonym_tree_view"));
+  sel = gtk_tree_view_get_selection (tv);
+  g_signal_connect(G_OBJECT(sel), "changed", 
+                  G_CALLBACK(selection_changed_cb), NULL); 
+  ret = gtk_dialog_run (GTK_DIALOG (ad));
+  if (ret == GTK_RESPONSE_OK)
+    {
+      if (TRUE == gtk_tree_selection_get_selected (sel, &ptm, &iter))
+       {
+         gtk_tree_model_get (ptm,
+                             &iter,
+                             1, &namespace,
+                             2, &namespace_id,
+                             5, &namespace_uid,
+                             -1);
+       }
+      else
+       {
+         namespace = NULL;
+         namespace_id = NULL;
+         namespace_uid = NULL;
+       }
+      if (gtk_tree_model_get_iter_first (tm, &iter))
+       do
+         {
+           fi = get_file_information (tm, &iter);
+           GNUNET_FS_publish_start (GNUNET_FS_GTK_get_fs_handle (),
+                                    fi,
+                                    namespace,
+                                    namespace_id,
+                                    namespace_uid,
+                                    GNUNET_FS_PUBLISH_OPTION_NONE);
+         }
+       while (gtk_tree_model_iter_next (tm, &iter));
+      if (namespace_id != NULL)
+       g_free (namespace_id);
+      if (namespace_uid != NULL)
+       g_free  (namespace_uid);
+    }  
+  ptm = GTK_TREE_MODEL (gtk_builder_get_object (master_builder,
+                                               
"GNUNET_GTK_pseudonym_tree_store"));
+  /* free state from 'ptm' */
+  if (TRUE ==
+      gtk_tree_model_get_iter_first (ptm,
+                                    &iter))
+    do 
+      {
+       free_pseudonym_tree_store (ptm,
+                                  &iter);
+      }
+    while (TRUE == gtk_tree_model_iter_next (ptm,
+                                            &iter));
+  
+  /* free state from 'tm' */
+  if (TRUE ==
+      gtk_tree_model_get_iter_first (tm,
+                                    &iter))
+    do 
+      {
+       free_file_information_tree_store (tm,
+                                         &iter);
+      }
+    while (TRUE == gtk_tree_model_iter_next (tm,
+                                            &iter));
+
+  gtk_widget_destroy (ad);
+  g_object_unref (G_OBJECT (master_builder));
+  master_builder = NULL;
+}
+
+
+/* end of main_window_file_publish.c */

Copied: gnunet-gtk/src/fs/main_window_file_search.c (from rev 16478, 
gnunet-gtk/src/main_window_file_search.c)
===================================================================
--- gnunet-gtk/src/fs/main_window_file_search.c                         (rev 0)
+++ gnunet-gtk/src/fs/main_window_file_search.c 2011-08-15 14:11:40 UTC (rev 
16543)
@@ -0,0 +1,243 @@
+/*
+     This file is part of GNUnet
+     (C) 2005, 2006, 2010 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/main_window_file_search.c
+ * @author Christian Grothoff
+ */
+#include "common.h"
+#include "gnunet-fs-gtk.h"
+
+/**
+ * Builder used for the search dialog.
+ */
+static GtkBuilder *builder;
+
+
+/**
+ * User double-clicked on namespace or pressed enter;
+ * move namespace root to the 'keywords' line.
+ */
+void 
+GNUNET_GTK_search_dialog_namespace_tree_view_row_activated_cb (GtkTreeView     
  *tree_view,
+                                                              GtkTreePath      
 *path,
+                                                              
GtkTreeViewColumn *column,
+                                                              gpointer         
  user_data)
+{
+  GtkTreeModel *ls;
+  GtkTreeSelection *sel;
+  GtkEntry *query;
+  GtkTreeIter iter;
+  char *root;
+
+  query = GTK_ENTRY (gtk_builder_get_object (builder,
+                                            
"GNUNET_GTK_search_dialog_keyword_entry"));
+  sel = gtk_tree_view_get_selection (tree_view); 
+  if (TRUE ==
+      gtk_tree_selection_get_selected (sel,
+                                      &ls,
+                                      &iter))
+    {
+      gtk_tree_model_get (ls, &iter,
+                         1, &root,
+                         -1);
+      gtk_entry_set_text (query,
+                         root);
+      GNUNET_free (root);
+    }
+}
+
+
+void
+GNUNET_GTK_search_dialog_search_button_clicked_cb (GtkWidget * dummy, 
+                                                  gpointer data)
+{
+  GtkEntry *query;
+  GtkSpinButton *anonymity;
+  GtkTreeView *namespace;
+  const char *keywords;
+  char *emsg;
+  struct GNUNET_FS_Uri *uri;
+  GNUNET_HashCode *nsid;
+  GtkTreeModel *ls;
+  GtkTreeSelection *sel;
+  GtkTreeIter iter;
+
+  query = GTK_ENTRY (gtk_builder_get_object (builder,
+                                            
"GNUNET_GTK_search_dialog_keyword_entry"));
+  anonymity = GTK_SPIN_BUTTON (gtk_builder_get_object (builder,
+                                                
"GNUNET_GTK_search_dialog_anonymity_spin_button"));
+  namespace = GTK_TREE_VIEW (gtk_builder_get_object (builder,
+                                                    
"GNUNET_GTK_search_dialog_namespace_tree_view"));
+  keywords = gtk_entry_get_text (query);
+  nsid = NULL;
+  sel = gtk_tree_view_get_selection (namespace); 
+  if (TRUE ==
+      gtk_tree_selection_get_selected (sel,
+                                      &ls,
+                                      &iter))
+    gtk_tree_model_get (ls, &iter,
+                       2, &nsid,
+                       -1);
+  if (nsid != NULL)
+    {
+      uri = GNUNET_FS_uri_sks_create_from_nsid (nsid,
+                                               keywords);    
+      GNUNET_assert (uri != NULL);
+    }
+  else    
+    {
+      emsg = NULL;
+      uri = GNUNET_FS_uri_ksk_create (keywords, &emsg);    
+      if (uri == NULL)
+       {
+         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                     _("Invalid keyword string `%s': %s"),
+                     keywords,
+                     emsg);
+         GNUNET_free_non_null (emsg);
+         return;
+       }
+    }
+  GNUNET_FS_search_start (GNUNET_FS_GTK_get_fs_handle (),
+                         uri,
+                         gtk_spin_button_get_value_as_int (anonymity),
+                         GNUNET_FS_SEARCH_OPTION_NONE,
+                         NULL);
+  GNUNET_FS_uri_destroy (uri);
+}
+
+
+
+/**
+ * Add pseudonym data to list store
+ *
+ * @param cls closure (the 'GtkListStore')
+ * @param pseudonym hash code of public key of pseudonym
+ * @param md meta data known about the pseudonym
+ * @param rating the local rating of the pseudonym
+ * @return GNUNET_OK to continue iteration, GNUNET_SYSERR to abort
+ */
+static int
+add_namespace_to_ls (void *cls,
+                    const GNUNET_HashCode *
+                    pseudonym,
+                    const struct
+                    GNUNET_CONTAINER_MetaData * md,
+                    int rating)
+{
+  GtkListStore *ls = cls;
+  char *root;
+  char *ns_name;
+  GNUNET_HashCode *nsid;
+  char *description;
+  char *uris;
+  char *emsg;
+  struct GNUNET_FS_Uri *uri;
+  GtkTreeIter iter;
+  
+  ns_name = GNUNET_PSEUDONYM_id_to_name (GNUNET_FS_GTK_get_configuration (),
+                                        pseudonym);
+  nsid = GNUNET_malloc (sizeof (GNUNET_HashCode));
+  *nsid = *pseudonym;
+  root = NULL;
+  uris = GNUNET_CONTAINER_meta_data_get_by_type (md,
+                                                EXTRACTOR_METATYPE_URI);
+  if (uris != NULL)
+    {
+      emsg = NULL;
+      uri = GNUNET_FS_uri_parse (uris, &emsg);
+      if (uri == NULL)
+       GNUNET_free (emsg);
+      root = GNUNET_FS_uri_sks_get_content_id (uri);
+      GNUNET_FS_uri_destroy (uri);
+    }
+  description = GNUNET_CONTAINER_meta_data_get_first_by_types (md,
+                                                              
EXTRACTOR_METATYPE_TITLE,
+                                                              
EXTRACTOR_METATYPE_BOOK_TITLE,
+                                                              
EXTRACTOR_METATYPE_DESCRIPTION,
+                                                              
EXTRACTOR_METATYPE_SUMMARY,
+                                                              
EXTRACTOR_METATYPE_ALBUM,
+                                                              
EXTRACTOR_METATYPE_COMMENT,
+                                                              
EXTRACTOR_METATYPE_SUBJECT,
+                                                              
EXTRACTOR_METATYPE_KEYWORDS,
+                                                              -1);
+  gtk_list_store_insert_with_values (ls,
+                                    &iter,
+                                    G_MAXINT,
+                                    0, ns_name,
+                                    1, root,
+                                    2, nsid,
+                                    3, description,
+                                    -1);
+  GNUNET_free (ns_name);
+  GNUNET_free_non_null (root);
+  GNUNET_free_non_null (description);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Search selected in 'file' menu.
+ */
+void
+GNUNET_GTK_main_menu_file_search_activate_cb (GtkWidget * dummy, 
+                                             gpointer data)
+{
+  GtkWidget *ad;
+  GtkListStore *ls;
+  GtkTreeIter iter;
+  GNUNET_HashCode *nsid;
+  
+  GNUNET_assert (builder == NULL);
+  builder = GNUNET_GTK_get_new_builder ("search_dialog.glade");
+  if (builder == NULL)
+    return;
+  ad = GTK_WIDGET (gtk_builder_get_object (builder,
+                                          "GNUNET_GTK_search_dialog"));
+  ls = GTK_LIST_STORE (gtk_builder_get_object (builder,
+                                              
"GNUNET_GTK_namespace_list_store"));
+  GNUNET_PSEUDONYM_list_all (GNUNET_FS_GTK_get_configuration (),
+                            &add_namespace_to_ls,
+                            ls);
+  gtk_dialog_run (GTK_DIALOG (ad));
+
+  /* free nsids from 'ls' */
+  if (TRUE ==
+      gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ls), 
+                                    &iter))
+    {
+      do
+       {
+         gtk_tree_model_get (GTK_TREE_MODEL (ls), 
+                             &iter,
+                             2, &nsid,
+                             -1);
+         GNUNET_free (nsid);
+       }
+      while (TRUE == gtk_tree_model_iter_next (GTK_TREE_MODEL (ls), 
+                                              &iter));
+    }
+  gtk_widget_destroy (ad);
+  g_object_unref (G_OBJECT (builder));
+  builder = NULL;
+}
+
+/* end of main_window_file_search.c */

Copied: gnunet-gtk/src/fs/main_window_open_directory.c (from rev 16478, 
gnunet-gtk/src/main_window_open_directory.c)
===================================================================
--- gnunet-gtk/src/fs/main_window_open_directory.c                              
(rev 0)
+++ gnunet-gtk/src/fs/main_window_open_directory.c      2011-08-15 14:11:40 UTC 
(rev 16543)
@@ -0,0 +1,147 @@
+/*
+     This file is part of GNUnet
+     (C) 2005, 2006, 2010 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/main_window_open_directory.c
+ * @author Christian Grothoff
+ */
+#include "common.h"
+#include "fs_event_handler.h"
+
+struct AddChildContext
+{
+  const char *filename;
+  GtkTreeStore *ts;
+  struct SearchTab *tab;
+  struct SearchResult *par;
+  GtkTreeRowReference *prr;
+  GtkTreeIter iter; 
+};
+
+
+/**
+ * Function used to process entries in a directory.
+ *
+ * @param cls closure, our 'struct AddChildContext*'
+ * @param filename name of the file in the directory
+ * @param uri URI of the file
+ * @param metadata metadata for the file; metadata for
+ *        the directory if everything else is NULL/zero
+ * @param length length of the available data for the file
+ *           (of type size_t since data must certainly fit
+ *            into memory; if files are larger than size_t
+ *            permits, then they will certainly not be
+ *            embedded with the directory itself).
+ * @param data data available for the file (length bytes)
+ */
+static void 
+add_child (void *cls,
+          const char *filename,
+          const struct GNUNET_FS_Uri *uri,
+          const struct GNUNET_CONTAINER_MetaData *meta,
+          size_t length,
+          const void *data)
+{
+  struct AddChildContext *acc = cls;
+  struct GNUNET_CONTAINER_MetaData *dmeta;
+  GtkTreePath *tp;
+  GtkTreeIter iter;
+
+  if (uri == NULL)
+    {
+      /* directory meta data itself */
+      dmeta = GNUNET_CONTAINER_meta_data_duplicate (meta);
+      GNUNET_CONTAINER_meta_data_insert (dmeta,
+                                        "<user>",
+                                        EXTRACTOR_METATYPE_FILENAME,
+                                        EXTRACTOR_METAFORMAT_C_STRING,
+                                        "text/plain",
+                                        acc->filename,
+                                        strlen (acc->filename) + 1);
+      acc->tab = GNUNET_GTK_add_to_uri_tab (&acc->iter,
+                                           &acc->par,
+                                           dmeta,
+                                           NULL); 
+      tp = gtk_tree_model_get_path (GTK_TREE_MODEL (acc->tab->ts),
+                                   &acc->iter);
+      acc->prr = gtk_tree_row_reference_new (GTK_TREE_MODEL (acc->tab->ts),
+                                            tp);
+      gtk_tree_path_free (tp);
+      acc->ts = acc->tab->ts;
+      GNUNET_CONTAINER_meta_data_destroy (dmeta);
+      return;
+    }
+  if (acc->ts == NULL)
+    return;
+  GNUNET_assert (NULL !=
+                GNUNET_GTK_add_search_result (acc->tab,
+                                              &iter,
+                                              acc->prr,
+                                              uri,
+                                              meta,
+                                              NULL,
+                                              0));
+}
+
+
+/**
+ * User selected "Open directory" in menu.  Display dialog, open
+ * file and then display a new tab with its contents.
+ */
+void
+GNUNET_GTK_main_menu_file_open_gnunet_directory_activate_cb (GtkWidget * 
dummy, 
+                                                            gpointer data)
+{
+  struct AddChildContext acc;
+  GtkWidget *ad;
+  GtkBuilder *builder;
+  char *filename;
+  GtkFileFilter *ff;
+
+  builder = GNUNET_GTK_get_new_builder ("open_directory_dialog.glade");
+  if (builder == NULL)
+    {
+      GNUNET_break (0);
+      return;
+    }
+  ad = GTK_WIDGET (gtk_builder_get_object (builder,
+                                          "GNUNET_GTK_open_directory_dialog"));
+  ff = GTK_FILE_FILTER (gtk_builder_get_object (builder,
+                                               "gnunet_directory_filter"));
+  /* FIXME: some day, write a custom file filter for gnunet-directories... */
+  gtk_file_filter_add_pattern (ff, "*" GNUNET_FS_DIRECTORY_EXT);
+  if (GTK_RESPONSE_OK != gtk_dialog_run (GTK_DIALOG (ad)))
+    {
+      gtk_widget_destroy (ad);
+      g_object_unref (G_OBJECT (builder));
+      return;
+    }
+  filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(ad));
+  gtk_widget_destroy (ad);
+  g_object_unref (G_OBJECT (builder));
+  acc.filename = filename;
+  acc.ts = NULL;
+  GNUNET_FS_GTK_mmap_and_scan (filename,
+                              &add_child,
+                              &acc);
+  g_free (filename);
+}
+
+/* end of main_window_open_directory.c */

Copied: gnunet-gtk/src/fs/main_window_view_toggles.c (from rev 16478, 
gnunet-gtk/src/main_window_view_toggles.c)
===================================================================
--- gnunet-gtk/src/fs/main_window_view_toggles.c                                
(rev 0)
+++ gnunet-gtk/src/fs/main_window_view_toggles.c        2011-08-15 14:11:40 UTC 
(rev 16543)
@@ -0,0 +1,90 @@
+/*
+     This file is part of GNUnet
+     (C) 2005, 2006, 2010 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/main_window_view_toggles.c
+ * @author Christian Grothoff
+ *
+ * This file contains callbacks for the 'view' menu that toggle views.
+ */
+#include "common.h"
+#include "gnunet-fs-gtk.h"
+
+
+/**
+ * Toggle the visibility of a widget based on the checkeness
+ * of a menu item.
+ *
+ * @param toggled_widget name of widget to toggle
+ * @param toggle_menu name of menu entry
+ */
+static void
+toggle_view (const char *toggled_widget,
+            const char *toggle_menu)
+{
+  GtkCheckMenuItem *mi;
+  GtkWidget *widget;
+  
+  widget = GTK_WIDGET (GNUNET_FS_GTK_get_main_window_object (toggled_widget));
+  mi = GTK_CHECK_MENU_ITEM (GNUNET_FS_GTK_get_main_window_object 
(toggle_menu));
+  if (gtk_check_menu_item_get_active (mi))
+    gtk_widget_show (widget);
+  else
+    gtk_widget_hide (widget);
+}
+
+
+/**
+ * Preview view is toggled.
+ */
+void
+GNUNET_GTK_main_menu_view_preview_toggled_cb (GtkWidget * dummy, 
+                                                gpointer data)
+{
+  toggle_view ("GNUNET_GTK_main_window_preview_image",
+              "GNUNET_GTK_main_menu_view_preview");
+}
+
+
+/**
+ * Neighbour view is toggled.
+ */
+void
+GNUNET_GTK_main_menu_view_neighbours_toggled_cb (GtkWidget * dummy, 
+                                                gpointer data)
+{
+  toggle_view ("GNUNET_GTK_main_window_peerinfo_treeview",
+              "GNUNET_GTK_main_menu_view_neighbours");
+}
+
+
+/**
+ * Metadata view is toggled.
+ */
+void
+GNUNET_GTK_main_menu_view_metadata_toggled_cb (GtkWidget * dummy, 
+                                              gpointer data)
+{
+  toggle_view ("GNUNET_GTK_main_window_metadata_treeview",
+              "GNUNET_GTK_main_menu_view_metadata");
+}
+
+
+/* end of main_window_view_toggles.c */




reply via email to

[Prev in Thread] Current Thread [Next in Thread]