From 762dd31acc761e33f05d27857a7b5f2b23f6d917 Mon Sep 17 00:00:00 2001 From: Stefano Karapetsas Date: Wed, 12 Dec 2012 15:41:41 +0100 Subject: re-add previously deleted files into libslab they are dependencies of gnome-main-menu they are migrated to gsettings --- libslab/Makefile.am | 4 + libslab/app-shell.c | 177 +++++++- libslab/app-shell.h | 4 +- libslab/directory-tile.c | 667 +++++++++++++++++++++++++++ libslab/directory-tile.h | 61 +++ libslab/document-tile.c | 1129 ++++++++++++++++++++++++++++++++++++++++++++++ libslab/document-tile.h | 70 +++ libslab/slab.h | 2 + shell/control-center.c | 2 +- 9 files changed, 2113 insertions(+), 3 deletions(-) create mode 100644 libslab/directory-tile.c create mode 100644 libslab/directory-tile.h create mode 100644 libslab/document-tile.c create mode 100644 libslab/document-tile.h diff --git a/libslab/Makefile.am b/libslab/Makefile.am index f9c844fd..00ec6fb0 100644 --- a/libslab/Makefile.am +++ b/libslab/Makefile.am @@ -12,6 +12,8 @@ HEADER_FILES= \ app-shell.h \ application-tile.h \ bookmark-agent.h \ + directory-tile.h \ + document-tile.h \ double-click-detector.h \ mate-utils.h \ libslab-utils.h \ @@ -34,6 +36,8 @@ libslab_la_SOURCES = \ app-shell.c \ application-tile.c \ bookmark-agent.c \ + directory-tile.c \ + document-tile.c \ double-click-detector.c \ mate-utils.c \ libslab-utils.c \ diff --git a/libslab/app-shell.c b/libslab/app-shell.c index 6e9be346..3c33230b 100644 --- a/libslab/app-shell.c +++ b/libslab/app-shell.c @@ -59,6 +59,7 @@ static GtkWidget *create_actions_section (AppShellData * app_data, const gchar * static void generate_category (const char * category, MateMenuTreeDirectory * root_dir, AppShellData * app_data, gboolean recursive); static void generate_launchers (MateMenuTreeDirectory * root_dir, AppShellData * app_data, CategoryData * cat_data, gboolean recursive); +static void generate_new_apps (AppShellData * app_data); static void insert_launcher_into_category (CategoryData * cat_data, MateDesktopItem * desktop_item, AppShellData * app_data); @@ -841,7 +842,7 @@ matemenu_tree_changed_callback (MateMenuTree * old_tree, gpointer user_data) } AppShellData * -appshelldata_new (const gchar * menu_name, GtkIconSize icon_size, gboolean show_tile_generic_name, gboolean exit_on_close) +appshelldata_new (const gchar * menu_name, GtkIconSize icon_size, gboolean show_tile_generic_name, gboolean exit_on_close, gint new_apps_max_items) { AppShellData *app_data = g_new0 (AppShellData, 1); app_data->settings = g_settings_new (CC_SCHEMA); @@ -850,6 +851,11 @@ appshelldata_new (const gchar * menu_name, GtkIconSize icon_size, gboolean show_ app_data->stop_incremental_relayout = TRUE; app_data->show_tile_generic_name = show_tile_generic_name; app_data->exit_on_close = exit_on_close; + if (new_apps_max_items > 0) { + app_data->new_apps = g_new0 (NewAppConfig, 1); + app_data->new_apps->max_items = new_apps_max_items; + app_data->new_apps->name = _("New Applications"); + } return app_data; } @@ -912,6 +918,9 @@ generate_categories (AppShellData * app_data) } matemenu_tree_item_unref (root_dir); + + if (app_data->new_apps && (app_data->new_apps->max_items > 0)) + generate_new_apps (app_data); } static void @@ -1054,6 +1063,172 @@ generate_launchers (MateMenuTreeDirectory * root_dir, AppShellData * app_data, C g_slist_free (contents); } +static void +generate_new_apps (AppShellData * app_data) +{ + GHashTable *all_apps_cache = NULL; + gchar *all_apps; + GError *error = NULL; + gchar *separator = "\n"; + + gchar *all_apps_file_name; + gchar **all_apps_split; + gint x; + gboolean got_new_apps; + CategoryData *new_apps_category = NULL; + GList *categories, *launchers; + GHashTable *new_apps_dups; + + all_apps_file_name = g_build_filename (g_get_user_config_dir (), "mate", "ab-newapps.txt", NULL); + + if (!g_file_get_contents (all_apps_file_name, &all_apps, NULL, &error)) + { + /* If file does not exist, this is the first time this user has run this, create the baseline file */ + GList *categories, *launchers; + GString *gstr; + gchar *dirname; + + g_error_free (error); + error = NULL; + + /* best initial size determined by running on a couple different platforms */ + gstr = g_string_sized_new (10000); + + for (categories = app_data->categories_list; categories; categories = categories->next) + { + CategoryData *data = categories->data; + for (launchers = data->launcher_list; launchers; launchers = launchers->next) + { + Tile *tile = TILE (launchers->data); + MateDesktopItem *item = + application_tile_get_desktop_item (APPLICATION_TILE (tile)); + const gchar *uri = mate_desktop_item_get_location (item); + g_string_append (gstr, uri); + g_string_append (gstr, separator); + } + } + + dirname = g_path_get_dirname (all_apps_file_name); + g_mkdir_with_parents (dirname, 0700); /* creates if does not exist */ + g_free (dirname); + + if (!g_file_set_contents (all_apps_file_name, gstr->str, -1, &error)) + g_warning ("Error setting all apps file:%s\n", error->message); + + g_string_free (gstr, TRUE); + g_free (all_apps_file_name); + return; + } + + all_apps_cache = g_hash_table_new (g_str_hash, g_str_equal); + all_apps_split = g_strsplit (all_apps, separator, -1); + for (x = 0; all_apps_split[x]; x++) + { + g_hash_table_insert (all_apps_cache, all_apps_split[x], all_apps_split[x]); + } + + got_new_apps = FALSE; + new_apps_dups = g_hash_table_new (g_str_hash, g_str_equal); + for (categories = app_data->categories_list; categories; categories = categories->next) + { + CategoryData *cat_data = categories->data; + for (launchers = cat_data->launcher_list; launchers; launchers = launchers->next) + { + Tile *tile = TILE (launchers->data); + MateDesktopItem *item = + application_tile_get_desktop_item (APPLICATION_TILE (tile)); + const gchar *uri = mate_desktop_item_get_location (item); + if (!g_hash_table_lookup (all_apps_cache, uri)) + { + GFile *file; + GFileInfo *info; + long filetime; + + if (g_hash_table_lookup (new_apps_dups, uri)) + { + /* if a desktop file is in 2 or more top level categories, only show it once */ + /* printf("Discarding Newapp duplicate:%s\n", uri); */ + break; + } + g_hash_table_insert (new_apps_dups, (gpointer) uri, (gpointer) uri); + + if (!got_new_apps) + { + new_apps_category = g_new0 (CategoryData, 1); + new_apps_category->category = + g_strdup (app_data->new_apps->name); + app_data->new_apps->garray = + g_array_sized_new (FALSE, TRUE, + sizeof (NewAppData *), + app_data->new_apps->max_items); + + /* should not need this, but a bug in glib does not actually clear the elements until you call this method */ + g_array_set_size (app_data->new_apps->garray, app_data->new_apps->max_items); + got_new_apps = TRUE; + } + + file = g_file_new_for_uri (uri); + info = g_file_query_info (file, + G_FILE_ATTRIBUTE_TIME_MODIFIED, + 0, NULL, NULL); + + if (!info) + { + g_object_unref (file); + g_warning ("Cant get vfs info for %s\n", uri); + return; + } + filetime = (long) g_file_info_get_attribute_uint64 (info, + G_FILE_ATTRIBUTE_TIME_MODIFIED); + g_object_unref (info); + g_object_unref (file); + + for (x = 0; x < app_data->new_apps->max_items; x++) + { + NewAppData *temp_data = (NewAppData *) + g_array_index (app_data->new_apps->garray, NewAppData *, x); + if (!temp_data || filetime > temp_data->time) /* if this slot is empty or we are newer than this slot */ + { + NewAppData *temp = g_new0 (NewAppData, 1); + temp->time = filetime; + temp->item = item; + g_array_insert_val (app_data->new_apps->garray, x, + temp); + break; + } + } + } + } + } + g_hash_table_destroy (new_apps_dups); + g_hash_table_destroy (all_apps_cache); + + if (got_new_apps) + { + for (x = 0; x < app_data->new_apps->max_items; x++) + { + NewAppData *data = + (NewAppData *) g_array_index (app_data->new_apps->garray, + NewAppData *, x); + if (data) + { + insert_launcher_into_category (new_apps_category, data->item, + app_data); + g_free (data); + } + else + break; + } + app_data->categories_list = + g_list_prepend (app_data->categories_list, new_apps_category); + + g_array_free (app_data->new_apps->garray, TRUE); + } + g_free (all_apps); + g_free (all_apps_file_name); + g_strfreev (all_apps_split); +} + static void insert_launcher_into_category (CategoryData * cat_data, MateDesktopItem * desktop_item, AppShellData * app_data) diff --git a/libslab/app-shell.h b/libslab/app-shell.h index d7032c2a..d5f08564 100644 --- a/libslab/app-shell.h +++ b/libslab/app-shell.h @@ -84,6 +84,7 @@ typedef struct _AppShellData SlabSection *selected_group; GtkIconSize icon_size; const gchar *menu_name; + NewAppConfig *new_apps; MateMenuTree *tree; GHashTable *hash; @@ -124,8 +125,9 @@ typedef struct void generate_categories (AppShellData * app_data); +/* If new_apps_max_items is 0 then the new applications category is not created */ AppShellData *appshelldata_new (const gchar * menu_name, - GtkIconSize icon_size, gboolean show_tile_generic_name, gboolean exit_on_close); + GtkIconSize icon_size, gboolean show_tile_generic_name, gboolean exit_on_close, gint new_apps_max_items); void layout_shell (AppShellData * app_data, const gchar * filter_title, const gchar * groups_title, const gchar * actions_title, GSList * actions, diff --git a/libslab/directory-tile.c b/libslab/directory-tile.c new file mode 100644 index 00000000..272d0587 --- /dev/null +++ b/libslab/directory-tile.c @@ -0,0 +1,667 @@ +/* + * This file is part of libtile. + * + * Copyright (c) 2006 Novell, Inc. + * + * Libtile is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Libtile 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 Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libslab; if not, write to the Free Software Foundation, Inc., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "directory-tile.h" +#include "config.h" + +#include +#include +#include +#include +#include + +#include "slab-mate-util.h" +#include "mate-utils.h" +#include "libslab-utils.h" + +#define GNOME_MAIN_MENU_SCHEMA "org.mate.gnome-main-menu.file-area" +#define SETTINGS_FILE_MGR_OPEN_KEY "file-mgr-open-cmd" +#define SETTINGS_SEND_TO_CMD_KEY "file-send-to-cmd" + +#define CAJA_SCHEMA "org.mate.caja.preferences" +#define SETTINGS_ENABLE_DELETE_KEY "enable-delete" +#define SETTINGS_CONFIRM_DELETE_KEY "confirm-trash" + +G_DEFINE_TYPE (DirectoryTile, directory_tile, NAMEPLATE_TILE_TYPE) + +static void directory_tile_finalize (GObject *); +static void directory_tile_style_set (GtkWidget *, GtkStyle *); + +static void directory_tile_private_setup (DirectoryTile *); +static void load_image (DirectoryTile *); + +static GtkWidget *create_header (const gchar *); + +static void header_size_allocate_cb (GtkWidget *, GtkAllocation *, gpointer); + +static void open_with_default_trigger (Tile *, TileEvent *, TileAction *); +static void rename_trigger (Tile *, TileEvent *, TileAction *); +static void move_to_trash_trigger (Tile *, TileEvent *, TileAction *); +static void delete_trigger (Tile *, TileEvent *, TileAction *); +static void send_to_trigger (Tile *, TileEvent *, TileAction *); + +static void rename_entry_activate_cb (GtkEntry *, gpointer); +static gboolean rename_entry_key_release_cb (GtkWidget *, GdkEventKey *, gpointer); +static void settings_enable_delete_cb (GSettings *, gchar *, gpointer); + +static void disown_spawned_child (gpointer); + +typedef struct +{ + gchar *basename; + gchar *mime_type; + gchar *icon_name; + + GtkBin *header_bin; + GAppInfo *default_app; + + gboolean image_is_broken; + + gboolean delete_enabled; + + GSettings *caja_settings; + GSettings *gnome_main_menu_settings; +} DirectoryTilePrivate; + +#define DIRECTORY_TILE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DIRECTORY_TILE_TYPE, DirectoryTilePrivate)) + +static void directory_tile_class_init (DirectoryTileClass *this_class) +{ + GObjectClass *g_obj_class = G_OBJECT_CLASS (this_class); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (this_class); + + g_obj_class->finalize = directory_tile_finalize; + + widget_class->style_set = directory_tile_style_set; + + g_type_class_add_private (this_class, sizeof (DirectoryTilePrivate)); +} + +GtkWidget * +directory_tile_new (const gchar *in_uri, const gchar *title, const gchar *icon_name, const gchar *mime_type) +{ + DirectoryTile *this; + DirectoryTilePrivate *priv; + + gchar *uri; + GtkWidget *image; + GtkWidget *header; + GtkMenu *context_menu; + + GtkContainer *menu_ctnr; + GtkWidget *menu_item; + + TileAction *action; + + gchar *basename; + + gchar *markup; + + AtkObject *accessible; + + gchar *filename; + gchar *tooltip_text; + + + uri = g_strdup (in_uri); + + image = gtk_image_new (); + + if (! title) { + markup = g_path_get_basename (uri); + basename = g_uri_unescape_string (markup, NULL); + g_free (markup); + } + else + basename = g_strdup (title); + + header = create_header (basename); + + filename = g_filename_from_uri (uri, NULL, NULL); + + if (filename) + tooltip_text = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL); + else + tooltip_text = NULL; + + g_free (filename); + + context_menu = GTK_MENU (gtk_menu_new ()); + + this = g_object_new ( + DIRECTORY_TILE_TYPE, + "tile-uri", uri, + "nameplate-image", image, + "nameplate-header", header, + "context-menu", context_menu, + NULL); + gtk_widget_set_tooltip_text (GTK_WIDGET (this), tooltip_text); + + g_free (uri); + if (tooltip_text) + g_free (tooltip_text); + + priv = DIRECTORY_TILE_GET_PRIVATE (this); + priv->basename = g_strdup (basename); + priv->header_bin = GTK_BIN (header); + priv->icon_name = g_strdup (icon_name); + priv->mime_type = g_strdup (mime_type); + + directory_tile_private_setup (this); + + TILE (this)->actions = g_new0 (TileAction *, 6); + TILE (this)->n_actions = 6; + + menu_ctnr = GTK_CONTAINER (TILE (this)->context_menu); + + /* make open with default action */ + + markup = g_markup_printf_escaped (_("Open")); + action = tile_action_new (TILE (this), open_with_default_trigger, markup, TILE_ACTION_OPENS_NEW_WINDOW); + g_free (markup); + + TILE (this)->default_action = action; + + menu_item = GTK_WIDGET (GTK_WIDGET (tile_action_get_menu_item (action))); + + TILE (this)->actions [DIRECTORY_TILE_ACTION_OPEN] = action; + + gtk_container_add (menu_ctnr, menu_item); + + /* insert separator */ + + menu_item = gtk_separator_menu_item_new (); + gtk_container_add (menu_ctnr, menu_item); + + /* make rename action */ + + action = tile_action_new (TILE (this), rename_trigger, _("Rename..."), 0); + TILE (this)->actions[DIRECTORY_TILE_ACTION_RENAME] = action; + + menu_item = GTK_WIDGET (tile_action_get_menu_item (action)); + gtk_container_add (menu_ctnr, menu_item); + + /* make send to action */ + + /* Only allow Send To for local files, ideally this would use something + * equivalent to mate_vfs_uri_is_local, but that method will stat the file and + * that can hang in some conditions. */ + + if (!strncmp (TILE (this)->uri, "file://", 7)) + { + action = tile_action_new (TILE (this), send_to_trigger, _("Send To..."), + TILE_ACTION_OPENS_NEW_WINDOW); + + menu_item = GTK_WIDGET (tile_action_get_menu_item (action)); + } + else + { + action = NULL; + + menu_item = gtk_menu_item_new_with_label (_("Send To...")); + gtk_widget_set_sensitive (menu_item, FALSE); + } + + TILE (this)->actions[DIRECTORY_TILE_ACTION_SEND_TO] = action; + + gtk_container_add (menu_ctnr, menu_item); + + /* insert separator */ + + menu_item = gtk_separator_menu_item_new (); + gtk_container_add (menu_ctnr, menu_item); + + /* make move to trash action */ + + action = tile_action_new (TILE (this), move_to_trash_trigger, _("Move to Trash"), 0); + TILE (this)->actions[DIRECTORY_TILE_ACTION_MOVE_TO_TRASH] = action; + + menu_item = GTK_WIDGET (tile_action_get_menu_item (action)); + gtk_container_add (menu_ctnr, menu_item); + + /* make delete action */ + + if (priv->delete_enabled) + { + action = tile_action_new (TILE (this), delete_trigger, _("Delete"), 0); + TILE (this)->actions[DIRECTORY_TILE_ACTION_DELETE] = action; + + menu_item = GTK_WIDGET (tile_action_get_menu_item (action)); + gtk_container_add (menu_ctnr, menu_item); + } + + gtk_widget_show_all (GTK_WIDGET (TILE (this)->context_menu)); + + load_image (this); + + accessible = gtk_widget_get_accessible (GTK_WIDGET (this)); + if (basename) + atk_object_set_name (accessible, basename); + + g_free (basename); + + return GTK_WIDGET (this); +} + +static void +directory_tile_private_setup (DirectoryTile *tile) +{ + DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (tile); + + if (priv->mime_type) + priv->default_app = g_app_info_get_default_for_type (priv->mime_type, TRUE); + else + priv->default_app = NULL; + + priv->gnome_main_menu_settings = g_settings_new (GNOME_MAIN_MENU_SCHEMA); + priv->caja_settings = g_settings_new (CAJA_SCHEMA); + + priv->delete_enabled = g_settings_get_boolean (priv->caja_settings, SETTINGS_ENABLE_DELETE_KEY); + + g_signal_connect (priv->caja_settings, "changed::" SETTINGS_ENABLE_DELETE_KEY, + G_CALLBACK (settings_enable_delete_cb), tile); +} + +static void +directory_tile_init (DirectoryTile *tile) +{ + DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (tile); + + priv->default_app = NULL; + priv->basename = NULL; + priv->header_bin = NULL; + priv->icon_name = NULL; + priv->mime_type = NULL; + priv->image_is_broken = TRUE; + priv->delete_enabled = FALSE; + priv->gnome_main_menu_settings = NULL; + priv->caja_settings = NULL; +} + +static void +directory_tile_finalize (GObject *g_object) +{ + DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (g_object); + + g_free (priv->basename); + g_free (priv->icon_name); + g_free (priv->mime_type); + + if (priv->default_app) + g_object_unref (priv->default_app); + + g_object_unref (priv->gnome_main_menu_settings); + g_object_unref (priv->caja_settings); + + (* G_OBJECT_CLASS (directory_tile_parent_class)->finalize) (g_object); +} + +static void +directory_tile_style_set (GtkWidget *widget, GtkStyle *prev_style) +{ + load_image (DIRECTORY_TILE (widget)); +} + +static void +load_image (DirectoryTile *tile) +{ + DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (tile); + gchar *icon_name; + + if (priv->icon_name) + icon_name = priv->icon_name; + else + icon_name = "folder"; + + priv->image_is_broken = slab_load_image ( + GTK_IMAGE (NAMEPLATE_TILE (tile)->image), GTK_ICON_SIZE_DND, icon_name); +} + +static GtkWidget * +create_header (const gchar *name) +{ + GtkWidget *header_bin; + GtkWidget *header; + + header = gtk_label_new (name); + gtk_label_set_ellipsize (GTK_LABEL (header), PANGO_ELLIPSIZE_END); + gtk_misc_set_alignment (GTK_MISC (header), 0.0, 0.5); + + header_bin = gtk_alignment_new (0.0, 0.5, 1.0, 0.0); + gtk_container_add (GTK_CONTAINER (header_bin), header); + + g_signal_connect (G_OBJECT (header), "size-allocate", G_CALLBACK (header_size_allocate_cb), + NULL); + + return header_bin; +} + +static void +header_size_allocate_cb (GtkWidget *widget, GtkAllocation *alloc, gpointer user_data) +{ + gtk_widget_set_size_request (widget, alloc->width, -1); +} + +static void +rename_entry_activate_cb (GtkEntry *entry, gpointer user_data) +{ + DirectoryTile *tile = DIRECTORY_TILE (user_data); + DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (tile); + + GFile *src_file; + GFile *dst_file; + + gchar *dirname; + gchar *dst_uri; + gchar *src_path; + + GtkWidget *child; + GtkWidget *header; + + gboolean res; + GError *error = NULL; + + if (strlen (gtk_entry_get_text (entry)) < 1) + return; + + src_file = g_file_new_for_uri (TILE (tile)->uri); + + src_path = g_filename_from_uri (TILE (tile)->uri, NULL, NULL); + dirname = g_path_get_dirname (src_path); + dst_uri = g_build_filename (dirname, gtk_entry_get_text (entry), NULL); + dst_file = g_file_new_for_uri (dst_uri); + + g_free (dirname); + g_free (src_path); + + res = g_file_move (src_file, dst_file, + G_FILE_COPY_NONE, NULL, NULL, NULL, &error); + + if (res) { + g_free (priv->basename); + priv->basename = g_strdup (gtk_entry_get_text (entry)); + } + else { + g_warning ("unable to move [%s] to [%s]: %s\n", TILE (tile)->uri, dst_uri, + error->message); + g_error_free (error); + } + + g_free (dst_uri); + g_object_unref (src_file); + g_object_unref (dst_file); + + header = gtk_label_new (priv->basename); + gtk_misc_set_alignment (GTK_MISC (header), 0.0, 0.5); + + child = gtk_bin_get_child (priv->header_bin); + + if (child) + gtk_widget_destroy (child); + + gtk_container_add (GTK_CONTAINER (priv->header_bin), header); + + gtk_widget_show (header); +} + +static gboolean +rename_entry_key_release_cb (GtkWidget *widget, GdkEventKey *event, gpointer user_data) +{ + return TRUE; +} + +static void +settings_enable_delete_cb (GSettings *settings, gchar *key, gpointer user_data) +{ + Tile *tile = TILE (user_data); + DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (user_data); + + GtkMenuShell *menu; + gboolean delete_enabled; + + TileAction *action; + GtkWidget *menu_item; + + menu = GTK_MENU_SHELL (tile->context_menu); + + delete_enabled = g_settings_get_boolean (settings, key); + + if (delete_enabled == priv->delete_enabled) + return; + + priv->delete_enabled = delete_enabled; + + if (priv->delete_enabled) + { + action = tile_action_new (tile, delete_trigger, _("Delete"), 0); + tile->actions[DIRECTORY_TILE_ACTION_DELETE] = action; + + menu_item = GTK_WIDGET (tile_action_get_menu_item (action)); + gtk_menu_shell_insert (menu, menu_item, 7); + + gtk_widget_show_all (menu_item); + } + else + { + g_object_unref (tile->actions[DIRECTORY_TILE_ACTION_DELETE]); + + tile->actions[DIRECTORY_TILE_ACTION_DELETE] = NULL; + } +} + +static void +rename_trigger (Tile *tile, TileEvent *event, TileAction *action) +{ + DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (tile); + + GtkWidget *child; + GtkWidget *entry; + + + entry = gtk_entry_new (); + gtk_entry_set_text (GTK_ENTRY (entry), priv->basename); + gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1); + + child = gtk_bin_get_child (priv->header_bin); + + if (child) + gtk_widget_destroy (child); + + gtk_container_add (GTK_CONTAINER (priv->header_bin), entry); + + g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (rename_entry_activate_cb), tile); + + g_signal_connect (G_OBJECT (entry), "key_release_event", + G_CALLBACK (rename_entry_key_release_cb), NULL); + + gtk_widget_show (entry); + gtk_widget_grab_focus (entry); +} + +static void +move_to_trash_trigger (Tile *tile, TileEvent *event, TileAction *action) +{ + GFile *src_file; + gboolean res; + GError *error = NULL; + + src_file = g_file_new_for_uri (TILE (tile)->uri); + + res = g_file_trash (src_file, NULL, &error); + if (!res) { + g_warning ("unable to move [%s] to the trash: %s\n", TILE (tile)->uri, + error->message); + g_error_free (error); + } + + g_object_unref (src_file); +} + +static void +delete_trigger (Tile *tile, TileEvent *event, TileAction *action) +{ + DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (tile); + GtkDialog *confirm_dialog; + gint result; + + GFile *src_file; + gboolean res; + GError *error = NULL; + + if (g_settings_get_boolean (priv->caja_settings, SETTINGS_CONFIRM_DELETE_KEY)) { + confirm_dialog = GTK_DIALOG(gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_WARNING, + GTK_BUTTONS_NONE, _("Are you sure you want to permanently delete \"%s\"?"), DIRECTORY_TILE_GET_PRIVATE (tile)->basename)); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG(confirm_dialog), _("If you delete an item, it is permanently lost.")); + + gtk_dialog_add_button (confirm_dialog, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); + gtk_dialog_add_button (confirm_dialog, GTK_STOCK_DELETE, GTK_RESPONSE_YES); + gtk_dialog_set_default_response (GTK_DIALOG (confirm_dialog), GTK_RESPONSE_YES); + + result = gtk_dialog_run (confirm_dialog); + + gtk_widget_destroy (GTK_WIDGET (confirm_dialog)); + + if (result != GTK_RESPONSE_YES) + return; + } + + src_file = g_file_new_for_uri (TILE (tile)->uri); + + res = g_file_delete (src_file, NULL, &error); + + if (!res) { + g_warning ("unable to delete [%s]: %s\n", TILE (tile)->uri, + error->message); + g_error_free (error); + } + + g_object_unref (src_file); +} + +static void +send_to_trigger (Tile *tile, TileEvent *event, TileAction *action) +{ + DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (tile); + gchar *cmd; + gint argc; + gchar **argv_parsed = NULL; + gchar **argv = NULL; + + gchar *path; + gchar *dirname; + gchar *basename; + + GError *error = NULL; + + gint i; + + + cmd = g_settings_get_string (priv->gnome_main_menu_settings, SETTINGS_SEND_TO_CMD_KEY); + + if (! g_shell_parse_argv (cmd, & argc, & argv_parsed, NULL)) + goto exit; + + argv = g_new0 (gchar *, argc + 1); + + path = g_filename_from_uri (tile->uri, NULL, NULL); + dirname = g_path_get_dirname (path); + basename = g_path_get_basename (path); + + for (i = 0; i < argc; ++i) { + if (strstr (argv_parsed [i], "DIRNAME")) + argv [i] = string_replace_once (argv_parsed [i], "DIRNAME", dirname); + else if (strstr (argv_parsed [i], "BASENAME")) + argv [i] = string_replace_once (argv_parsed [i], "BASENAME", basename); + else + argv [i] = g_strdup (argv_parsed [i]); + } + + argv [argc] = NULL; + + g_free (path); + g_free (dirname); + g_free (basename); + + g_spawn_async ( + NULL, argv, NULL, G_SPAWN_SEARCH_PATH, + disown_spawned_child, NULL, NULL, & error); + + if (error) { + cmd = g_strjoinv (" ", argv); + libslab_handle_g_error ( + & error, "%s: can't execute search [%s]\n", G_STRFUNC, cmd); + g_free (cmd); + } + + g_strfreev (argv); + +exit: + + g_free (cmd); + g_strfreev (argv_parsed); +} + +static void +disown_spawned_child (gpointer user_data) +{ + setsid (); + setpgid (0, 0); +} + +static void +open_with_default_trigger (Tile *tile, TileEvent *event, TileAction *action) +{ + DirectoryTilePrivate *priv = DIRECTORY_TILE_GET_PRIVATE (tile); + GList *uris = NULL; + gboolean res; + GdkAppLaunchContext *launch_context; + GError *error = NULL; + + if (priv->default_app) + { + uris = g_list_append (uris, TILE (tile)->uri); + + launch_context = gdk_app_launch_context_new (); + gdk_app_launch_context_set_screen (launch_context, + gtk_widget_get_screen (GTK_WIDGET (tile))); + gdk_app_launch_context_set_timestamp (launch_context, + event->time); + + res = g_app_info_launch_uris (priv->default_app, uris, + G_APP_LAUNCH_CONTEXT (launch_context), + &error); + + if (!res) { + g_warning + ("error: could not launch application with [%s]: %s\n", + TILE (tile)->uri, error->message); + g_error_free (error); + } + + g_list_free (uris); + g_object_unref (launch_context); + } else { + gchar *cmd; + cmd = string_replace_once ( + g_settings_get_string (priv->gnome_main_menu_settings, SETTINGS_FILE_MGR_OPEN_KEY), "FILE_URI", tile->uri); + spawn_process (cmd); + g_free (cmd); + } +} diff --git a/libslab/directory-tile.h b/libslab/directory-tile.h new file mode 100644 index 00000000..374faa2b --- /dev/null +++ b/libslab/directory-tile.h @@ -0,0 +1,61 @@ +/* + * This file is part of libtile. + * + * Copyright (c) 2006 Novell, Inc. + * + * Libtile is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Libtile 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 Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libslab; if not, write to the Free Software Foundation, Inc., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __DIRECTORY_TILE_H__ +#define __DIRECTORY_TILE_H__ + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define DIRECTORY_TILE_TYPE (directory_tile_get_type ()) +#define DIRECTORY_TILE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), DIRECTORY_TILE_TYPE, DirectoryTile)) +#define DIRECTORY_TILE_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), DIRECTORY_TILE_TYPE, DirectoryTileClass)) +#define IS_DIRECTORY_TILE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), DIRECTORY_TILE_TYPE)) +#define IS_DIRECTORY_TILE_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), DIRECTORY_TILE_TYPE)) +#define DIRECTORY_TILE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), DIRECTORY_TILE_TYPE, DirectoryTileClass)) + +typedef struct { + NameplateTile nameplate_tile; +} DirectoryTile; + +typedef struct { + NameplateTileClass nameplate_tile_class; +} DirectoryTileClass; + +#define DIRECTORY_TILE_ACTION_OPEN 0 +#define DIRECTORY_TILE_ACTION_RENAME 1 +#define DIRECTORY_TILE_ACTION_MOVE_TO_TRASH 2 +#define DIRECTORY_TILE_ACTION_DELETE 3 +#define DIRECTORY_TILE_ACTION_SEND_TO 4 + +GType directory_tile_get_type (void); + +GtkWidget *directory_tile_new (const gchar *uri, const gchar *title, const gchar *icon_name, const gchar *mime_type); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libslab/document-tile.c b/libslab/document-tile.c new file mode 100644 index 00000000..b2d934ae --- /dev/null +++ b/libslab/document-tile.c @@ -0,0 +1,1129 @@ +/* + * This file is part of libtile. + * + * Copyright (c) 2006, 2007 Novell, Inc. + * + * Libtile is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Libtile 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 Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libslab; if not, write to the Free Software Foundation, Inc., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "document-tile.h" +#include "config.h" + +#include +#include +#include + +#include "slab-mate-util.h" +#include "mate-utils.h" +#include "libslab-utils.h" +#include "bookmark-agent.h" + +#define GNOME_MAIN_MENU_SCHEMA "org.mate.gnome-main-menu.file-area" +#define SETTINGS_FILE_MGR_OPEN_KEY "file-mgr-open-cmd" +#define SETTINGS_SEND_TO_CMD_KEY "file-send-to-cmd" + +#define CAJA_SCHEMA "org.mate.caja.preferences" +#define SETTINGS_ENABLE_DELETE_KEY "enable-delete" +#define SETTINGS_CONFIRM_DELETE_KEY "confirm-trash" + +G_DEFINE_TYPE (DocumentTile, document_tile, NAMEPLATE_TILE_TYPE) + +static void document_tile_finalize (GObject *); +static void document_tile_style_set (GtkWidget *, GtkStyle *); + +static void document_tile_private_setup (DocumentTile *); +static void load_image (DocumentTile *); + +static GtkWidget *create_header (const gchar *); + +static char *create_subheader_string (time_t date); +static GtkWidget *create_subheader (const gchar *); +static void update_user_list_menu_item (DocumentTile *); + +static void header_size_allocate_cb (GtkWidget *, GtkAllocation *, gpointer); + +static void open_with_default_trigger (Tile *, TileEvent *, TileAction *); +static void open_in_file_manager_trigger (Tile *, TileEvent *, TileAction *); +static void rename_trigger (Tile *, TileEvent *, TileAction *); +static void move_to_trash_trigger (Tile *, TileEvent *, TileAction *); +static void remove_recent_item (Tile *, TileEvent *, TileAction *); +static void purge_recent_items (Tile *, TileEvent *, TileAction *); +static void delete_trigger (Tile *, TileEvent *, TileAction *); +static void user_docs_trigger (Tile *, TileEvent *, TileAction *); +static void send_to_trigger (Tile *, TileEvent *, TileAction *); + +static void rename_entry_activate_cb (GtkEntry *, gpointer); +static gboolean rename_entry_key_release_cb (GtkWidget *, GdkEventKey *, gpointer); + +static void settings_enable_delete_cb (GSettings *, gchar *, gpointer); + +static void agent_notify_cb (GObject *, GParamSpec *, gpointer); + +typedef struct +{ + gchar *basename; + gchar *mime_type; + time_t modified; + + GAppInfo *default_app; + + GtkBin *header_bin; + + gboolean image_is_broken; + gchar * force_icon_name; //show an icon instead of a thumbnail + + gboolean delete_enabled; + + GSettings *caja_settings; + GSettings *gnome_main_menu_settings; + + BookmarkAgent *agent; + BookmarkStoreStatus store_status; + gboolean is_bookmarked; + gulong notify_signal_id; +} DocumentTilePrivate; + +#define DOCUMENT_TILE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DOCUMENT_TILE_TYPE, DocumentTilePrivate)) + +static void document_tile_class_init (DocumentTileClass *this_class) +{ + GObjectClass *g_obj_class = G_OBJECT_CLASS (this_class); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (this_class); + + g_obj_class->finalize = document_tile_finalize; + + widget_class->style_set = document_tile_style_set; + + g_type_class_add_private (this_class, sizeof (DocumentTilePrivate)); +} + +//Use a specific icon instead of a thumbnail. +GtkWidget * +document_tile_new_force_icon (const gchar *in_uri, const gchar *mime_type, time_t modified, const gchar *icon) +{ + DocumentTile *this; + DocumentTilePrivate *priv; + + this = (DocumentTile *) document_tile_new (BOOKMARK_STORE_USER_DOCS, in_uri, mime_type, modified); + priv = DOCUMENT_TILE_GET_PRIVATE (this); + priv->force_icon_name = g_strdup (icon); + return GTK_WIDGET (this); +} + +GtkWidget * +document_tile_new (BookmarkStoreType bookmark_store_type, const gchar *in_uri, const gchar *mime_type, time_t modified) +{ + DocumentTile *this; + DocumentTilePrivate *priv; + + gchar *uri; + GtkWidget *image; + GtkWidget *header; + GtkWidget *subheader; + GtkMenu *context_menu; + + GtkContainer *menu_ctnr; + GtkWidget *menu_item; + + TileAction *action; + + gchar *basename; + + gchar *time_str; + + gchar *markup; + gchar *str; + + AtkObject *accessible; + + GFile * file; + gchar *tooltip_text; + + libslab_checkpoint ("document_tile_new(): start"); + + uri = g_strdup (in_uri); + + image = gtk_image_new (); + + markup = g_path_get_basename (uri); + basename = g_uri_unescape_string (markup, NULL); + g_free (markup); + + header = create_header (basename); + + time_str = create_subheader_string (modified); + subheader = create_subheader (time_str); + + file = g_file_new_for_uri (uri); + tooltip_text = g_file_get_parse_name (file); + g_object_unref (file); + + context_menu = GTK_MENU (gtk_menu_new ()); + + this = g_object_new (DOCUMENT_TILE_TYPE, "tile-uri", uri, "nameplate-image", image, + "nameplate-header", header, "nameplate-subheader", subheader, + "context-menu", context_menu, NULL); + gtk_widget_set_tooltip_text (GTK_WIDGET (this), tooltip_text); + + g_free (uri); + if (tooltip_text) + g_free (tooltip_text); + + priv = DOCUMENT_TILE_GET_PRIVATE (this); + priv->basename = g_strdup (basename); + priv->mime_type = g_strdup (mime_type); + priv->modified = modified; + priv->header_bin = GTK_BIN (header); + priv->agent = bookmark_agent_get_instance (bookmark_store_type); + + document_tile_private_setup (this); + TILE (this)->actions = g_new0 (TileAction *, DOCUMENT_TILE_ACTION_NUM_OF_ACTIONS); + TILE (this)->n_actions = DOCUMENT_TILE_ACTION_NUM_OF_ACTIONS; + + menu_ctnr = GTK_CONTAINER (TILE (this)->context_menu); + + /* make open with default action */ + + if (priv->default_app) { + str = g_strdup_printf (_("Open with \"%s\""), + g_app_info_get_name (priv->default_app)); + markup = g_markup_printf_escaped ("%s", str); + action = tile_action_new (TILE (this), open_with_default_trigger, markup, + TILE_ACTION_OPENS_NEW_WINDOW); + g_free (markup); + g_free (str); + + TILE (this)->default_action = action; + + menu_item = GTK_WIDGET (GTK_WIDGET (tile_action_get_menu_item (action))); + } + else { + action = NULL; + menu_item = gtk_menu_item_new_with_label (_("Open with Default Application")); + gtk_widget_set_sensitive (menu_item, FALSE); + } + + TILE (this)->actions[DOCUMENT_TILE_ACTION_OPEN_WITH_DEFAULT] = action; + + gtk_container_add (menu_ctnr, menu_item); + + /* make open in caja action */ + + action = tile_action_new (TILE (this), open_in_file_manager_trigger, + _("Open in File Manager"), TILE_ACTION_OPENS_NEW_WINDOW); + TILE (this)->actions[DOCUMENT_TILE_ACTION_OPEN_IN_FILE_MANAGER] = action; + + if (!TILE (this)->default_action) + TILE (this)->default_action = action; + + menu_item = GTK_WIDGET (tile_action_get_menu_item (action)); + gtk_container_add (menu_ctnr, menu_item); + + /* insert separator */ + + menu_item = gtk_separator_menu_item_new (); + gtk_container_add (menu_ctnr, menu_item); + + /* make rename action */ + + action = tile_action_new (TILE (this), rename_trigger, _("Rename..."), 0); + TILE (this)->actions[DOCUMENT_TILE_ACTION_RENAME] = action; + + menu_item = GTK_WIDGET (tile_action_get_menu_item (action)); + gtk_container_add (menu_ctnr, menu_item); + + /* make send to action */ + + /* Only allow Send To for local files, ideally this would use something + * equivalent to mate_vfs_uri_is_local, but that method will stat the file and + * that can hang in some conditions. */ + + if (!strncmp (TILE (this)->uri, "file://", 7)) + { + action = tile_action_new (TILE (this), send_to_trigger, _("Send To..."), + TILE_ACTION_OPENS_NEW_WINDOW); + + menu_item = GTK_WIDGET (tile_action_get_menu_item (action)); + } + else + { + action = NULL; + + menu_item = gtk_menu_item_new_with_label (_("Send To...")); + gtk_widget_set_sensitive (menu_item, FALSE); + } + + TILE (this)->actions[DOCUMENT_TILE_ACTION_SEND_TO] = action; + + gtk_container_add (menu_ctnr, menu_item); + + /* make "add/remove to favorites" action */ + + action = tile_action_new (TILE (this), user_docs_trigger, NULL, 0); + TILE (this)->actions [DOCUMENT_TILE_ACTION_UPDATE_MAIN_MENU] = action; + + update_user_list_menu_item (this); + + menu_item = GTK_WIDGET (tile_action_get_menu_item (action)); + + gtk_container_add (menu_ctnr, menu_item); + + /* insert separator */ + + menu_item = gtk_separator_menu_item_new (); + gtk_container_add (menu_ctnr, menu_item); + + /* make move to trash action */ + + action = tile_action_new (TILE (this), move_to_trash_trigger, _("Move to Trash"), 0); + TILE (this)->actions[DOCUMENT_TILE_ACTION_MOVE_TO_TRASH] = action; + + menu_item = GTK_WIDGET (tile_action_get_menu_item (action)); + gtk_container_add (menu_ctnr, menu_item); + + /* make delete action */ + + if (priv->delete_enabled) + { + action = tile_action_new (TILE (this), delete_trigger, _("Delete"), 0); + TILE (this)->actions[DOCUMENT_TILE_ACTION_DELETE] = action; + + menu_item = GTK_WIDGET (tile_action_get_menu_item (action)); + gtk_container_add (menu_ctnr, menu_item); + } + + if (!priv->is_bookmarked) { + /* clean item from menu */ + action = tile_action_new (TILE (this), remove_recent_item, _("Remove from recent menu"), 0); + TILE (this)->actions[DOCUMENT_TILE_ACTION_CLEAN_ITEM] = action; + + menu_item = GTK_WIDGET (tile_action_get_menu_item (action)); + gtk_container_add (menu_ctnr, menu_item); + + /* clean all the items from menu */ + + action = tile_action_new (TILE (this), purge_recent_items, _("Purge all the recent items"), 0); + TILE (this)->actions[DOCUMENT_TILE_ACTION_CLEAN_ALL] = action; + + menu_item = GTK_WIDGET (tile_action_get_menu_item (action)); + gtk_container_add (menu_ctnr, menu_item); + } + + gtk_widget_show_all (GTK_WIDGET (TILE (this)->context_menu)); + + accessible = gtk_widget_get_accessible (GTK_WIDGET (this)); + if (basename) + atk_object_set_name (accessible, basename); + if (time_str) + atk_object_set_description (accessible, time_str); + + g_free (basename); + g_free (time_str); + + libslab_checkpoint ("document_tile_new(): end"); + + return GTK_WIDGET (this); +} + +static void +document_tile_private_setup (DocumentTile *this) +{ + DocumentTilePrivate *priv = DOCUMENT_TILE_GET_PRIVATE (this); + + GFile *file; + GAppInfo *app; + + GError *error = NULL; + + file = g_file_new_for_uri (TILE (this)->uri); + app = g_file_query_default_handler (file, NULL, &error); + priv->default_app = app; + + if (error) + g_error_free (error); + g_object_unref (file); + + priv->gnome_main_menu_settings = g_settings_new (GNOME_MAIN_MENU_SCHEMA); + priv->caja_settings = g_settings_new (CAJA_SCHEMA); + + priv->delete_enabled = g_settings_get_boolean (priv->caja_settings, SETTINGS_ENABLE_DELETE_KEY); + + g_signal_connect (priv->caja_settings, "changed::" SETTINGS_ENABLE_DELETE_KEY, + G_CALLBACK (settings_enable_delete_cb), this); + + priv->notify_signal_id = g_signal_connect ( + G_OBJECT (priv->agent), "notify", G_CALLBACK (agent_notify_cb), this); +} + +static void +document_tile_init (DocumentTile *tile) +{ + DocumentTilePrivate *priv = DOCUMENT_TILE_GET_PRIVATE (tile); + + priv->basename = NULL; + priv->mime_type = NULL; + priv->modified = 0; + + priv->default_app = NULL; + + priv->header_bin = NULL; + + priv->image_is_broken = TRUE; + priv->force_icon_name = NULL; + + priv->delete_enabled = FALSE; + + priv->caja_settings = NULL; + priv->gnome_main_menu_settings = NULL; + + priv->agent = NULL; + priv->store_status = BOOKMARK_STORE_DEFAULT; + priv->is_bookmarked = FALSE; + priv->notify_signal_id = 0; +} + +static void +document_tile_finalize (GObject *g_object) +{ + DocumentTilePrivate *priv = DOCUMENT_TILE_GET_PRIVATE (g_object); + + g_free (priv->basename); + g_free (priv->mime_type); + g_free (priv->force_icon_name); + + if (priv->default_app) + g_object_unref (priv->default_app); + + if (priv->notify_signal_id) + g_signal_handler_disconnect (priv->agent, priv->notify_signal_id); + + g_object_unref (G_OBJECT (priv->agent)); + + g_object_unref (priv->gnome_main_menu_settings); + g_object_unref (priv->caja_settings); + + G_OBJECT_CLASS (document_tile_parent_class)->finalize (g_object); +} + +static void +document_tile_style_set (GtkWidget *widget, GtkStyle *prev_style) +{ + load_image (DOCUMENT_TILE (widget)); +} + +static void +load_image (DocumentTile *tile) +{ + DocumentTilePrivate *priv = DOCUMENT_TILE_GET_PRIVATE (tile); + + gchar *icon_id = NULL; + gboolean free_icon_id = TRUE; + MateDesktopThumbnailFactory *thumbnail_factory; + GIcon *icon; + + libslab_checkpoint ("document-tile.c: load_image(): start for %s", TILE (tile)->uri); + + if (priv->force_icon_name || ! priv->mime_type) { + if (priv->force_icon_name) + icon_id = priv->force_icon_name; + else + icon_id = "text-x-preview"; + free_icon_id = FALSE; + + goto exit; + } + + thumbnail_factory = libslab_thumbnail_factory_get (); + + icon_id = mate_desktop_thumbnail_factory_lookup (thumbnail_factory, TILE (tile)->uri, priv->modified); + + if (! icon_id) { + icon = g_content_type_get_icon (priv->mime_type); + g_object_get (icon, "name", &icon_id, NULL); + + g_object_unref (icon); + } + +exit: + + priv->image_is_broken = slab_load_image ( + GTK_IMAGE (NAMEPLATE_TILE (tile)->image), GTK_ICON_SIZE_DND, icon_id); + + if (free_icon_id && icon_id) + g_free (icon_id); + + libslab_checkpoint ("document-tile.c: load_image(): end"); +} + +/* Next function taken from e-data-server-util.c in evolution-data-server */ +/** + * e_strftime: + * @s: The string array to store the result in. + * @max: The size of array @s. + * @fmt: The formatting to use on @tm. + * @tm: The time value to format. + * + * This function is a wrapper around the strftime(3) function, which + * converts the %l and %k (12h and 24h) format variables if necessary. + * + * Returns: The number of characters placed in @s. + **/ +static size_t +e_strftime(char *s, size_t max, const char *fmt, const struct tm *tm) +{ +#ifdef HAVE_LKSTRFTIME + return strftime(s, max, fmt, tm); +#else + char *c, *ffmt, *ff; + size_t ret; + + ffmt = g_strdup(fmt); + ff = ffmt; + while ((c = strstr(ff, "%l")) != NULL) { + c[1] = 'I'; + ff = c; + } + + ff = ffmt; + while ((c = strstr(ff, "%k")) != NULL) { + c[1] = 'H'; + ff = c; + } + +#ifdef G_OS_WIN32 + /* The Microsoft strftime() doesn't have %e either */ + ff = ffmt; + while ((c = strstr(ff, "%e")) != NULL) { + c[1] = 'd'; + ff = c; + } +#endif + + ret = strftime(s, max, ffmt, tm); + g_free(ffmt); + return ret; +#endif +} + +/* Next two functions taken from e-util.c in evolution */ +/** + * Function to do a last minute fixup of the AM/PM stuff if the locale + * and gettext haven't done it right. Most English speaking countries + * except the USA use the 24 hour clock (UK, Australia etc). However + * since they are English nobody bothers to write a language + * translation (gettext) file. So the locale turns off the AM/PM, but + * gettext does not turn on the 24 hour clock. Leaving a mess. + * + * This routine checks if AM/PM are defined in the locale, if not it + * forces the use of the 24 hour clock. + * + * The function itself is a front end on strftime and takes exactly + * the same arguments. + * + * TODO: Actually remove the '%p' from the fixed up string so that + * there isn't a stray space. + **/ + +static size_t +e_strftime_fix_am_pm(char *s, size_t max, const char *fmt, const struct tm *tm) +{ + char buf[10]; + char *sp; + char *ffmt; + size_t ret; + + if (strstr(fmt, "%p")==NULL && strstr(fmt, "%P")==NULL) { + /* No AM/PM involved - can use the fmt string directly */ + ret=e_strftime(s, max, fmt, tm); + } else { + /* Get the AM/PM symbol from the locale */ + e_strftime (buf, 10, "%p", tm); + + if (buf[0]) { + /** + * AM/PM have been defined in the locale + * so we can use the fmt string directly + **/ + ret=e_strftime(s, max, fmt, tm); + } else { + /** + * No AM/PM defined by locale + * must change to 24 hour clock + **/ + ffmt=g_strdup(fmt); + for (sp=ffmt; (sp=strstr(sp, "%l")); sp++) { + /** + * Maybe this should be 'k', but I have never + * seen a 24 clock actually use that format + **/ + sp[1]='H'; + } + for (sp=ffmt; (sp=strstr(sp, "%I")); sp++) { + sp[1]='H'; + } + ret=e_strftime(s, max, ffmt, tm); + g_free(ffmt); + } + } + + return(ret); +} + +static size_t +e_utf8_strftime_fix_am_pm(char *s, size_t max, const char *fmt, const struct tm *tm) +{ + size_t sz, ret; + char *locale_fmt, *buf; + + locale_fmt = g_locale_from_utf8(fmt, -1, NULL, &sz, NULL); + if (!locale_fmt) + return 0; + + ret = e_strftime_fix_am_pm(s, max, locale_fmt, tm); + if (!ret) { + g_free (locale_fmt); + return 0; + } + + buf = g_locale_to_utf8(s, ret, NULL, &sz, NULL); + if (!buf) { + g_free (locale_fmt); + return 0; + } + + if (sz >= max) { + char *tmp = buf + max - 1; + tmp = g_utf8_find_prev_char(buf, tmp); + if (tmp) + sz = tmp - buf; + else + sz = 0; + } + memcpy(s, buf, sz); + s[sz] = '\0'; + g_free(locale_fmt); + g_free(buf); + return sz; +} + +static char * +create_subheader_string (time_t date) +{ + time_t nowdate = time(NULL); + time_t yesdate; + struct tm then, now, yesterday; + char buf[100]; + gboolean done = FALSE; + + if (date == 0) { + return g_strdup (_("?")); + } + + localtime_r (&date, &then); + localtime_r (&nowdate, &now); + + if (nowdate - date < 60 * 60 * 8 && nowdate > date) { + e_utf8_strftime_fix_am_pm (buf, 100, _("%l:%M %p"), &then); + done = TRUE; + } + + if (!done) { + if (then.tm_mday == now.tm_mday && + then.tm_mon == now.tm_mon && + then.tm_year == now.tm_year) { + e_utf8_strftime_fix_am_pm (buf, 100, _("Today %l:%M %p"), &then); + done = TRUE; + } + } + if (!done) { + yesdate = nowdate - 60 * 60 * 24; + localtime_r (&yesdate, &yesterday); + if (then.tm_mday == yesterday.tm_mday && + then.tm_mon == yesterday.tm_mon && + then.tm_year == yesterday.tm_year) { + e_utf8_strftime_fix_am_pm (buf, 100, _("Yesterday %l:%M %p"), &then); + done = TRUE; + } + } + if (!done) { + int i; + for (i = 2; i < 7; i++) { + yesdate = nowdate - 60 * 60 * 24 * i; + localtime_r (&yesdate, &yesterday); + if (then.tm_mday == yesterday.tm_mday && + then.tm_mon == yesterday.tm_mon && + then.tm_year == yesterday.tm_year) { + e_utf8_strftime_fix_am_pm (buf, 100, _("%a %l:%M %p"), &then); + done = TRUE; + break; + } + } + } + if (!done) { + if (then.tm_year == now.tm_year) { + e_utf8_strftime_fix_am_pm (buf, 100, _("%b %d %l:%M %p"), &then); + } else { + e_utf8_strftime_fix_am_pm (buf, 100, _("%b %d %Y"), &then); + } + } + + return g_strdup (g_strstrip (buf)); +} + +static GtkWidget * +create_header (const gchar *name) +{ + GtkWidget *header_bin; + GtkWidget *header; + + header = gtk_label_new (name); + gtk_label_set_ellipsize (GTK_LABEL (header), PANGO_ELLIPSIZE_END); + gtk_misc_set_alignment (GTK_MISC (header), 0.0, 0.5); + + header_bin = gtk_alignment_new (0.0, 0.5, 1.0, 0.0); + gtk_container_add (GTK_CONTAINER (header_bin), header); + + g_signal_connect (G_OBJECT (header), "size-allocate", G_CALLBACK (header_size_allocate_cb), + NULL); + + return header_bin; +} + +static GtkWidget * +create_subheader (const gchar *desc) +{ + GtkWidget *subheader; + + subheader = gtk_label_new (desc); + gtk_label_set_ellipsize (GTK_LABEL (subheader), PANGO_ELLIPSIZE_END); + gtk_misc_set_alignment (GTK_MISC (subheader), 0.0, 0.5); + gtk_widget_modify_fg (subheader, GTK_STATE_NORMAL, + &subheader->style->fg[GTK_STATE_INSENSITIVE]); + + return subheader; +} + +static void +update_user_list_menu_item (DocumentTile *this) +{ + DocumentTilePrivate *priv = DOCUMENT_TILE_GET_PRIVATE (this); + + TileAction *action; + GtkMenuItem *item; + + + action = TILE (this)->actions [DOCUMENT_TILE_ACTION_UPDATE_MAIN_MENU]; + + if (! action) + return; + + priv->is_bookmarked = bookmark_agent_has_item (bookmark_agent_get_instance (BOOKMARK_STORE_USER_DOCS), TILE (this)->uri); + + if (priv->is_bookmarked) + tile_action_set_menu_item_label (action, _("Remove from Favorites")); + else + tile_action_set_menu_item_label (action, _("Add to Favorites")); + + item = tile_action_get_menu_item (action); + + if (! GTK_IS_MENU_ITEM (item)) + return; + + g_object_get (G_OBJECT (priv->agent), BOOKMARK_AGENT_STORE_STATUS_PROP, & priv->store_status, NULL); + + gtk_widget_set_sensitive (GTK_WIDGET (item), (priv->store_status != BOOKMARK_STORE_DEFAULT_ONLY)); +} + +static void +header_size_allocate_cb (GtkWidget *widget, GtkAllocation *alloc, gpointer user_data) +{ + gtk_widget_set_size_request (widget, alloc->width, -1); +} + +static void +rename_entry_activate_cb (GtkEntry *entry, gpointer user_data) +{ + DocumentTile *tile = DOCUMENT_TILE (user_data); + DocumentTilePrivate *priv = DOCUMENT_TILE_GET_PRIVATE (tile); + + GFile *src_file; + GFile *dst_file; + + char *src_path; + char *dirname; + char *dst_path; + + gboolean res; + GError *error = NULL; + + GtkWidget *child; + GtkWidget *header; + + if (strlen (gtk_entry_get_text (entry)) < 1) + return; + + src_file = g_file_new_for_uri (TILE (tile)->uri); + + src_path = g_filename_from_uri (TILE (tile)->uri, NULL, NULL); + dirname = g_path_get_dirname (src_path); + dst_path = g_build_filename (dirname, gtk_entry_get_text (entry), NULL); + dst_file = g_file_new_for_path (dst_path); + + res = g_file_move (src_file, dst_file, 0, NULL, NULL, NULL, &error); + + if (res) { + char *dst_uri; + + dst_uri = g_file_get_uri (dst_file); + bookmark_agent_move_item (priv->agent, TILE (tile)->uri, dst_uri); + g_free (dst_uri); + + g_free (priv->basename); + priv->basename = g_strdup (gtk_entry_get_text (entry)); + } + else { + g_warning ("unable to move [%s] to [%s]: %s\n", TILE (tile)->uri, + dst_path, error->message); + g_error_free (error); + } + + header = gtk_label_new (priv->basename); + gtk_misc_set_alignment (GTK_MISC (header), 0.0, 0.5); + + child = gtk_bin_get_child (priv->header_bin); + + if (child) + gtk_widget_destroy (child); + + gtk_container_add (GTK_CONTAINER (priv->header_bin), header); + + gtk_widget_show (header); + + g_object_unref (src_file); + g_object_unref (dst_file); + + g_free (dirname); + g_free (dst_path); + g_free (src_path); +} + +static gboolean +rename_entry_key_release_cb (GtkWidget *widget, GdkEventKey *event, gpointer user_data) +{ + return TRUE; +} + +static void +settings_enable_delete_cb (GSettings *settings, gchar *key, gpointer user_data) +{ + Tile *tile = TILE (user_data); + DocumentTilePrivate *priv = DOCUMENT_TILE_GET_PRIVATE (user_data); + + GtkMenuShell *menu; + gboolean delete_enabled; + + TileAction *action; + GtkWidget *menu_item; + + menu = GTK_MENU_SHELL (tile->context_menu); + + delete_enabled = g_settings_get_boolean (settings, key); + + if (delete_enabled == priv->delete_enabled) + return; + + priv->delete_enabled = delete_enabled; + + if (priv->delete_enabled) + { + action = tile_action_new (tile, delete_trigger, _("Delete"), 0); + tile->actions[DOCUMENT_TILE_ACTION_DELETE] = action; + + menu_item = GTK_WIDGET (tile_action_get_menu_item (action)); + gtk_menu_shell_insert (menu, menu_item, 7); + + gtk_widget_show_all (menu_item); + } + else + { + g_object_unref (tile->actions[DOCUMENT_TILE_ACTION_DELETE]); + + tile->actions[DOCUMENT_TILE_ACTION_DELETE] = NULL; + } +} + +static void +open_with_default_trigger (Tile *tile, TileEvent *event, TileAction *action) +{ + DocumentTilePrivate *priv = DOCUMENT_TILE_GET_PRIVATE (tile); + + GList *uris = NULL; + gboolean res; + GdkAppLaunchContext *launch_context; + GError *error = NULL; + + if (priv->default_app) + { + uris = g_list_append (uris, TILE (tile)->uri); + launch_context = gdk_app_launch_context_new (); + gdk_app_launch_context_set_screen (launch_context, + gtk_widget_get_screen (GTK_WIDGET (tile))); + gdk_app_launch_context_set_timestamp (launch_context, + event->time); + + res = g_app_info_launch_uris (priv->default_app, uris, + G_APP_LAUNCH_CONTEXT (launch_context), &error); + + if (!res) { + g_warning + ("error: could not launch application with [%s]: %s\n", + TILE (tile)->uri, error->message); + g_error_free (error); + } + + g_list_free (uris); + g_object_unref (launch_context); + } +} + +static void +open_in_file_manager_trigger (Tile *tile, TileEvent *event, TileAction *action) +{ + DocumentTilePrivate *priv = DOCUMENT_TILE_GET_PRIVATE (tile); + GFile *filename; + GFile *dirname; + gchar *uri; + + gchar *cmd; + + filename = g_file_new_for_uri (TILE (tile)->uri); + dirname = g_file_get_parent (filename); + uri = g_file_get_uri (dirname); + + if (!uri) + g_warning ("error getting dirname for [%s]\n", TILE (tile)->uri); + else + { + cmd = string_replace_once (g_settings_get_string(priv->gnome_main_menu_settings, SETTINGS_FILE_MGR_OPEN_KEY), + "FILE_URI", uri); + spawn_process (cmd); + + g_free (cmd); + } + + g_object_unref (filename); + g_object_unref (dirname); + g_free (uri); +} + +static void +rename_trigger (Tile *tile, TileEvent *event, TileAction *action) +{ + DocumentTilePrivate *priv = DOCUMENT_TILE_GET_PRIVATE (tile); + + GtkWidget *child; + GtkWidget *entry; + + + entry = gtk_entry_new (); + gtk_entry_set_text (GTK_ENTRY (entry), priv->basename); + gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1); + + child = gtk_bin_get_child (priv->header_bin); + + if (child) + gtk_widget_destroy (child); + + gtk_container_add (GTK_CONTAINER (priv->header_bin), entry); + + g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (rename_entry_activate_cb), tile); + + g_signal_connect (G_OBJECT (entry), "key_release_event", + G_CALLBACK (rename_entry_key_release_cb), NULL); + + gtk_widget_show (entry); + gtk_widget_grab_focus (entry); +} + +static void +remove_recent_item (Tile *tile, TileEvent *event, TileAction *action) +{ + DocumentTilePrivate *priv = DOCUMENT_TILE_GET_PRIVATE (tile); + bookmark_agent_remove_item (priv->agent, TILE (tile)->uri); +} + +static void +purge_recent_items (Tile *tile, TileEvent *event, TileAction *action) +{ + DocumentTilePrivate *priv = DOCUMENT_TILE_GET_PRIVATE (tile); + bookmark_agent_purge_items (priv->agent); +} + +static void +move_to_trash_trigger (Tile *tile, TileEvent *event, TileAction *action) +{ + DocumentTilePrivate *priv = DOCUMENT_TILE_GET_PRIVATE (tile); + + GFile *src_file; + gboolean res; + GError *error = NULL; + + src_file = g_file_new_for_uri (TILE (tile)->uri); + + res = g_file_trash (src_file, NULL, &error); + + if (res) + bookmark_agent_remove_item (priv->agent, TILE (tile)->uri); + else { + g_warning ("unable to move [%s] to the trash: %s\n", TILE (tile)->uri, + error->message); + + g_error_free (error); + } + + g_object_unref (src_file); +} + +static void +delete_trigger (Tile *tile, TileEvent *event, TileAction *action) +{ + DocumentTilePrivate *priv = DOCUMENT_TILE_GET_PRIVATE (tile); + + GtkDialog *confirm_dialog; + gint result; + + GFile *src_file; + gboolean res; + GError *error = NULL; + + + if (g_settings_get_boolean (priv->caja_settings, SETTINGS_CONFIRM_DELETE_KEY)) { + confirm_dialog = GTK_DIALOG(gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_WARNING, + GTK_BUTTONS_NONE, _("Are you sure you want to permanently delete \"%s\"?"), priv->basename)); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG(confirm_dialog), _("If you delete an item, it is permanently lost.")); + + gtk_dialog_add_button (confirm_dialog, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); + gtk_dialog_add_button (confirm_dialog, GTK_STOCK_DELETE, GTK_RESPONSE_YES); + gtk_dialog_set_default_response (GTK_DIALOG (confirm_dialog), GTK_RESPONSE_YES); + + result = gtk_dialog_run (confirm_dialog); + + gtk_widget_destroy (GTK_WIDGET (confirm_dialog)); + + if (result != GTK_RESPONSE_YES) + return; + } + + src_file = g_file_new_for_uri (TILE (tile)->uri); + + res = g_file_delete (src_file, NULL, &error); + + if (res) + bookmark_agent_remove_item (priv->agent, TILE (tile)->uri); + else { + g_warning ("unable to delete [%s]: %s\n", TILE (tile)->uri, + error->message); + g_error_free (error); + } + + g_object_unref (src_file); +} + +static void +user_docs_trigger (Tile *tile, TileEvent *event, TileAction *action) +{ + DocumentTile *this = DOCUMENT_TILE (tile); + DocumentTilePrivate *priv = DOCUMENT_TILE_GET_PRIVATE (this); + + BookmarkItem *item; + + + if (priv->is_bookmarked) + bookmark_agent_remove_item (priv->agent, tile->uri); + else { + item = g_new0 (BookmarkItem, 1); + item->uri = tile->uri; + item->mime_type = priv->mime_type; + item->mtime = priv->modified; + if (priv->default_app) { + item->app_name = (gchar *) g_app_info_get_name (priv->default_app); + item->app_exec = (gchar *) g_app_info_get_executable (priv->default_app); + } + + bookmark_agent_add_item (priv->agent, item); + g_free (item); + } + + update_user_list_menu_item (this); +} + +static void +send_to_trigger (Tile *tile, TileEvent *event, TileAction *action) +{ + DocumentTilePrivate *priv = DOCUMENT_TILE_GET_PRIVATE (tile); + gchar *cmd; + gchar **argv; + + gchar *filename; + gchar *dirname; + gchar *basename; + + GError *error = NULL; + + gchar *tmp; + gint i; + + cmd = g_settings_get_string (priv->gnome_main_menu_settings, SETTINGS_SEND_TO_CMD_KEY); + argv = g_strsplit (cmd, " ", 0); + + filename = g_filename_from_uri (TILE (tile)->uri, NULL, NULL); + dirname = g_path_get_dirname (filename); + basename = g_path_get_basename (filename); + + for (i = 0; argv[i]; ++i) + { + if (strstr (argv[i], "DIRNAME")) + { + tmp = string_replace_once (argv[i], "DIRNAME", dirname); + g_free (argv[i]); + argv[i] = tmp; + } + + if (strstr (argv[i], "BASENAME")) + { + tmp = string_replace_once (argv[i], "BASENAME", basename); + g_free (argv[i]); + argv[i] = tmp; + } + } + + gdk_spawn_on_screen (gtk_widget_get_screen (GTK_WIDGET (tile)), NULL, argv, NULL, + G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error); + + if (error) + handle_g_error (&error, "error in %s", G_STRFUNC); + + g_free (cmd); + g_free (filename); + g_free (dirname); + g_free (basename); + g_strfreev (argv); +} + +static void +agent_notify_cb (GObject *g_obj, GParamSpec *pspec, gpointer user_data) +{ + update_user_list_menu_item (DOCUMENT_TILE (user_data)); +} diff --git a/libslab/document-tile.h b/libslab/document-tile.h new file mode 100644 index 00000000..c5f1f1f8 --- /dev/null +++ b/libslab/document-tile.h @@ -0,0 +1,70 @@ +/* + * This file is part of libtile. + * + * Copyright (c) 2006 Novell, Inc. + * + * Libtile is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Libtile 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 Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libslab; if not, write to the Free Software Foundation, Inc., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __DOCUMENT_TILE_H__ +#define __DOCUMENT_TILE_H__ + +#include + +#include "nameplate-tile.h" +#include "bookmark-agent.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define DOCUMENT_TILE_TYPE (document_tile_get_type ()) +#define DOCUMENT_TILE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), DOCUMENT_TILE_TYPE, DocumentTile)) +#define DOCUMENT_TILE_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), DOCUMENT_TILE_TYPE, DocumentTileClass)) +#define IS_DOCUMENT_TILE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), DOCUMENT_TILE_TYPE)) +#define IS_DOCUMENT_TILE_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), DOCUMENT_TILE_TYPE)) +#define DOCUMENT_TILE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), DOCUMENT_TILE_TYPE, DocumentTileClass)) + +typedef struct { + NameplateTile nameplate_tile; +} DocumentTile; + +typedef struct { + NameplateTileClass nameplate_tile_class; +} DocumentTileClass; + +#define DOCUMENT_TILE_ACTION_OPEN_WITH_DEFAULT 0 +#define DOCUMENT_TILE_ACTION_OPEN_IN_FILE_MANAGER 1 +#define DOCUMENT_TILE_ACTION_RENAME 2 +#define DOCUMENT_TILE_ACTION_MOVE_TO_TRASH 3 +#define DOCUMENT_TILE_ACTION_DELETE 4 +#define DOCUMENT_TILE_ACTION_UPDATE_MAIN_MENU 5 +#define DOCUMENT_TILE_ACTION_SEND_TO 6 +#define DOCUMENT_TILE_ACTION_CLEAN_ITEM 7 +#define DOCUMENT_TILE_ACTION_CLEAN_ALL 8 +#define DOCUMENT_TILE_ACTION_NUM_OF_ACTIONS 9 /* must be last entry and equal to the number of actions */ + +GType document_tile_get_type (void); + +GtkWidget *document_tile_new (BookmarkStoreType bookmark_store_type, const gchar *uri, const gchar *mime_type, time_t modified); + +//If you want to show a icon instead of a thumbnail +GtkWidget *document_tile_new_force_icon (const gchar *uri, const gchar *mime_type, time_t modified, const gchar *icon); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libslab/slab.h b/libslab/slab.h index 0cfb363c..cacf7a9c 100644 --- a/libslab/slab.h +++ b/libslab/slab.h @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/shell/control-center.c b/shell/control-center.c index 79dd4a5f..56201b1f 100644 --- a/shell/control-center.c +++ b/shell/control-center.c @@ -194,7 +194,7 @@ int main(int argc, char* argv[]) return retval; } - app_data = appshelldata_new("matecc.menu", GTK_ICON_SIZE_DND, FALSE, TRUE); + app_data = appshelldata_new("matecc.menu", GTK_ICON_SIZE_DND, FALSE, TRUE, 0); generate_categories(app_data); actions = get_actions_list(); -- cgit v1.2.1