diff options
Diffstat (limited to 'src/caja-dropbox.c')
-rw-r--r-- | src/caja-dropbox.c | 960 |
1 files changed, 960 insertions, 0 deletions
diff --git a/src/caja-dropbox.c b/src/caja-dropbox.c new file mode 100644 index 0000000..6451015 --- /dev/null +++ b/src/caja-dropbox.c @@ -0,0 +1,960 @@ +/* + * Copyright 2008 Evenflow, Inc. + * + * caja-dropbox.c + * Implements the Caja extension API for Dropbox. + * + * This file is part of caja-dropbox. + * + * caja-dropbox 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 3 of the License, or + * (at your option) any later version. + * + * caja-dropbox 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 caja-dropbox. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> /* for GETTEXT_PACKAGE */ +#endif + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <errno.h> +#include <unistd.h> +#include <ctype.h> + +#include <glib.h> +#include <glib/gprintf.h> +#include <glib-object.h> +#include <gtk/gtk.h> + +#include <libcaja-extension/caja-extension-types.h> +#include <libcaja-extension/caja-menu-provider.h> +#include <libcaja-extension/caja-info-provider.h> + +#include "g-util.h" +#include "dropbox-command-client.h" +#include "caja-dropbox.h" +#include "caja-dropbox-hooks.h" + +static char *emblems[] = {"dropbox-uptodate", "dropbox-syncing", "dropbox-unsyncable"}; +gchar *DEFAULT_EMBLEM_PATHS[2] = { EMBLEMDIR , NULL }; + +gboolean dropbox_use_caja_submenu_workaround; +gboolean dropbox_use_operation_in_progress_workaround; + +static GType dropbox_type = 0; + +/* for old versions of glib */ +#if 0 // Silence Warnings. +static void my_g_hash_table_get_keys_helper(gpointer key, + gpointer value, + GList **ud) { + *ud = g_list_append(*ud, key); +} + +static GList *my_g_hash_table_get_keys(GHashTable *ght) { + GList *list = NULL; + g_hash_table_foreach(ght, (GHFunc) my_g_hash_table_get_keys_helper, &list); + return list; +} +#endif + +/* probably my favorite function */ +static gchar * +canonicalize_path(gchar *path) { + int i, j = 0; + gchar *toret, **cpy, **elts; + + g_assert(path != NULL); + g_assert(path[0] == '/'); + + elts = g_strsplit(path, "/", 0); + cpy = g_new(gchar *, g_strv_length(elts)+1); + cpy[j++] = "/"; + for (i = 0; elts[i] != NULL; i++) { + if (strcmp(elts[i], "..") == 0) { + j--; + } + else if (strcmp(elts[i], ".") != 0 && elts[i][0] != '\0') { + cpy[j++] = elts[i]; + } + } + + cpy[j] = NULL; + toret = g_build_filenamev(cpy); + g_free(cpy); + g_strfreev(elts); + + return toret; +} + +static void +reset_file(CajaFileInfo *file) { + debug("resetting file %p", (void *) file); + caja_file_info_invalidate_extension_info(file); +} + +gboolean +reset_all_files(CajaDropbox *cvs) { + /* Only run this on the main loop or you'll cause problems. */ + + /* this works because you can call a function pointer with + more arguments than it takes */ + g_hash_table_foreach(cvs->obj2filename, (GHFunc) reset_file, NULL); + return FALSE; +} + + +static void +when_file_dies(CajaDropbox *cvs, CajaFileInfo *address) { + gchar *filename; + + filename = g_hash_table_lookup(cvs->obj2filename, address); + + /* we never got a change to view this file */ + if (filename == NULL) { + return; + } + + /* too chatty */ + /* debug("removing %s <-> 0x%p", filename, address); */ + + g_hash_table_remove(cvs->filename2obj, filename); + g_hash_table_remove(cvs->obj2filename, address); +} + +static void +changed_cb(CajaFileInfo *file, CajaDropbox *cvs) { + /* check if this file's path has changed, if so update the hash and invalidate + the file */ + gchar *filename, *pfilename; + gchar *filename2; + gchar *uri; + + uri = caja_file_info_get_uri(file); + pfilename = g_filename_from_uri(uri, NULL, NULL); + filename = pfilename ? canonicalize_path(pfilename) : NULL; + filename2 = g_hash_table_lookup(cvs->obj2filename, file); + + g_free(pfilename); + g_free(uri); + + /* if filename2 is NULL we've never seen this file in update_file_info */ + if (filename2 == NULL) { + g_free(filename); + return; + } + + if (filename == NULL) { + /* A file has moved to offline storage. Lets remove it from our tables. */ + g_object_weak_unref(G_OBJECT(file), (GWeakNotify) when_file_dies, cvs); + g_hash_table_remove(cvs->filename2obj, filename2); + g_hash_table_remove(cvs->obj2filename, file); + g_signal_handlers_disconnect_by_func(file, G_CALLBACK(changed_cb), cvs); + reset_file(file); + return; + } + + /* this is a hack, because caja doesn't do this for us, for some reason + the file's path has changed */ + if (strcmp(filename, filename2) != 0) { + debug("shifty old: %s, new %s", filename2, filename); + + /* gotta do this first, the call after this frees filename2 */ + g_hash_table_remove(cvs->filename2obj, filename2); + + g_hash_table_replace(cvs->obj2filename, file, g_strdup(filename)); + + { + CajaFileInfo *f2; + /* we shouldn't have another mapping from filename to an object */ + f2 = g_hash_table_lookup(cvs->filename2obj, filename); + if (f2 != NULL) { + /* lets fix it if it's true, just remove the mapping */ + g_hash_table_remove(cvs->filename2obj, filename); + g_hash_table_remove(cvs->obj2filename, f2); + } + } + + g_hash_table_insert(cvs->filename2obj, g_strdup(filename), file); + reset_file(file); + } + + g_free(filename); +} + +static CajaOperationResult +caja_dropbox_update_file_info(CajaInfoProvider *provider, + CajaFileInfo *file, + GClosure *update_complete, + CajaOperationHandle **handle) { + CajaDropbox *cvs; + + cvs = CAJA_DROPBOX(provider); + + /* this code adds this file object to our two-way hash of file objects + so we can shell touch these files later */ + { + gchar *pfilename, *uri; + + uri = caja_file_info_get_uri(file); + pfilename = g_filename_from_uri(uri, NULL, NULL); + g_free(uri); + if (pfilename == NULL) { + return CAJA_OPERATION_COMPLETE; + } + else { + int cmp = 0; + gchar *stored_filename; + gchar *filename; + + filename = canonicalize_path(pfilename); + g_free(pfilename); + stored_filename = g_hash_table_lookup(cvs->obj2filename, file); + + /* don't worry about the dup checks, gcc is smart enough to optimize this + GCSE ftw */ + if ((stored_filename != NULL && (cmp = strcmp(stored_filename, filename)) != 0) || + stored_filename == NULL) { + + if (stored_filename != NULL && cmp != 0) { + /* this happens when the filename changes name on a file obj + but changed_cb isn't called */ + g_object_weak_unref(G_OBJECT(file), (GWeakNotify) when_file_dies, cvs); + g_hash_table_remove(cvs->obj2filename, file); + g_hash_table_remove(cvs->filename2obj, stored_filename); + g_signal_handlers_disconnect_by_func(file, G_CALLBACK(changed_cb), cvs); + } + else if (stored_filename == NULL) { + CajaFileInfo *f2; + + if ((f2 = g_hash_table_lookup(cvs->filename2obj, filename)) != NULL) { + /* if the filename exists in the filename2obj hash + but the file obj doesn't exist in the obj2filename hash: + + this happens when caja allocates another file object + for a filename without first deleting the original file object + + just remove the association to the older file object, it's obsolete + */ + g_object_weak_unref(G_OBJECT(f2), (GWeakNotify) when_file_dies, cvs); + g_signal_handlers_disconnect_by_func(f2, G_CALLBACK(changed_cb), cvs); + g_hash_table_remove(cvs->filename2obj, filename); + g_hash_table_remove(cvs->obj2filename, f2); + } + } + + /* too chatty */ + /* debug("adding %s <-> 0x%p", filename, file);*/ + g_object_weak_ref(G_OBJECT(file), (GWeakNotify) when_file_dies, cvs); + g_hash_table_insert(cvs->filename2obj, g_strdup(filename), file); + g_hash_table_insert(cvs->obj2filename, file, g_strdup(filename)); + g_signal_connect(file, "changed", G_CALLBACK(changed_cb), cvs); + } + + g_free(filename); + } + } + + if (dropbox_client_is_connected(&(cvs->dc)) == FALSE || + caja_file_info_is_gone(file)) { + return CAJA_OPERATION_COMPLETE; + } + + { + DropboxFileInfoCommand *dfic = g_new0(DropboxFileInfoCommand, 1); + + dfic->cancelled = FALSE; + dfic->provider = provider; + dfic->dc.request_type = GET_FILE_INFO; + dfic->update_complete = g_closure_ref(update_complete); + dfic->file = g_object_ref(file); + + dropbox_command_client_request(&(cvs->dc.dcc), (DropboxCommand *) dfic); + + *handle = (CajaOperationHandle *) dfic; + + return dropbox_use_operation_in_progress_workaround + ? CAJA_OPERATION_COMPLETE + : CAJA_OPERATION_IN_PROGRESS; + } +} + +static void +handle_shell_touch(GHashTable *args, CajaDropbox *cvs) { + gchar **path; + + // debug_enter(); + + if ((path = g_hash_table_lookup(args, "path")) != NULL && + path[0][0] == '/') { + CajaFileInfo *file; + gchar *filename; + + filename = canonicalize_path(path[0]); + + debug("shell touch for %s", filename); + + file = g_hash_table_lookup(cvs->filename2obj, filename); + + if (file != NULL) { + debug("gonna reset %s", filename); + reset_file(file); + } + g_free(filename); + } + + return; +} + +gboolean +caja_dropbox_finish_file_info_command(DropboxFileInfoCommandResponse *dficr) { + + //debug_enter(); + CajaOperationResult result = CAJA_OPERATION_FAILED; + + if (!dficr->dfic->cancelled) { + gchar **status = NULL; + gboolean isdir; + + isdir = caja_file_info_is_directory(dficr->dfic->file) ; + + /* if we have emblems just use them. */ + if (dficr->emblems_response != NULL && + (status = g_hash_table_lookup(dficr->emblems_response, "emblems")) != NULL) { + int i; + for ( i = 0; status[i] != NULL; i++) { + if (status[i][0]) + caja_file_info_add_emblem(dficr->dfic->file, status[i]); + } + result = CAJA_OPERATION_COMPLETE; + } + /* if the file status command went okay */ + else if ((dficr->file_status_response != NULL && + (status = + g_hash_table_lookup(dficr->file_status_response, "status")) != NULL) && + ((isdir == TRUE && + dficr->folder_tag_response != NULL) || isdir == FALSE)) { + gchar **tag = NULL; + + /* set the tag emblem */ + if (isdir && + (tag = g_hash_table_lookup(dficr->folder_tag_response, "tag")) != NULL) { + if (strcmp("public", tag[0]) == 0) { + caja_file_info_add_emblem(dficr->dfic->file, "web"); + } + else if (strcmp("shared", tag[0]) == 0) { + caja_file_info_add_emblem(dficr->dfic->file, "people"); + } + else if (strcmp("photos", tag[0]) == 0) { + caja_file_info_add_emblem(dficr->dfic->file, "photos"); + } + else if (strcmp("sandbox", tag[0]) == 0) { + caja_file_info_add_emblem(dficr->dfic->file, "star"); + } + } + + /* set the status emblem */ + { + int emblem_code = 0; + + if (strcmp("up to date", status[0]) == 0) { + emblem_code = 1; + } + else if (strcmp("syncing", status[0]) == 0) { + emblem_code = 2; + } + else if (strcmp("unsyncable", status[0]) == 0) { + emblem_code = 3; + } + + if (emblem_code > 0) { + /* + debug("%s to %s", emblems[emblem_code-1], + g_filename_from_uri(caja_file_info_get_uri(dficr->dfic->file), + NULL, NULL)); + */ + caja_file_info_add_emblem(dficr->dfic->file, emblems[emblem_code-1]); + } + } + result = CAJA_OPERATION_COMPLETE; + } + } + + /* complete the info request */ + if (!dropbox_use_operation_in_progress_workaround) { + caja_info_provider_update_complete_invoke(dficr->dfic->update_complete, + dficr->dfic->provider, + (CajaOperationHandle*) dficr->dfic, + result); + } + + /* destroy the objects we created */ + if (dficr->file_status_response != NULL) + g_hash_table_unref(dficr->file_status_response); + if (dficr->folder_tag_response != NULL) + g_hash_table_unref(dficr->folder_tag_response); + if (dficr->emblems_response != NULL) + g_hash_table_unref(dficr->emblems_response); + + /* unref the objects we didn't create */ + g_closure_unref(dficr->dfic->update_complete); + g_object_unref(dficr->dfic->file); + + /* now free the structs */ + g_free(dficr->dfic); + g_free(dficr); + + return FALSE; +} + +static void +caja_dropbox_cancel_update(CajaInfoProvider *provider, + CajaOperationHandle *handle) { + DropboxFileInfoCommand *dfic = (DropboxFileInfoCommand *) handle; + dfic->cancelled = TRUE; + return; +} + +static void +menu_item_cb(CajaMenuItem *item, + CajaDropbox *cvs) { + gchar *verb; + GList *files; + DropboxGeneralCommand *dcac; + + dcac = g_new(DropboxGeneralCommand, 1); + + /* maybe these would be better passed in a container + struct used as the userdata pointer, oh well this + is how dave camp does it */ + files = g_object_get_data(G_OBJECT(item), "caja_dropbox_files"); + verb = g_object_get_data(G_OBJECT(item), "caja_dropbox_verb"); + + dcac->dc.request_type = GENERAL_COMMAND; + + /* build the argument list */ + dcac->command_args = g_hash_table_new_full((GHashFunc) g_str_hash, + (GEqualFunc) g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_strfreev); + { + gchar **arglist; + guint i; + GList *li; + + arglist = g_new0(gchar *,g_list_length(files) + 1); + + for (li = files, i = 0; li != NULL; li = g_list_next(li)) { + char *uri = caja_file_info_get_uri(CAJA_FILE_INFO(li->data)); + char *path = g_filename_from_uri(uri, NULL, NULL); + g_free(uri); + if (!path) + continue; + arglist[i] = path; + i++; + } + + g_hash_table_insert(dcac->command_args, + g_strdup("paths"), + arglist); + } + + { + gchar **arglist; + arglist = g_new(gchar *, 2); + arglist[0] = g_strdup(verb); + arglist[1] = NULL; + g_hash_table_insert(dcac->command_args, g_strdup("verb"), arglist); + } + + dcac->command_name = g_strdup("icon_overlay_context_action"); + dcac->handler = NULL; + dcac->handler_ud = NULL; + + dropbox_command_client_request(&(cvs->dc.dcc), (DropboxCommand *) dcac); +} + +static char from_hex(gchar ch) { + return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10; +} + +// decode in --> out, but dont fill more than n chars into out +// returns len of out if thing went well, -1 if n wasn't big enough +// can be used in place (whoa!) +int GhettoURLDecode(gchar* out, gchar* in, int n) { + char *out_initial; + + for(out_initial = out; out-out_initial < n && *in != '\0'; out++) { + if (*in == '%') { + *out = from_hex(in[1]) << 4 | from_hex(in[2]); + in += 3; + } + else { + *out = *in; + in++; + } + } + + if (out-out_initial < n) { + *out = '\0'; + return out-out_initial; + } + return -1; +} + +static int +caja_dropbox_parse_menu(gchar **options, + CajaMenu *menu, + GString *old_action_string, + GList *toret, + CajaMenuProvider *provider, + GList *files) +{ + int ret = 0; + int i; + + for ( i = 0; options[i] != NULL; i++) { + gchar **option_info = g_strsplit(options[i], "~", 3); + /* if this is a valid string */ + if (option_info[0] == NULL || option_info[1] == NULL || + option_info[2] == NULL || option_info[3] != NULL) { + g_strfreev(option_info); + continue; + } + + gchar* item_name = option_info[0]; + gchar* item_inner = option_info[1]; + gchar* verb = option_info[2]; + + GhettoURLDecode(item_name, item_name, strlen(item_name)); + GhettoURLDecode(verb, verb, strlen(verb)); + GhettoURLDecode(item_inner, item_inner, strlen(item_inner)); + + // If the inner section has a menu in it then we create a submenu. The verb will be ignored. + // Otherwise add the verb to our map and add the menu item to the list. + if (strchr(item_inner, '~') != NULL) { + GString *new_action_string = g_string_new(old_action_string->str); + gchar **suboptions = g_strsplit(item_inner, "|", -1); + CajaMenuItem *item; + CajaMenu *submenu = caja_menu_new(); + + g_string_append(new_action_string, item_name); + g_string_append(new_action_string, "::"); + + ret += caja_dropbox_parse_menu(suboptions, submenu, new_action_string, + toret, provider, files); + + item = caja_menu_item_new(new_action_string->str, + item_name, "", NULL); + caja_menu_item_set_submenu(item, submenu); + caja_menu_append_item(menu, item); + + g_strfreev(suboptions); + g_object_unref(item); + g_object_unref(submenu); + g_string_free(new_action_string, TRUE); + } else { + CajaMenuItem *item; + GString *new_action_string = g_string_new(old_action_string->str); + gboolean grayed_out = FALSE; + + g_string_append(new_action_string, verb); + + if (item_name[0] == '!') { + item_name++; + grayed_out = TRUE; + } + + item = caja_menu_item_new(new_action_string->str, item_name, item_inner, NULL); + + caja_menu_append_item(menu, item); + /* add the file metadata to this item */ + g_object_set_data_full (G_OBJECT(item), "caja_dropbox_files", + caja_file_info_list_copy (files), + (GDestroyNotify) caja_file_info_list_free); + /* add the verb metadata */ + g_object_set_data_full (G_OBJECT(item), "caja_dropbox_verb", + g_strdup(verb), + (GDestroyNotify) g_free); + g_signal_connect (item, "activate", G_CALLBACK (menu_item_cb), provider); + + if (grayed_out) { + GValue sensitive = { 0 }; + g_value_init (&sensitive, G_TYPE_BOOLEAN); + g_value_set_boolean (&sensitive, FALSE); + g_object_set_property (G_OBJECT(item), "sensitive", &sensitive); + } + + /* taken from caja-file-repairer (http://repairer.kldp.net/): + * this code is a workaround for a bug of caja + * See: http://bugzilla.gnome.org/show_bug.cgi?id=508878 */ + if (dropbox_use_caja_submenu_workaround) { + toret = g_list_append(toret, item); + } + + g_object_unref(item); + g_string_free(new_action_string, TRUE); + ret++; + } + g_strfreev(option_info); + } + return ret; +} + +static void +get_file_items_callback(GHashTable *response, gpointer ud) +{ + GAsyncQueue *reply_queue = ud; + + /* queue_push doesn't accept NULL as a value so we create an empty hash table + * if we got no response. */ + g_async_queue_push(reply_queue, response ? g_hash_table_ref(response) : + g_hash_table_new((GHashFunc) g_str_hash, (GEqualFunc) g_str_equal)); + g_async_queue_unref(reply_queue); +} + + +static GList * +caja_dropbox_get_file_items(CajaMenuProvider *provider, + GtkWidget *window, + GList *files) +{ + /* + * 1. Convert files to filenames. + */ + int file_count = g_list_length(files); + + if (file_count < 1) + return NULL; + + gchar **paths = g_new0(gchar *, file_count + 1); + int i = 0; + GList* elem; + + for (elem = files; elem; elem = elem->next, i++) { + gchar *uri = caja_file_info_get_uri(elem->data); + gchar *filename_un = uri ? g_filename_from_uri(uri, NULL, NULL) : NULL; + gchar *filename = filename_un ? g_filename_to_utf8(filename_un, -1, NULL, NULL, NULL) : NULL; + + g_free(uri); + g_free(filename_un); + + if (filename == NULL) { + /* oooh, filename wasn't correctly encoded, or isn't a local file. */ + g_strfreev(paths); + return NULL; + } + + paths[i] = filename; + } + + GAsyncQueue *reply_queue = g_async_queue_new_full((GDestroyNotify)g_hash_table_unref); + + /* + * 2. Create a DropboxGeneralCommand to call "icon_overlay_context_options" + */ + + DropboxGeneralCommand *dgc = g_new0(DropboxGeneralCommand, 1); + dgc->dc.request_type = GENERAL_COMMAND; + dgc->command_name = g_strdup("icon_overlay_context_options"); + dgc->command_args = g_hash_table_new_full((GHashFunc) g_str_hash, + (GEqualFunc) g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_strfreev); + g_hash_table_insert(dgc->command_args, g_strdup("paths"), paths); + dgc->handler = get_file_items_callback; + dgc->handler_ud = g_async_queue_ref(reply_queue); + + /* + * 3. Queue it up for the helper thread to run it. + */ + CajaDropbox *cvs = CAJA_DROPBOX(provider); + dropbox_command_client_request(&(cvs->dc.dcc), (DropboxCommand *) dgc); + + GTimeVal gtv; + + /* + * 4. We have to block until it's done because caja expects a reply. But we will + * only block for 50 ms for a reply. + */ + + g_get_current_time(>v); + g_time_val_add(>v, 50000); + + GHashTable *context_options_response = g_async_queue_timed_pop(reply_queue, >v); + g_async_queue_unref(reply_queue); + + if (!context_options_response) { + return NULL; + } + + /* + * 5. Parse the reply. + */ + + char **options = g_hash_table_lookup(context_options_response, "options"); + GList *toret = NULL; + + if (options && *options && **options) { + /* build the menu */ + CajaMenuItem *root_item; + CajaMenu *root_menu; + + root_menu = caja_menu_new(); + root_item = caja_menu_item_new("CajaDropbox::root_item", + "Dropbox", "Dropbox Options", "dropbox"); + + toret = g_list_append(toret, root_item); + GString *action_string = g_string_new("CajaDropbox::"); + + if (!caja_dropbox_parse_menu(options, root_menu, action_string, + toret, provider, files)) { + g_object_unref(toret); + toret = NULL; + } + + caja_menu_item_set_submenu(root_item, root_menu); + + g_string_free(action_string, TRUE); + g_object_unref(root_menu); + } + + g_hash_table_unref(context_options_response); + + return toret; +} + +gboolean +add_emblem_paths(GHashTable* emblem_paths_response) +{ + /* Only run this on the main loop or you'll cause problems. */ + if (!emblem_paths_response) + return FALSE; + + gchar **emblem_paths_list; + int i; + + GtkIconTheme *theme = gtk_icon_theme_get_default(); + + if (emblem_paths_response && + (emblem_paths_list = g_hash_table_lookup(emblem_paths_response, "path"))) { + for (i = 0; emblem_paths_list[i] != NULL; i++) { + if (emblem_paths_list[i][0]) + gtk_icon_theme_append_search_path(theme, emblem_paths_list[i]); + } + } + g_hash_table_unref(emblem_paths_response); + return FALSE; +} + +gboolean +remove_emblem_paths(GHashTable* emblem_paths_response) +{ + /* Only run this on the main loop or you'll cause problems. */ + if (!emblem_paths_response) + return FALSE; + + gchar **emblem_paths_list = g_hash_table_lookup(emblem_paths_response, "path"); + if (!emblem_paths_list) + goto exit; + + // We need to remove the old paths. + GtkIconTheme * icon_theme = gtk_icon_theme_get_default(); + gchar ** paths; + gint path_count; + + gtk_icon_theme_get_search_path(icon_theme, &paths, &path_count); + + gint i, j, out = 0; + gboolean found = FALSE; + for (i = 0; i < path_count; i++) { + gboolean keep = TRUE; + for (j = 0; emblem_paths_list[j] != NULL; j++) { + if (emblem_paths_list[j][0]) { + if (!g_strcmp0(paths[i], emblem_paths_list[j])) { + found = TRUE; + keep = FALSE; + g_free(paths[i]); + break; + } + } + } + if (keep) { + paths[out] = paths[i]; + out++; + } + } + + /* If we found one we need to reset the path to + accomodate the changes */ + if (found) { + paths[out] = NULL; /* Clear the last one */ + gtk_icon_theme_set_search_path(icon_theme, (const gchar **)paths, out); + } + + g_strfreev(paths); +exit: + g_hash_table_unref(emblem_paths_response); + return FALSE; +} + +void get_emblem_paths_cb(GHashTable *emblem_paths_response, CajaDropbox *cvs) +{ + if (!emblem_paths_response) { + emblem_paths_response = g_hash_table_new((GHashFunc) g_str_hash, + (GEqualFunc) g_str_equal); + g_hash_table_insert(emblem_paths_response, "path", DEFAULT_EMBLEM_PATHS); + } else { + /* Increase the ref so that finish_general_command doesn't delete it. */ + g_hash_table_ref(emblem_paths_response); + } + + g_mutex_lock(cvs->emblem_paths_mutex); + if (cvs->emblem_paths) { + g_idle_add((GSourceFunc) remove_emblem_paths, cvs->emblem_paths); + cvs->emblem_paths = NULL; + } + cvs->emblem_paths = emblem_paths_response; + g_mutex_unlock(cvs->emblem_paths_mutex); + + g_idle_add((GSourceFunc) add_emblem_paths, g_hash_table_ref(emblem_paths_response)); + g_idle_add((GSourceFunc) reset_all_files, cvs); +} + +static void +on_connect(CajaDropbox *cvs) { + reset_all_files(cvs); + + dropbox_command_client_send_command(&(cvs->dc.dcc), + (CajaDropboxCommandResponseHandler) get_emblem_paths_cb, + cvs, "get_emblem_paths", NULL); +} + +static void +on_disconnect(CajaDropbox *cvs) { + reset_all_files(cvs); + + g_mutex_lock(cvs->emblem_paths_mutex); + /* This call will free the data too. */ + g_idle_add((GSourceFunc) remove_emblem_paths, cvs->emblem_paths); + cvs->emblem_paths = NULL; + g_mutex_unlock(cvs->emblem_paths_mutex); +} + + +static void +caja_dropbox_menu_provider_iface_init (CajaMenuProviderIface *iface) { + iface->get_file_items = caja_dropbox_get_file_items; + return; +} + +static void +caja_dropbox_info_provider_iface_init (CajaInfoProviderIface *iface) { + iface->update_file_info = caja_dropbox_update_file_info; + iface->cancel_update = caja_dropbox_cancel_update; + return; +} + +static void +caja_dropbox_instance_init (CajaDropbox *cvs) { + cvs->filename2obj = g_hash_table_new_full((GHashFunc) g_str_hash, + (GEqualFunc) g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) NULL); + cvs->obj2filename = g_hash_table_new_full((GHashFunc) g_direct_hash, + (GEqualFunc) g_direct_equal, + (GDestroyNotify) NULL, + (GDestroyNotify) g_free); + cvs->emblem_paths_mutex = g_mutex_new(); + cvs->emblem_paths = NULL; + + /* setup the connection obj*/ + dropbox_client_setup(&(cvs->dc)); + + /* our hooks */ + caja_dropbox_hooks_add(&(cvs->dc.hookserv), "shell_touch", + (DropboxUpdateHook) handle_shell_touch, cvs); + + /* add connection handlers */ + dropbox_client_add_on_connect_hook(&(cvs->dc), + (DropboxClientConnectHook) on_connect, + cvs); + dropbox_client_add_on_disconnect_hook(&(cvs->dc), + (DropboxClientConnectHook) on_disconnect, + cvs); + + /* now start the connection */ + debug("about to start client connection"); + dropbox_client_start(&(cvs->dc)); + + return; +} + +static void +caja_dropbox_class_init (CajaDropboxClass *class) { +} + +static void +caja_dropbox_class_finalize (CajaDropboxClass *class) { + debug("just checking"); + /* kill threads here? */ +} + +GType +caja_dropbox_get_type (void) { + return dropbox_type; +} + +void +caja_dropbox_register_type (GTypeModule *module) { + static const GTypeInfo info = { + sizeof (CajaDropboxClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) caja_dropbox_class_init, + (GClassFinalizeFunc) caja_dropbox_class_finalize, + NULL, + sizeof (CajaDropbox), + 0, + (GInstanceInitFunc) caja_dropbox_instance_init, + }; + + static const GInterfaceInfo menu_provider_iface_info = { + (GInterfaceInitFunc) caja_dropbox_menu_provider_iface_init, + NULL, + NULL + }; + + static const GInterfaceInfo info_provider_iface_info = { + (GInterfaceInitFunc) caja_dropbox_info_provider_iface_init, + NULL, + NULL + }; + + dropbox_type = + g_type_module_register_type(module, + G_TYPE_OBJECT, + "CajaDropbox", + &info, 0); + + g_type_module_add_interface (module, + dropbox_type, + CAJA_TYPE_MENU_PROVIDER, + &menu_provider_iface_info); + + g_type_module_add_interface (module, + dropbox_type, + CAJA_TYPE_INFO_PROVIDER, + &info_provider_iface_info); +} |