summaryrefslogtreecommitdiff
path: root/libslab
diff options
context:
space:
mode:
authorStefano Karapetsas <[email protected]>2012-12-12 15:41:41 +0100
committerStefano Karapetsas <[email protected]>2012-12-12 15:41:41 +0100
commit762dd31acc761e33f05d27857a7b5f2b23f6d917 (patch)
tree3a91cd3f6213a8b8e5b19a73174800b1b03f6b7b /libslab
parent443a918bd4735e674bdec300f6b8eabe8cda24e0 (diff)
downloadmate-control-center-762dd31acc761e33f05d27857a7b5f2b23f6d917.tar.bz2
mate-control-center-762dd31acc761e33f05d27857a7b5f2b23f6d917.tar.xz
re-add previously deleted files into libslab
they are dependencies of gnome-main-menu they are migrated to gsettings
Diffstat (limited to 'libslab')
-rw-r--r--libslab/Makefile.am4
-rw-r--r--libslab/app-shell.c177
-rw-r--r--libslab/app-shell.h4
-rw-r--r--libslab/directory-tile.c667
-rw-r--r--libslab/directory-tile.h61
-rw-r--r--libslab/document-tile.c1129
-rw-r--r--libslab/document-tile.h70
-rw-r--r--libslab/slab.h2
8 files changed, 2112 insertions, 2 deletions
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
@@ -1055,6 +1064,172 @@ generate_launchers (MateMenuTreeDirectory * root_dir, AppShellData * app_data, C
}
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 <glib/gi18n-lib.h>
+#include <string.h>
+#include <gio/gio.h>
+#include <gdk/gdk.h>
+#include <unistd.h>
+
+#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 (_("<b>Open</b>"));
+ 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 <time.h>
+
+#include <libslab/nameplate-tile.h>
+
+#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 <glib/gi18n-lib.h>
+#include <string.h>
+#include <gio/gio.h>
+
+#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 ("<b>%s</b>", 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 &percnt;l and &percnt;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 <time.h>
+
+#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 <libslab/app-shell.h>
#include <libslab/application-tile.h>
#include <libslab/bookmark-agent.h>
+#include <libslab/directory-tile.h>
+#include <libslab/document-tile.h>
#include <libslab/double-click-detector.h>
#include <libslab/mate-utils.h>
#include <libslab/libslab-utils.h>