/* This file is part of GNUnet. (C) 2005, 2006, 2007 Christian Grothoff (and other contributing authors) GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNUnet; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /** * @file src/plugins/fs/fs.c * @brief main file-sharing code of gnunet-gtk * @author Christian Grothoff */ #include "platform.h" #include #include #include "fs.h" #include "meta.h" #include "download.h" #include "search.h" #include "upload.h" #include "collection.h" #include "namespace.h" struct GNUNET_FSUI_Context *ctx; struct GNUNET_GE_Context *ectx; struct GNUNET_GC_Configuration *cfg; SearchList *search_head; DownloadList *download_head; UploadList *upload_head; GtkListStore *search_summary; GtkTreeStore *download_summary; GtkTreeStore *upload_summary; /** * Last right-click event coordinates in summary. */ static unsigned int last_x; /** * Last right-click event coordinates in summary. */ static unsigned int last_y; void on_updateIntervalComboEntry_changed_fs (GtkWidget * button, GtkWidget * entryBox) { const char *time; GNUNET_Int32Time t; GtkEntry *entry; entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (entryBox))); time = gtk_entry_get_text (entry); if (GNUNET_OK != parseTimeInterval (time, &t)) { gtk_widget_set_sensitive (button, FALSE); } else { gtk_widget_set_sensitive (button, TRUE); } } /** * The selection of the download summary changed. * Update button status. */ void on_anonymity_spin_changed_fs (GtkWidget * w, gpointer dummy) { gint val; GdkColor color; GtkSpinButton *spin; spin = GTK_SPIN_BUTTON (w); if (spin == NULL) { GNUNET_GE_BREAK (NULL, 0); return; } val = gtk_spin_button_get_value_as_int (spin); if (val == 0) { if ((TRUE == gdk_color_parse ("red", &color)) && (TRUE == gdk_colormap_alloc_color (gdk_colormap_get_system (), &color, FALSE, TRUE))) gtk_widget_modify_base (w, GTK_STATE_NORMAL, &color); } else gtk_widget_modify_base (w, GTK_STATE_NORMAL, NULL); } static void * saveEventProcessor (void *cls) { const GNUNET_FSUI_Event *event = cls; void *ret; unsigned int i; ret = NULL; switch (event->type) { /* search events */ case GNUNET_FSUI_search_started: ret = fs_search_started (event->data.SearchStarted.sc.pos, event->data.SearchStarted.searchURI, event->data.SearchStarted.anonymityLevel, 0, NULL, GNUNET_FSUI_ACTIVE); break; case GNUNET_FSUI_search_result: fs_search_result_received (event->data.SearchResult.sc.cctx, &event->data.SearchResult.fi, event->data.SearchResult.searchURI); break; case GNUNET_FSUI_search_aborted: fs_search_aborted (event->data.SearchAborted.sc.cctx); break; case GNUNET_FSUI_search_paused: fs_search_paused (event->data.SearchPaused.sc.cctx); break; case GNUNET_FSUI_search_restarted: fs_search_restarted (event->data.SearchRestarted.sc.cctx); break; case GNUNET_FSUI_search_suspended: fs_search_aborted (event->data.SearchSuspended.sc.cctx); break; case GNUNET_FSUI_search_resumed: ret = fs_search_started (event->data.SearchResumed.sc.pos, event->data.SearchResumed.searchURI, event->data.SearchResumed.anonymityLevel, event->data.SearchResumed.fisSize, event->data.SearchResumed.fis, event->data.SearchResumed.state); for (i = 0; i < event->data.SearchResumed.fisSize; i++) fs_search_update (ret, &event->data.SearchResumed.fis[i], event->data.SearchResumed.availability_rank[i], event->data.SearchResumed.availability_certainty[i], event->data.SearchResumed.applicability_rank[i]); break; case GNUNET_FSUI_search_stopped: fs_search_stopped (event->data.SearchStopped.sc.cctx); break; case GNUNET_FSUI_search_update: fs_search_update (event->data.SearchUpdate.sc.cctx, &event->data.SearchUpdate.fi, event->data.SearchUpdate.availability_rank, event->data.SearchUpdate.availability_certainty, event->data.SearchUpdate.applicability_rank); break; /* download events */ case GNUNET_FSUI_download_aborted: fs_download_aborted (event->data.DownloadAborted.dc.cctx); break; case GNUNET_FSUI_download_error: fs_download_aborted (event->data.DownloadError.dc.cctx); break; case GNUNET_FSUI_download_suspended: fs_download_stopped (event->data.DownloadSuspended.dc.cctx); break; case GNUNET_FSUI_download_progress: fs_download_update (event->data.DownloadProgress.dc.cctx, event->data.DownloadProgress.completed, event->data.DownloadProgress.last_block, event->data.DownloadProgress.last_size); break; case GNUNET_FSUI_download_completed: fs_download_completed (event->data.DownloadCompleted.dc.cctx); break; case GNUNET_FSUI_download_stopped: fs_download_stopped (event->data.DownloadStopped.dc.cctx); break; case GNUNET_FSUI_download_started: ret = fs_download_started (event->data.DownloadStarted.dc.pos, event->data.DownloadStarted.dc.pcctx, event->data.DownloadStarted.dc.sctx, event->data.DownloadStarted.total, event->data.DownloadStarted.anonymityLevel, &event->data.DownloadStarted.fi, event->data.DownloadStarted.filename, 0, GNUNET_get_time (), GNUNET_FSUI_ACTIVE); break; case GNUNET_FSUI_download_resumed: ret = fs_download_started (event->data.DownloadResumed.dc.pos, event->data.DownloadResumed.dc.pcctx, event->data.DownloadResumed.dc.sctx, event->data.DownloadResumed.total, event->data.DownloadResumed.anonymityLevel, &event->data.DownloadResumed.fi, event->data.DownloadResumed.filename, event->data.DownloadResumed.completed, event->data.DownloadResumed.eta, event->data.DownloadResumed.state); break; /* upload events */ case GNUNET_FSUI_upload_progress: fs_upload_update (event->data.UploadProgress.uc.cctx, event->data.UploadProgress.completed, event->data.UploadProgress.total); break; case GNUNET_FSUI_upload_completed: fs_upload_complete (event->data.UploadCompleted.uc.cctx, event->data.UploadCompleted.uri); break; case GNUNET_FSUI_upload_error: fs_upload_error (event->data.UploadError.uc.cctx, event->data.UploadError.message); break; case GNUNET_FSUI_upload_aborted: fs_upload_aborted (event->data.UploadAborted.uc.cctx); break; case GNUNET_FSUI_upload_stopped: fs_upload_stopped (event->data.UploadStopped.uc.cctx); break; case GNUNET_FSUI_upload_suspended: fs_upload_stopped (event->data.UploadSuspended.uc.cctx); break; case GNUNET_FSUI_upload_started: ret = fs_upload_started (event->data.UploadStarted.uc.pos, event->data.UploadStarted.uc.pcctx, event->data.UploadStarted.filename, NULL, event->data.UploadStarted.total, 0, GNUNET_FSUI_ACTIVE); break; case GNUNET_FSUI_upload_resumed: ret = fs_upload_started (event->data.UploadResumed.uc.pos, event->data.UploadResumed.uc.pcctx, event->data.UploadResumed.filename, event->data.UploadResumed.uri, event->data.UploadResumed.total, event->data.UploadResumed.completed, event->data.UploadResumed.state); break; /* TODO: unindex events */ default: GNUNET_GE_BREAK (ectx, 0); GNUNET_GE_LOG (ectx, GNUNET_GE_ERROR, _("Unhandled (unknown) FSUI event: %u.\n"), event->type); break; } return ret; } static void * eventProcessor (void *unused, const GNUNET_FSUI_Event * event) { return GNUNET_GTK_save_call (&saveEventProcessor, (void *) event); } /** * The selection of the upload summary changed. * Update button status. */ static void on_upload_summary_selection_changed (gpointer signal, gpointer cls) { GtkTreeSelection *selection; GtkWidget *button; selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (glade_xml_get_widget (GNUNET_GTK_get_main_glade_XML (), "activeUploadsList"))); button = glade_xml_get_widget (GNUNET_GTK_get_main_glade_XML (), "stopUploadButton"); gtk_widget_set_sensitive (button, gtk_tree_selection_count_selected_rows (selection) > 0); } /** * The selection of the download summary changed. * Update button status. */ static void on_download_summary_selection_changed (gpointer signal, gpointer cls) { GtkTreeSelection *selection; GtkWidget *button; selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (glade_xml_get_widget (GNUNET_GTK_get_main_glade_XML (), "activeDownloadsList"))); button = glade_xml_get_widget (GNUNET_GTK_get_main_glade_XML (), "abortDownloadButton"); gtk_widget_set_sensitive (button, gtk_tree_selection_count_selected_rows (selection) > 0); button = glade_xml_get_widget (GNUNET_GTK_get_main_glade_XML (), "stopDownloadButton"); gtk_widget_set_sensitive (button, gtk_tree_selection_count_selected_rows (selection) > 0); } /** * The selection of the download summary changed. * Update button status. */ static void on_search_summary_selection_changed (gpointer signal, gpointer cls) { GtkTreeSelection *selection; GtkWidget *button; selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (glade_xml_get_widget (GNUNET_GTK_get_main_glade_XML (), "activeSearchesSummary"))); button = glade_xml_get_widget (GNUNET_GTK_get_main_glade_XML (), "cancelSearchButton"); gtk_widget_set_sensitive (button, gtk_tree_selection_count_selected_rows (selection) > 0); button = glade_xml_get_widget (GNUNET_GTK_get_main_glade_XML (), "stopSearchButton"); gtk_widget_set_sensitive (button, gtk_tree_selection_count_selected_rows (selection) > 0); } int on_upload_copy_uri_activate_fs (void *dummy1, GtkWidget *dummy2) { GtkWidget *uploadsList = glade_xml_get_widget (GNUNET_GTK_get_main_glade_XML (), "activeUploadsList"); GtkTreePath *path; GtkTreeIter iter; struct GNUNET_ECRS_URI *uri; char *str; GtkClipboard *clip; path = NULL; if (FALSE == gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW(uploadsList), last_x, last_y, &path, NULL, NULL, NULL)) { GNUNET_GE_BREAK (NULL, 0); return FALSE; } if (FALSE == gtk_tree_model_get_iter (GTK_TREE_MODEL (upload_summary), &iter, path)) { GNUNET_GE_BREAK (NULL, 0); gtk_tree_path_free (path); return FALSE; } gtk_tree_path_free (path); uri = NULL; gtk_tree_model_get (GTK_TREE_MODEL (upload_summary), &iter, UPLOAD_URISTRING, &str, -1); clip = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD); gtk_clipboard_set_text (clip, str, strlen (str)); GNUNET_free (str); return FALSE; } /** * Setup the summary views (in particular the models * and the renderers). */ static void fs_summary_start () { GtkComboBoxEntry *searchCB; GtkWidget *uploadEntry; GtkTreeView *searchList; GtkTreeView *downloadList; GtkTreeView *uploadList; GtkListStore *model; GtkCellRenderer *renderer; GtkTreeViewColumn *column; int col; GladeXML *contextMenuXML; /* keyword list setup */ searchCB = GTK_COMBO_BOX_ENTRY (glade_xml_get_widget (GNUNET_GTK_get_main_glade_XML (), "fssearchKeywordComboBoxEntry")); model = gtk_list_store_new (1, G_TYPE_STRING /* search string */ ); gtk_combo_box_set_model (GTK_COMBO_BOX (searchCB), GTK_TREE_MODEL (model)); gtk_combo_box_entry_set_text_column (searchCB, 0); /* search namespace selection setup */ searchCB = GTK_COMBO_BOX_ENTRY (glade_xml_get_widget (GNUNET_GTK_get_main_glade_XML (), "searchNamespaceComboBoxEntry")); model = gtk_list_store_new (NS_SEARCH_NUM, G_TYPE_STRING, /* what we show */ G_TYPE_STRING, /* GNUNET_EncName of namespace */ G_TYPE_POINTER, /* ECRS MetaData */ G_TYPE_INT); /* Meta-data about namespace */ gtk_combo_box_set_model (GTK_COMBO_BOX (searchCB), GTK_TREE_MODEL (model)); gtk_combo_box_entry_set_text_column (searchCB, NS_SEARCH_DESCRIPTION); /* search summary setup */ searchList = GTK_TREE_VIEW (glade_xml_get_widget (GNUNET_GTK_get_main_glade_XML (), "activeSearchesSummary")); search_summary = gtk_list_store_new (SEARCH_SUMMARY_NUM, G_TYPE_STRING, /* name */ G_TYPE_INT, /* # results */ G_TYPE_POINTER); /* internal: search list */ gtk_tree_view_set_model (searchList, GTK_TREE_MODEL (search_summary)); gtk_tree_selection_set_mode (gtk_tree_view_get_selection (searchList), GTK_SELECTION_MULTIPLE); g_signal_connect_data (gtk_tree_view_get_selection (searchList), "changed", G_CALLBACK (&on_search_summary_selection_changed), NULL, NULL, 0); renderer = gtk_cell_renderer_text_new (); col = gtk_tree_view_insert_column_with_attributes (searchList, -1, _("Query"), renderer, "text", SEARCH_SUMMARY_NAME, NULL); column = gtk_tree_view_get_column (searchList, col - 1); gtk_tree_view_column_set_resizable (column, TRUE); gtk_tree_view_column_set_clickable (column, TRUE); gtk_tree_view_column_set_reorderable (column, TRUE); gtk_tree_view_column_set_sort_column_id (column, SEARCH_SUMMARY_NAME); gtk_tree_view_column_set_resizable (column, TRUE); renderer = gtk_cell_renderer_text_new (); col = gtk_tree_view_insert_column_with_attributes (searchList, -1, _("Results"), renderer, "text", SEARCH_SUMMARY_RESULT_COUNT, NULL); column = gtk_tree_view_get_column (searchList, col - 1); gtk_tree_view_column_set_resizable (column, TRUE); gtk_tree_view_column_set_clickable (column, TRUE); gtk_tree_view_column_set_reorderable (column, TRUE); gtk_tree_view_column_set_sort_column_id (column, SEARCH_SUMMARY_RESULT_COUNT); gtk_tree_view_column_set_resizable (column, TRUE); /* download summary setup */ downloadList = GTK_TREE_VIEW (glade_xml_get_widget (GNUNET_GTK_get_main_glade_XML (), "activeDownloadsList")); download_summary = gtk_tree_store_new (DOWNLOAD_NUM, G_TYPE_STRING, /* name (full-path file name) */ G_TYPE_STRING, /* name (user-friendly name) */ G_TYPE_UINT64, /* size */ G_TYPE_STRING, /* human readable size */ G_TYPE_INT, /* progress */ G_TYPE_STRING, /* uri as string */ G_TYPE_POINTER); /* internal download list ptr */ gtk_tree_view_set_model (downloadList, GTK_TREE_MODEL (download_summary)); gtk_tree_selection_set_mode (gtk_tree_view_get_selection (downloadList), GTK_SELECTION_MULTIPLE); g_signal_connect_data (gtk_tree_view_get_selection (downloadList), "changed", G_CALLBACK (&on_download_summary_selection_changed), NULL, NULL, 0); contextMenuXML = glade_xml_new (GNUNET_GTK_get_glade_filename (), "downloadsContextMenu", PACKAGE_NAME); GNUNET_GTK_connect_glade_with_plugins (contextMenuXML); renderer = gtk_cell_renderer_text_new (); col = gtk_tree_view_insert_column_with_attributes (downloadList, -1, _("Name"), renderer, "text", DOWNLOAD_SHORTNAME, NULL); column = gtk_tree_view_get_column (downloadList, col - 1); gtk_tree_view_column_set_resizable (column, TRUE); gtk_tree_view_column_set_clickable (column, TRUE); gtk_tree_view_column_set_reorderable (column, TRUE); gtk_tree_view_column_set_sort_column_id (column, DOWNLOAD_SHORTNAME); gtk_tree_view_column_set_resizable (column, TRUE); renderer = gtk_cell_renderer_text_new (); g_object_set (renderer, "xalign", 1.00, NULL); col = gtk_tree_view_insert_column_with_attributes (downloadList, -1, _("Size"), renderer, "text", DOWNLOAD_HSIZE, NULL); column = gtk_tree_view_get_column (downloadList, col - 1); gtk_tree_view_column_set_resizable (column, TRUE); gtk_tree_view_column_set_clickable (column, TRUE); gtk_tree_view_column_set_reorderable (column, TRUE); gtk_tree_view_column_set_sort_column_id (column, DOWNLOAD_SIZE); gtk_tree_view_column_set_resizable (column, TRUE); renderer = gtk_cell_renderer_progress_new (); col = gtk_tree_view_insert_column_with_attributes (downloadList, -1, _("Progress"), renderer, "value", DOWNLOAD_PROGRESS, NULL); g_object_set (G_OBJECT (renderer), "width", 400, NULL); column = gtk_tree_view_get_column (downloadList, col - 1); gtk_tree_view_column_set_resizable (column, TRUE); gtk_tree_view_column_set_clickable (column, TRUE); gtk_tree_view_column_set_reorderable (column, TRUE); gtk_tree_view_column_set_sort_column_id (column, DOWNLOAD_PROGRESS); gtk_tree_view_column_set_resizable (column, TRUE); renderer = gtk_cell_renderer_text_new (); col = gtk_tree_view_insert_column_with_attributes (downloadList, -1, _("URI"), renderer, "text", DOWNLOAD_URISTRING, NULL); g_object_set (G_OBJECT (renderer), "wrap-width", 30, "width-chars", 30, "ellipsize", PANGO_ELLIPSIZE_END, NULL); column = gtk_tree_view_get_column (downloadList, col - 1); gtk_tree_view_column_set_resizable (column, TRUE); gtk_tree_view_column_set_reorderable (column, TRUE); gtk_tree_view_column_set_resizable (column, TRUE); /* upload summary setup */ uploadList = GTK_TREE_VIEW (glade_xml_get_widget (GNUNET_GTK_get_main_glade_XML (), "activeUploadsList")); upload_summary = gtk_tree_store_new (UPLOAD_NUM, G_TYPE_STRING, /* filename */ G_TYPE_INT, /* progress */ G_TYPE_STRING, /* URI as string */ G_TYPE_POINTER); /* UploadList */ gtk_tree_view_set_model (uploadList, GTK_TREE_MODEL (upload_summary)); gtk_tree_selection_set_mode (gtk_tree_view_get_selection (uploadList), GTK_SELECTION_MULTIPLE); g_signal_connect_data (gtk_tree_view_get_selection (uploadList), "changed", G_CALLBACK (&on_upload_summary_selection_changed), NULL, NULL, 0); contextMenuXML = glade_xml_new (GNUNET_GTK_get_glade_filename (), "uploadsContextMenu", PACKAGE_NAME); GNUNET_GTK_connect_glade_with_plugins (contextMenuXML); renderer = gtk_cell_renderer_progress_new (); col = gtk_tree_view_insert_column_with_attributes (uploadList, -1, _("Filename"), renderer, "text", UPLOAD_FILENAME, "value", UPLOAD_PROGRESS, NULL); gtk_tree_view_column_set_resizable (gtk_tree_view_get_column (uploadList, col - 1), TRUE); renderer = gtk_cell_renderer_text_new (); col = gtk_tree_view_insert_column_with_attributes (uploadList, -1, _("URI"), renderer, "text", UPLOAD_URISTRING, NULL); gtk_tree_view_column_set_resizable (gtk_tree_view_get_column (uploadList, col - 1), TRUE); /* upload entry setup */ uploadEntry = glade_xml_get_widget (GNUNET_GTK_get_main_glade_XML (), "uploadFilenameComboBoxEntry"); model = gtk_list_store_new (1, G_TYPE_STRING); gtk_combo_box_set_model (GTK_COMBO_BOX (uploadEntry), GTK_TREE_MODEL (model)); gtk_combo_box_entry_set_text_column (GTK_COMBO_BOX_ENTRY (uploadEntry), 0); } /** * Shutdown the summary dialogs. */ static void fs_summary_stop () { struct GNUNET_MetaData *meta; GtkComboBox *searchCB; GtkTreeModel *model; GtkTreeIter iter; searchCB = GTK_COMBO_BOX (glade_xml_get_widget (GNUNET_GTK_get_main_glade_XML (), "searchNamespaceComboBoxEntry")); model = gtk_combo_box_get_model (searchCB); if (gtk_tree_model_get_iter_first (model, &iter)) { do { gtk_tree_model_get (model, &iter, NS_SEARCH_METADATA, &meta, -1); gtk_list_store_set (GTK_LIST_STORE (model), &iter, NS_SEARCH_METADATA, NULL, -1); if (meta != NULL) GNUNET_meta_data_destroy (meta); } while (gtk_list_store_remove (GTK_LIST_STORE (model), &iter)); } } static void init_cron_job (void *arg) { GtkWidget *tab = arg; ctx = GNUNET_FSUI_start (ectx, cfg, "gnunet-gtk", 8, /* FIXME: allow user to configure download parallelism */ GNUNET_YES, &eventProcessor, NULL); GNUNET_GTK_save_call ((GNUNET_ThreadMainFunction) & gtk_widget_show, tab); } void init_fs (struct GNUNET_GE_Context *e, struct GNUNET_GC_Configuration *c) { GtkWidget *tab; struct GNUNET_CronManager *cron; ectx = e; cfg = c; GNUNET_CO_init (ectx, cfg); fs_summary_start (); fs_collection_start (); fs_namespace_start (); tab = glade_xml_get_widget (GNUNET_GTK_get_main_glade_XML (), "fsnotebook"); cron = GNUNET_GTK_get_cron_manager (); GNUNET_cron_add_job (cron, &init_cron_job, 0, 0, tab); } static void * cleanup_save_call (void *arg) { GtkWidget *tab = arg; fs_summary_stop (); fs_namespace_stop (); gtk_widget_hide (tab); return NULL; } void done_fs () { struct GNUNET_CronManager *cron; GtkWidget *tab; tab = glade_xml_get_widget (GNUNET_GTK_get_main_glade_XML (), "fsnotebook"); cron = GNUNET_GTK_get_cron_manager (); GNUNET_cron_del_job (cron, &init_cron_job, 0, tab); if (ctx != NULL) GNUNET_FSUI_stop (ctx); GNUNET_GTK_save_call (&cleanup_save_call, tab); GNUNET_CO_done (); } /* end of fs.c */