summaryrefslogtreecommitdiff
path: root/trashapplet/src/trashapplet.c
diff options
context:
space:
mode:
Diffstat (limited to 'trashapplet/src/trashapplet.c')
-rw-r--r--trashapplet/src/trashapplet.c645
1 files changed, 645 insertions, 0 deletions
diff --git a/trashapplet/src/trashapplet.c b/trashapplet/src/trashapplet.c
new file mode 100644
index 00000000..2294c7ac
--- /dev/null
+++ b/trashapplet/src/trashapplet.c
@@ -0,0 +1,645 @@
+/* -*- mode: c; c-basic-offset: 8 -*-
+ * trashapplet.c
+ *
+ * Copyright (c) 2004 Michiel Sikkes <[email protected]>,
+ * 2004 Emmanuele Bassi <[email protected]>
+ * 2008 Ryan Lortie <[email protected]>
+ * Matthias Clasen <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <mateconf/mateconf-client.h>
+#include <gio/gio.h>
+#include <mate-panel-applet.h>
+
+#include "trash-empty.h"
+#include "xstuff.h"
+
+typedef MatePanelAppletClass TrashAppletClass;
+
+typedef struct
+{
+ MatePanelApplet applet;
+
+ GFileMonitor *trash_monitor;
+ GFile *trash;
+
+ GtkImage *image;
+ GIcon *icon;
+ gint items;
+} TrashApplet;
+
+G_DEFINE_TYPE (TrashApplet, trash_applet, PANEL_TYPE_APPLET);
+#define TRASH_TYPE_APPLET (trash_applet_get_type ())
+#define TRASH_APPLET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+ TRASH_TYPE_APPLET, TrashApplet))
+
+static void trash_applet_do_empty (GtkAction *action,
+ TrashApplet *applet);
+static void trash_applet_show_about (GtkAction *action,
+ TrashApplet *applet);
+static void trash_applet_open_folder (GtkAction *action,
+ TrashApplet *applet);
+static void trash_applet_show_help (GtkAction *action,
+ TrashApplet *applet);
+
+static const GtkActionEntry trash_applet_menu_actions [] = {
+ { "EmptyTrash", GTK_STOCK_CLEAR, N_("_Empty Trash"),
+ NULL, NULL,
+ G_CALLBACK (trash_applet_do_empty) },
+ { "OpenTrash", GTK_STOCK_OPEN, N_("_Open Trash"),
+ NULL, NULL,
+ G_CALLBACK (trash_applet_open_folder) },
+ { "HelpTrash", GTK_STOCK_HELP, N_("_Help"),
+ NULL, NULL,
+ G_CALLBACK (trash_applet_show_help) },
+ { "AboutTrash", GTK_STOCK_ABOUT, N_("_About"),
+ NULL, NULL,
+ G_CALLBACK (trash_applet_show_about) }
+};
+
+static void
+trash_applet_monitor_changed (TrashApplet *applet)
+{
+ GError *error = NULL;
+ GFileInfo *info;
+ GIcon *icon;
+ gint items;
+
+ info = g_file_query_info (applet->trash,
+ G_FILE_ATTRIBUTE_STANDARD_ICON","
+ G_FILE_ATTRIBUTE_TRASH_ITEM_COUNT,
+ 0, NULL, &error);
+
+ if (!info)
+ {
+ g_critical ("could not query trash:/: '%s'", error->message);
+ g_error_free (error);
+
+ return;
+ }
+
+ icon = g_file_info_get_icon (info);
+ items = g_file_info_get_attribute_uint32 (info,
+ G_FILE_ATTRIBUTE_TRASH_ITEM_COUNT);
+
+ if (!g_icon_equal (icon, applet->icon))
+ {
+ /* note: the size is meaningless here,
+ * since we do set_pixel_size() later
+ */
+ gtk_image_set_from_gicon (GTK_IMAGE (applet->image),
+ icon, GTK_ICON_SIZE_MENU);
+
+ if (applet->icon)
+ g_object_unref (applet->icon);
+
+ applet->icon = g_object_ref (icon);
+ }
+
+ if (items != applet->items)
+ {
+ if (items)
+ {
+ char *text;
+
+ text = g_strdup_printf (ngettext ("%d Item in Trash",
+ "%d Items in Trash",
+ items), items);
+ gtk_widget_set_tooltip_text (GTK_WIDGET (applet), text);
+ g_free (text);
+ }
+ else
+ gtk_widget_set_tooltip_text (GTK_WIDGET (applet),
+ _("No Items in Trash"));
+
+ applet->items = items;
+ }
+
+ g_object_unref (info);
+}
+
+static void
+trash_applet_set_icon_size (TrashApplet *applet,
+ gint size)
+{
+ /* copied from button-widget.c in the panel */
+ if (size < 22)
+ size = 16;
+ else if (size < 24)
+ size = 22;
+ else if (size < 32)
+ size = 24;
+ else if (size < 48)
+ size = 32;
+ else
+ size = 48;
+
+ /* GtkImage already contains a check to do nothing if it's the same */
+ gtk_image_set_pixel_size (applet->image, size);
+}
+
+static void
+trash_applet_size_allocate (GtkWidget *widget,
+ GdkRectangle *allocation)
+{
+ TrashApplet *applet = TRASH_APPLET (widget);
+
+ switch (mate_panel_applet_get_orient (MATE_PANEL_APPLET (applet)))
+ {
+ case MATE_PANEL_APPLET_ORIENT_LEFT:
+ case MATE_PANEL_APPLET_ORIENT_RIGHT:
+ trash_applet_set_icon_size (applet, allocation->width);
+ break;
+
+ case MATE_PANEL_APPLET_ORIENT_UP:
+ case MATE_PANEL_APPLET_ORIENT_DOWN:
+ trash_applet_set_icon_size (applet, allocation->height);
+ break;
+ }
+
+ GTK_WIDGET_CLASS (trash_applet_parent_class)
+ ->size_allocate (widget, allocation);
+}
+
+static void
+trash_applet_destroy (GtkObject *object)
+{
+ TrashApplet *applet = TRASH_APPLET (object);
+
+ if (applet->trash_monitor)
+ g_object_unref (applet->trash_monitor);
+ applet->trash_monitor = NULL;
+
+ if (applet->trash)
+ g_object_unref (applet->trash);
+ applet->trash = NULL;
+
+ if (applet->image)
+ g_object_unref (applet->image);
+ applet->image = NULL;
+
+ if (applet->icon)
+ g_object_unref (applet->icon);
+ applet->icon = NULL;
+
+ GTK_OBJECT_CLASS (trash_applet_parent_class)
+ ->destroy (object);
+}
+
+static void
+trash_applet_init (TrashApplet *applet)
+{
+ const GtkTargetEntry drop_types[] = { { "text/uri-list" } };
+
+ /* needed to clamp ourselves to the panel size */
+ mate_panel_applet_set_flags (MATE_PANEL_APPLET (applet), MATE_PANEL_APPLET_EXPAND_MINOR);
+
+ /* enable transparency hack */
+ mate_panel_applet_set_background_widget (MATE_PANEL_APPLET (applet),
+ GTK_WIDGET (applet));
+
+ /* setup the image */
+ applet->image = g_object_ref_sink (gtk_image_new ());
+ gtk_container_add (GTK_CONTAINER (applet),
+ GTK_WIDGET (applet->image));
+ gtk_widget_show (GTK_WIDGET (applet->image));
+
+ /* setup the trash backend */
+ applet->trash = g_file_new_for_uri ("trash:/");
+ applet->trash_monitor = g_file_monitor_file (applet->trash, 0, NULL, NULL);
+ g_signal_connect_swapped (applet->trash_monitor, "changed",
+ G_CALLBACK (trash_applet_monitor_changed),
+ applet);
+
+ /* setup drag and drop */
+ gtk_drag_dest_set (GTK_WIDGET (applet), GTK_DEST_DEFAULT_ALL,
+ drop_types, G_N_ELEMENTS (drop_types),
+ GDK_ACTION_MOVE);
+
+ /* synthesise the first update */
+ applet->items = -1;
+ trash_applet_monitor_changed (applet);
+}
+
+#define PANEL_ENABLE_ANIMATIONS "/apps/panel/global/enable_animations"
+static gboolean
+trash_applet_button_release (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ TrashApplet *applet = TRASH_APPLET (widget);
+ static MateConfClient *client;
+
+ if (client == NULL)
+ client = mateconf_client_get_default ();
+
+ if (event->button == 1)
+ {
+ if (mateconf_client_get_bool (client, PANEL_ENABLE_ANIMATIONS, NULL))
+ xstuff_zoom_animate (widget, NULL);
+
+ trash_applet_open_folder (NULL, applet);
+
+ return TRUE;
+ }
+
+ if (GTK_WIDGET_CLASS (trash_applet_parent_class)->button_release_event)
+ return GTK_WIDGET_CLASS (trash_applet_parent_class)
+ ->button_release_event (widget, event);
+ else
+ return FALSE;
+}
+static gboolean
+trash_applet_key_press (GtkWidget *widget,
+ GdkEventKey *event)
+{
+ TrashApplet *applet = TRASH_APPLET (widget);
+
+ switch (event->keyval)
+ {
+ case GDK_KP_Enter:
+ case GDK_ISO_Enter:
+ case GDK_3270_Enter:
+ case GDK_Return:
+ case GDK_space:
+ case GDK_KP_Space:
+ trash_applet_open_folder (NULL, applet);
+ return TRUE;
+
+ default:
+ break;
+ }
+
+ if (GTK_WIDGET_CLASS (trash_applet_parent_class)->key_press_event)
+ return GTK_WIDGET_CLASS (trash_applet_parent_class)
+ ->key_press_event (widget, event);
+ else
+ return FALSE;
+}
+
+static gboolean
+trash_applet_drag_motion (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time)
+{
+ GList *target;
+
+ /* refuse drops of panel applets */
+ for (target = context->targets; target; target = target->next)
+ {
+ const char *name = gdk_atom_name (target->data);
+
+ if (!strcmp (name, "application/x-panel-icon-internal"))
+ break;
+ }
+
+ if (target)
+ gdk_drag_status (context, 0, time);
+ else
+ gdk_drag_status (context, GDK_ACTION_MOVE, time);
+
+ return TRUE;
+}
+
+/* TODO - Must HIGgify this dialog */
+static void
+error_dialog (TrashApplet *applet, const gchar *error, ...)
+{
+ va_list args;
+ gchar *error_string;
+ GtkWidget *dialog;
+
+ g_return_if_fail (error != NULL);
+
+ va_start (args, error);
+ error_string = g_strdup_vprintf (error, args);
+ va_end (args);
+
+ dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL,
+ GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+ "%s", error_string);
+
+ g_signal_connect (G_OBJECT (dialog), "response",
+ G_CALLBACK (gtk_widget_destroy),
+ NULL);
+
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+ gtk_window_set_screen (GTK_WINDOW(dialog),
+ gtk_widget_get_screen (GTK_WIDGET (applet)));
+ gtk_widget_show (dialog);
+
+ g_free (error_string);
+}
+
+static void
+trash_applet_do_empty (GtkAction *action,
+ TrashApplet *applet)
+{
+ trash_empty (GTK_WIDGET (applet));
+}
+
+static void
+trash_applet_open_folder (GtkAction *action,
+ TrashApplet *applet)
+{
+ GError *err = NULL;
+
+ gtk_show_uri (gtk_widget_get_screen (GTK_WIDGET (applet)),
+ "trash:",
+ gtk_get_current_event_time (),
+ &err);
+
+ if (err)
+ {
+ error_dialog (applet, _("Error while spawning caja:\n%s"),
+ err->message);
+ g_error_free (err);
+ }
+}
+
+static void
+trash_applet_show_help (GtkAction *action,
+ TrashApplet *applet)
+{
+ GError *err = NULL;
+
+ /* FIXME - Actually, we need a user guide */
+ gtk_show_uri (gtk_widget_get_screen (GTK_WIDGET (applet)),
+ "ghelp:trashapplet",
+ gtk_get_current_event_time (),
+ &err);
+
+ if (err)
+ {
+ error_dialog (applet,
+ _("There was an error displaying help: %s"),
+ err->message);
+ g_error_free (err);
+ }
+}
+
+
+static void
+trash_applet_show_about (GtkAction *action,
+ TrashApplet *applet)
+{
+ static const char *authors[] = {
+ "Michiel Sikkes <[email protected]>",
+ "Emmanuele Bassi <[email protected]>",
+ "Sebastian Bacher <[email protected]>",
+ "James Henstridge <[email protected]>",
+ "Ryan Lortie <[email protected]>",
+ NULL
+ };
+ static const char *documenters[] = {
+ "Michiel Sikkes <[email protected]>",
+ NULL
+ };
+
+ gtk_show_about_dialog (NULL,
+ "version", VERSION,
+ "copyright", "Copyright \xC2\xA9 2004 Michiel Sikkes,"
+ "\xC2\xA9 2008 Ryan Lortie",
+ "comments", _("A MATE trash bin that lives in your panel. "
+ "You can use it to view the trash or drag "
+ "and drop items into the trash."),
+ "authors", authors,
+ "documenters", documenters,
+ "translator-credits", _("translator-credits"),
+ "logo_icon_name", "user-trash-full",
+ NULL);
+}
+
+static gboolean
+confirm_delete_immediately (GtkWidget *parent_view,
+ gint num_files,
+ gboolean all)
+{
+ GdkScreen *screen;
+ GtkWidget *dialog, *hbox, *vbox, *image, *label;
+ gchar *str, *prompt, *detail;
+ int response;
+
+ screen = gtk_widget_get_screen (parent_view);
+
+ dialog = gtk_dialog_new ();
+ gtk_window_set_screen (GTK_WINDOW (dialog), screen);
+ atk_object_set_role (gtk_widget_get_accessible (dialog), ATK_ROLE_ALERT);
+ gtk_window_set_title (GTK_WINDOW (dialog), _("Delete Immediately?"));
+ gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+ gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+
+ gtk_widget_realize (dialog);
+ gdk_window_set_transient_for (gtk_widget_get_window (GTK_WIDGET (dialog)),
+ gdk_screen_get_root_window (screen));
+ gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+
+ gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 14);
+
+ hbox = gtk_hbox_new (FALSE, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
+ gtk_widget_show (hbox);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), hbox,
+ FALSE, FALSE, 0);
+
+ image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_QUESTION,
+ GTK_ICON_SIZE_DIALOG);
+ gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
+ gtk_widget_show (image);
+ gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
+
+ vbox = gtk_vbox_new (FALSE, 12);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ if (all)
+ {
+ prompt = _("Cannot move items to trash, do you want to delete them immediately?");
+ detail = g_strdup_printf ("None of the %d selected items can be moved to the Trash", num_files);
+ }
+ else
+ {
+ prompt = _("Cannot move some items to trash, do you want to delete these immediately?");
+ detail = g_strdup_printf ("%d of the selected items cannot be moved to the Trash", num_files);
+ }
+
+ str = g_strconcat ("<span weight=\"bold\" size=\"larger\">",
+ prompt, "</span>", NULL);
+ label = gtk_label_new (str);
+ gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
+ gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+ g_free (str);
+
+ label = gtk_label_new (detail);
+ gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+ g_free (detail);
+
+ gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL);
+ gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_DELETE,
+ GTK_RESPONSE_YES);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog),
+ GTK_RESPONSE_YES);
+
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ gtk_object_destroy (GTK_OBJECT (dialog));
+
+ return response == GTK_RESPONSE_YES;
+}
+
+static void
+trash_applet_drag_data_received (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selectiondata,
+ guint info,
+ guint time_)
+{
+ gchar **list;
+ gint i;
+ GList *trashed = NULL;
+ GList *untrashable = NULL;
+ GList *l;
+ GError *error = NULL;
+
+ list = g_uri_list_extract_uris ((gchar *)gtk_selection_data_get_data (selectiondata));
+
+ for (i = 0; list[i]; i++)
+ {
+ GFile *file;
+
+ file = g_file_new_for_uri (list[i]);
+
+ if (!g_file_trash (file, NULL, NULL))
+ {
+ untrashable = g_list_prepend (untrashable, file);
+ }
+ else
+ {
+ trashed = g_list_prepend (trashed, file);
+ }
+ }
+
+ if (untrashable)
+ {
+ if (confirm_delete_immediately (widget,
+ g_list_length (untrashable),
+ trashed == NULL))
+ {
+ for (l = untrashable; l; l = l->next)
+ {
+ if (!g_file_delete (l->data, NULL, &error))
+ {
+/*
+* FIXME: uncomment me after branched (we're string frozen)
+ error_dialog (applet,
+ _("Unable to delete '%s': %s"),
+ g_file_get_uri (l->data),
+ error->message);
+*/
+ g_clear_error (&error);
+ }
+ }
+ }
+ }
+
+ g_list_foreach (untrashable, (GFunc)g_object_unref, NULL);
+ g_list_free (untrashable);
+ g_list_foreach (trashed, (GFunc)g_object_unref, NULL);
+ g_list_free (trashed);
+
+ g_strfreev (list);
+
+ gtk_drag_finish (context, TRUE, FALSE, time_);
+}
+
+static void
+trash_applet_class_init (TrashAppletClass *class)
+{
+ GtkObjectClass *gtkobject_class = GTK_OBJECT_CLASS (class);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+
+ gtkobject_class->destroy = trash_applet_destroy;
+ widget_class->size_allocate = trash_applet_size_allocate;
+ widget_class->button_release_event = trash_applet_button_release;
+ widget_class->key_press_event = trash_applet_key_press;
+ widget_class->drag_motion = trash_applet_drag_motion;
+ widget_class->drag_data_received = trash_applet_drag_data_received;
+}
+
+static gboolean
+trash_applet_factory (MatePanelApplet *applet,
+ const gchar *iid,
+ gpointer data)
+{
+ gboolean retval = FALSE;
+
+ if (!strcmp (iid, "TrashApplet"))
+ {
+ GtkActionGroup *action_group;
+ gchar *ui_path;
+
+ g_set_application_name (_("Trash Applet"));
+
+ gtk_window_set_default_icon_name ("user-trash");
+
+ /* Set up the menu */
+ action_group = gtk_action_group_new ("Trash Applet Actions");
+ gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
+ gtk_action_group_add_actions (action_group,
+ trash_applet_menu_actions,
+ G_N_ELEMENTS (trash_applet_menu_actions),
+ applet);
+ ui_path = g_build_filename (TRASH_MENU_UI_DIR, "trashapplet-menu.xml", NULL);
+ mate_panel_applet_setup_menu_from_file (applet, ui_path, action_group);
+ g_free (ui_path);
+ g_object_unref (action_group);
+
+ gtk_widget_show (GTK_WIDGET (applet));
+
+ retval = TRUE;
+ }
+
+ return retval;
+}
+
+MATE_PANEL_APPLET_OUT_PROCESS_FACTORY ("TrashAppletFactory",
+ TRASH_TYPE_APPLET,
+ "TrashApplet",
+ trash_applet_factory,
+ NULL)