diff options
Diffstat (limited to 'trashapplet/src')
-rw-r--r-- | trashapplet/src/Makefile.am | 19 | ||||
-rw-r--r-- | trashapplet/src/trash-empty.c | 378 | ||||
-rw-r--r-- | trashapplet/src/trash-empty.h | 29 | ||||
-rw-r--r-- | trashapplet/src/trashapplet.c | 645 | ||||
-rw-r--r-- | trashapplet/src/xstuff.c | 536 | ||||
-rw-r--r-- | trashapplet/src/xstuff.h | 35 |
6 files changed, 1642 insertions, 0 deletions
diff --git a/trashapplet/src/Makefile.am b/trashapplet/src/Makefile.am new file mode 100644 index 00000000..37c83dfb --- /dev/null +++ b/trashapplet/src/Makefile.am @@ -0,0 +1,19 @@ +INCLUDES = -I$(top_srcdir) \ + $(MATE_APPLETS3_CFLAGS) \ + $(GIO_CFLAGS) \ + -DTRASH_MENU_UI_DIR=\""$(datadir)/mate-2.0/ui"\" + +libexec_PROGRAMS = trashapplet + +trashapplet_SOURCES = \ + trashapplet.c \ + trash-empty.h \ + trash-empty.c \ + xstuff.c \ + xstuff.h + +trashapplet_LDADD = \ + $(MATE_APPLETS3_LIBS) \ + $(GIO_LIBS) + +-include $(top_srcdir)/git.mk diff --git a/trashapplet/src/trash-empty.c b/trashapplet/src/trash-empty.c new file mode 100644 index 00000000..1c311e5e --- /dev/null +++ b/trashapplet/src/trash-empty.c @@ -0,0 +1,378 @@ +/* + * trash-empty.c: a routine to empty the trash + * + * Copyright © 2008 Ryan Lortie + * + * 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. + */ + +#include <mateconf/mateconf-client.h> +#include <gio/gio.h> +#include <glib/gi18n.h> + +#include "trash-empty.h" +#include "config.h" + +/* only one concurrent trash empty operation can occur */ +static GtkDialog *trash_empty_confirm_dialog; +static GtkDialog *trash_empty_dialog; +static GtkProgressBar *trash_empty_progress_bar; +static GtkLabel *trash_empty_location; +static GtkLabel *trash_empty_file; + +/* the rules: + * 1) nothing here may be modified while trash_empty_update_pending. + * 2) an idle may only be scheduled if trash_empty_update_pending. + * 3) only the worker may set trash_empty_update_pending = TRUE. + * 4) only the UI updater may set trash_empty_update_pending = FALSE. + * + * i -think- this is threadsafe... ((famous last words...)) + */ +static GFile * volatile trash_empty_current_file; +static gsize volatile trash_empty_deleted_files; +static gsize volatile trash_empty_total_files; +static gboolean volatile trash_empty_update_pending; + +static gboolean +trash_empty_clear_pending (gpointer user_data) +{ + trash_empty_update_pending = FALSE; + + return FALSE; +} + +static gboolean +trash_empty_update_dialog (gpointer user_data) +{ + gsize deleted, total; + GFile *file; + + g_assert (trash_empty_update_pending); + + deleted = trash_empty_deleted_files; + total = trash_empty_total_files; + file = trash_empty_current_file; + + /* maybe the done() got processed first. */ + if (trash_empty_dialog) + { + char *index_str, *total_str; + char *text_tmp, *text; + char *tmp; + + /* The i18n tools can't handle a direct embedding of the + * size format using a macro. This is a work-around. */ + index_str = g_strdup_printf ("%"G_GSIZE_FORMAT, deleted + 1); + total_str = g_strdup_printf ("%"G_GSIZE_FORMAT, total); + /* Translators: the %s in this string should be read as %d. */ + text = g_strdup_printf (_("Removing item %s of %s"), + index_str, total_str); + gtk_progress_bar_set_text (trash_empty_progress_bar, text); + g_free (total_str); + g_free (index_str); + g_free (text); + + if (deleted > total) + gtk_progress_bar_set_fraction (trash_empty_progress_bar, 1.0); + else + gtk_progress_bar_set_fraction (trash_empty_progress_bar, + (gdouble) deleted / (gdouble) total); + + /* no g_file_get_basename? */ + { + GFile *parent; + + parent = g_file_get_parent (file); + text = g_file_get_uri (parent); + g_object_unref (parent); + } + gtk_label_set_text (trash_empty_location, text); + g_free (text); + + tmp = g_file_get_basename (file); + /* Translators: %s is a file name */ + text_tmp = g_strdup_printf (_("Removing: %s"), tmp); + text = g_markup_printf_escaped ("<i>%s</i>", text_tmp); + gtk_label_set_markup (trash_empty_file, text); + g_free (text); + g_free (text_tmp); + g_free (tmp); + + /* unhide the labels */ + gtk_widget_show_all (GTK_WIDGET (trash_empty_dialog)); + } + + trash_empty_current_file = NULL; + g_object_unref (file); + + trash_empty_update_pending = FALSE; + + return FALSE; +} + +static gboolean +trash_empty_done (gpointer user_data) +{ + gtk_object_destroy (GTK_OBJECT (trash_empty_dialog)); + + g_assert (trash_empty_dialog == NULL); + + return FALSE; +} + +/* =============== worker thread code begins here =============== */ +static void +trash_empty_maybe_schedule_update (GIOSchedulerJob *job, + GFile *file, + gsize deleted) +{ + if (!trash_empty_update_pending) + { + g_assert (trash_empty_current_file == NULL); + + trash_empty_current_file = g_object_ref (file); + trash_empty_deleted_files = deleted; + + trash_empty_update_pending = TRUE; + g_io_scheduler_job_send_to_mainloop_async (job, + trash_empty_update_dialog, + NULL, NULL); + } +} + +static void +trash_empty_delete_contents (GIOSchedulerJob *job, + GCancellable *cancellable, + GFile *file, + gboolean actually_delete, + gsize *deleted) +{ + GFileEnumerator *enumerator; + GFileInfo *info; + GFile *child; + + if (g_cancellable_is_cancelled (cancellable)) + return; + + enumerator = g_file_enumerate_children (file, + G_FILE_ATTRIBUTE_STANDARD_NAME "," + G_FILE_ATTRIBUTE_STANDARD_TYPE, + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + cancellable, NULL); + if (enumerator) + { + while ((info = g_file_enumerator_next_file (enumerator, + cancellable, NULL)) != NULL) + { + child = g_file_get_child (file, g_file_info_get_name (info)); + + if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) + trash_empty_delete_contents (job, cancellable, child, + actually_delete, deleted); + + if (actually_delete) + { + trash_empty_maybe_schedule_update (job, child, *deleted); + g_file_delete (child, cancellable, NULL); + } + + (*deleted)++; + + g_object_unref (child); + g_object_unref (info); + + if (g_cancellable_is_cancelled (cancellable)) + break; + } + + g_object_unref (enumerator); + } +} + +static gboolean +trash_empty_job (GIOSchedulerJob *job, + GCancellable *cancellable, + gpointer user_data) +{ + gsize deleted; + GFile *trash; + + trash = g_file_new_for_uri ("trash:///"); + + /* first do a dry run to count the number of files */ + deleted = 0; + trash_empty_delete_contents (job, cancellable, trash, FALSE, &deleted); + trash_empty_total_files = deleted; + + /* now do the real thing */ + deleted = 0; + trash_empty_delete_contents (job, cancellable, trash, TRUE, &deleted); + + /* done */ + g_object_unref (trash); + g_io_scheduler_job_send_to_mainloop_async (job, + trash_empty_done, + NULL, NULL); + + return FALSE; +} +/* ================ worker thread code ends here ================ */ + +static void +trash_empty_start (GtkWidget *parent) +{ + struct { const char *name; gpointer *pointer; } widgets[] = + { + { "empty_trash", (gpointer *) &trash_empty_dialog }, + { "progressbar", (gpointer *) &trash_empty_progress_bar }, + { "location_label", (gpointer *) &trash_empty_location }, + { "file_label", (gpointer *) &trash_empty_file } + }; + GCancellable *cancellable; + GtkBuilder *builder; + gint i; + + builder = gtk_builder_new (); + gtk_builder_add_from_file (builder, + GTK_BUILDERDIR "/trashapplet-empty-progress.ui", + NULL); + + for (i = 0; i < G_N_ELEMENTS (widgets); i++) + { + GObject *object; + + object = gtk_builder_get_object (builder, widgets[i].name); + + if (object == NULL) + { + g_critical ("failed to parse trash-empty dialog markup"); + + if (trash_empty_dialog) + gtk_object_destroy (GTK_OBJECT (trash_empty_dialog)); + + g_object_unref (builder); + return; + } + + *widgets[i].pointer = object; + g_object_add_weak_pointer (object, widgets[i].pointer); + } + g_object_unref (builder); + + cancellable = g_cancellable_new (); + g_signal_connect_object (trash_empty_dialog, "response", + G_CALLBACK (g_cancellable_cancel), + cancellable, G_CONNECT_SWAPPED); + g_io_scheduler_push_job (trash_empty_job, NULL, NULL, 0, cancellable); + g_object_unref (cancellable); + + gtk_window_set_screen (GTK_WINDOW (trash_empty_dialog), + gtk_widget_get_screen (parent)); + gtk_widget_show (GTK_WIDGET (trash_empty_dialog)); +} + +static gboolean +trash_empty_require_confirmation (void) +{ + return mateconf_client_get_bool (mateconf_client_get_default (), + "/apps/caja/preferences/confirm_trash", + NULL); +} + +static void +trash_empty_confirmation_response (GtkDialog *dialog, + gint response_id, + gpointer user_data) +{ + if (response_id == GTK_RESPONSE_YES) + trash_empty_start (GTK_WIDGET (dialog)); + + gtk_object_destroy (GTK_OBJECT (dialog)); + g_assert (trash_empty_confirm_dialog == NULL); +} + +/* + * The code in trash_empty_show_confirmation_dialog() was taken from + * libcaja-private/caja-file-operations.c (confirm_empty_trash) + * by Michiel Sikkes <[email protected]> and adapted for the applet. + */ +static void +trash_empty_show_confirmation_dialog (GtkWidget *parent) +{ + GtkWidget *dialog; + GtkWidget *button; + GdkScreen *screen; + + if (!trash_empty_require_confirmation ()) + { + trash_empty_start (parent); + return; + } + + screen = gtk_widget_get_screen (parent); + + dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, + GTK_MESSAGE_WARNING, + GTK_BUTTONS_NONE, + _("Empty all of the items from " + "the trash?")); + trash_empty_confirm_dialog = GTK_DIALOG (dialog); + g_object_add_weak_pointer (G_OBJECT (dialog), + (gpointer *) &trash_empty_confirm_dialog); + + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + _("If you choose to empty " + "the trash, all items in " + "it will be permanently " + "lost. Please note that " + "you can also delete them " + "separately.")); + + gtk_window_set_screen (GTK_WINDOW (dialog), screen); + atk_object_set_role (gtk_widget_get_accessible (dialog), ATK_ROLE_ALERT); + gtk_window_set_wmclass (GTK_WINDOW (dialog), "empty_trash", "Caja"); + + gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL); + + button = gtk_button_new_with_mnemonic (_("_Empty Trash")); + gtk_widget_show (button); + gtk_widget_set_can_default (button, TRUE); + + gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, + GTK_RESPONSE_YES); + + gtk_dialog_set_default_response (GTK_DIALOG (dialog), + GTK_RESPONSE_YES); + + gtk_widget_show (dialog); + + g_signal_connect (dialog, "response", + G_CALLBACK (trash_empty_confirmation_response), NULL); +} + +void +trash_empty (GtkWidget *parent) +{ + if (trash_empty_confirm_dialog) + gtk_window_present (GTK_WINDOW (trash_empty_confirm_dialog)); + else if (trash_empty_dialog) + gtk_window_present (GTK_WINDOW (trash_empty_dialog)); + + /* theoretically possible that an update is pending, but very unlikely. */ + else if (!trash_empty_update_pending) + trash_empty_show_confirmation_dialog (parent); +} diff --git a/trashapplet/src/trash-empty.h b/trashapplet/src/trash-empty.h new file mode 100644 index 00000000..1bfbdc52 --- /dev/null +++ b/trashapplet/src/trash-empty.h @@ -0,0 +1,29 @@ +/* + * trash-empty.h: empty the trash + * + * Copyright © 2008 Ryan Lortie + * + * 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. + */ + +#ifndef _trash_empty_h_ +#define _trash_empty_h_ + +#include <gtk/gtk.h> + +void trash_empty (GtkWidget *parent); + +#endif /* _trash_empty_h_ */ 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) diff --git a/trashapplet/src/xstuff.c b/trashapplet/src/xstuff.c new file mode 100644 index 00000000..0458f182 --- /dev/null +++ b/trashapplet/src/xstuff.c @@ -0,0 +1,536 @@ +/* + * MATE panel x stuff + * + * Copyright (C) 2000, 2001 Eazel, Inc. + * 2002 Sun Microsystems Inc. + * + * Authors: George Lebl <[email protected]> + * Mark McLoughlin <[email protected]> + * + * Contains code from the Window Maker window manager + * + * Copyright (c) 1997-2002 Alfredo K. Kojima + + */ +#include <config.h> +#include <string.h> +#include <unistd.h> + +#include <gdk/gdk.h> +#include <gdk/gdkx.h> + +#include <X11/Xlib.h> +#include <X11/Xatom.h> + +#include "xstuff.h" + +static Atom +panel_atom_get (const char *atom_name) +{ + static GHashTable *atom_hash; + Display *xdisplay; + Atom retval; + + g_return_val_if_fail (atom_name != NULL, None); + + xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); + + if (!atom_hash) + atom_hash = g_hash_table_new_full ( + g_str_hash, g_str_equal, g_free, NULL); + + retval = GPOINTER_TO_UINT (g_hash_table_lookup (atom_hash, atom_name)); + if (!retval) { + retval = XInternAtom (xdisplay, atom_name, FALSE); + + if (retval != None) + g_hash_table_insert (atom_hash, g_strdup (atom_name), + GUINT_TO_POINTER (retval)); + } + + return retval; +} + +/* Stolen from deskguide */ +static gpointer +get_typed_property_data (Display *xdisplay, + Window xwindow, + Atom property, + Atom requested_type, + gint *size_p, + guint expected_format) +{ + static const guint prop_buffer_lengh = 1024 * 1024; + unsigned char *prop_data = NULL; + Atom type_returned = 0; + unsigned long nitems_return = 0, bytes_after_return = 0; + int format_returned = 0; + gpointer data = NULL; + gboolean abort = FALSE; + + g_return_val_if_fail (size_p != NULL, NULL); + *size_p = 0; + + gdk_error_trap_push (); + + abort = XGetWindowProperty (xdisplay, + xwindow, + property, + 0, prop_buffer_lengh, + False, + requested_type, + &type_returned, &format_returned, + &nitems_return, + &bytes_after_return, + &prop_data) != Success; + if (gdk_error_trap_pop () || + type_returned == None) + abort++; + if (!abort && + requested_type != AnyPropertyType && + requested_type != type_returned) + { + g_warning ("%s(): Property has wrong type, probably on crack", G_STRFUNC); + abort++; + } + if (!abort && bytes_after_return) + { + g_warning ("%s(): Eeek, property has more than %u bytes, stored on harddisk?", + G_STRFUNC, prop_buffer_lengh); + abort++; + } + if (!abort && expected_format && expected_format != format_returned) + { + g_warning ("%s(): Expected format (%u) unmatched (%d), programmer was drunk?", + G_STRFUNC, expected_format, format_returned); + abort++; + } + if (!abort && prop_data && nitems_return && format_returned) + { + switch (format_returned) + { + case 32: + *size_p = nitems_return * 4; + if (sizeof (gulong) == 8) + { + guint32 i, *mem = g_malloc0 (*size_p + 1); + gulong *prop_longs = (gulong*) prop_data; + + for (i = 0; i < *size_p / 4; i++) + mem[i] = prop_longs[i]; + data = mem; + } + break; + case 16: + *size_p = nitems_return * 2; + break; + case 8: + *size_p = nitems_return; + break; + default: + g_warning ("Unknown property data format with %d bits (extraterrestrial?)", + format_returned); + break; + } + if (!data && *size_p) + { + guint8 *mem = g_malloc (*size_p + 1); + + memcpy (mem, prop_data, *size_p); + mem[*size_p] = 0; + data = mem; + } + } + + if (prop_data) + XFree (prop_data); + + return data; +} + +gboolean +xstuff_is_compliant_wm (void) +{ + Display *xdisplay; + Window root_window; + gpointer data; + int size; + + xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); + root_window = GDK_WINDOW_XWINDOW ( + gdk_get_default_root_window ()); + + /* FIXME this is totally broken; should be using + * gdk_net_wm_supports() on particular hints when we rely + * on those particular hints + */ + data = get_typed_property_data ( + xdisplay, root_window, + panel_atom_get ("_NET_SUPPORTED"), + XA_ATOM, &size, 32); + + if (!data) + return FALSE; + + /* Actually checks for some of these */ + g_free (data); + return TRUE; +} + +gboolean +xstuff_net_wm_supports (const char *hint) +{ + return gdk_net_wm_supports (gdk_atom_intern (hint, FALSE)); +} + +void +xstuff_set_no_group (GdkWindow *win) +{ + XWMHints *old_wmhints; + XWMHints wmhints = {0}; + + XDeleteProperty (GDK_WINDOW_XDISPLAY (win), + GDK_WINDOW_XWINDOW (win), + panel_atom_get ("WM_CLIENT_LEADER")); + + old_wmhints = XGetWMHints (GDK_WINDOW_XDISPLAY (win), + GDK_WINDOW_XWINDOW (win)); + /* General paranoia */ + if (old_wmhints != NULL) { + memcpy (&wmhints, old_wmhints, sizeof (XWMHints)); + XFree (old_wmhints); + + wmhints.flags &= ~WindowGroupHint; + wmhints.window_group = 0; + } else { + /* General paranoia */ + wmhints.flags = StateHint; + wmhints.window_group = 0; + wmhints.initial_state = NormalState; + } + + XSetWMHints (GDK_WINDOW_XDISPLAY (win), + GDK_WINDOW_XWINDOW (win), + &wmhints); +} + +/* This is such a broken stupid function. */ +void +xstuff_set_pos_size (GdkWindow *window, int x, int y, int w, int h) +{ + XSizeHints size_hints; + int old_x, old_y, old_w, old_h; + + old_x = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window), "xstuff-cached-x")); + old_y = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window), "xstuff-cached-y")); + old_w = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window), "xstuff-cached-w")); + old_h = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window), "xstuff-cached-h")); + + if (x == old_x && y == old_y && w == old_w && h == old_h) + return; + + /* Do not add USPosition / USSize here, fix the damn WM */ + size_hints.flags = PPosition | PSize | PMaxSize | PMinSize; + size_hints.x = 0; /* window managers aren't supposed to and */ + size_hints.y = 0; /* don't use these fields */ + size_hints.width = w; + size_hints.height = h; + size_hints.min_width = w; + size_hints.min_height = h; + size_hints.max_width = w; + size_hints.max_height = h; + + gdk_error_trap_push (); + + XSetWMNormalHints (GDK_WINDOW_XDISPLAY (window), + GDK_WINDOW_XWINDOW (window), + &size_hints); + + gdk_window_move_resize (window, x, y, w, h); + + gdk_flush (); + gdk_error_trap_pop (); + + g_object_set_data (G_OBJECT (window), "xstuff-cached-x", GINT_TO_POINTER (x)); + g_object_set_data (G_OBJECT (window), "xstuff-cached-y", GINT_TO_POINTER (y)); + g_object_set_data (G_OBJECT (window), "xstuff-cached-w", GINT_TO_POINTER (w)); + g_object_set_data (G_OBJECT (window), "xstuff-cached-h", GINT_TO_POINTER (h)); +} + +void +xstuff_set_wmspec_dock_hints (GdkWindow *window, + gboolean autohide) +{ + Atom atoms [2] = { None, None }; + + if (!autohide) + atoms [0] = panel_atom_get ("_NET_WM_WINDOW_TYPE_DOCK"); + else { + atoms [0] = panel_atom_get ("_MATE_WINDOW_TYPE_AUTOHIDE_PANEL"); + atoms [1] = panel_atom_get ("_NET_WM_WINDOW_TYPE_DOCK"); + } + + XChangeProperty (GDK_WINDOW_XDISPLAY (window), + GDK_WINDOW_XWINDOW (window), + panel_atom_get ("_NET_WM_WINDOW_TYPE"), + XA_ATOM, 32, PropModeReplace, + (unsigned char *) atoms, + autohide ? 2 : 1); +} + +void +xstuff_set_wmspec_strut (GdkWindow *window, + int left, + int right, + int top, + int bottom) +{ + long vals [4]; + + vals [0] = left; + vals [1] = right; + vals [2] = top; + vals [3] = bottom; + + XChangeProperty (GDK_WINDOW_XDISPLAY (window), + GDK_WINDOW_XWINDOW (window), + panel_atom_get ("_NET_WM_STRUT"), + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *) vals, 4); +} + +void +xstuff_delete_property (GdkWindow *window, const char *name) +{ + Display *xdisplay = GDK_WINDOW_XDISPLAY (window); + Window xwindow = GDK_WINDOW_XWINDOW (window); + + XDeleteProperty (xdisplay, xwindow, + panel_atom_get (name)); +} + +/* Zoom animation */ +#define MINIATURIZE_ANIMATION_FRAMES_Z 1 +#define MINIATURIZE_ANIMATION_STEPS_Z 6 +/* the delay per draw */ +#define MINIATURIZE_ANIMATION_DELAY_Z 10 + +static void +draw_zoom_animation (GdkScreen *gscreen, + int x, int y, int w, int h, + int fx, int fy, int fw, int fh, + int steps) +{ +#define FRAMES (MINIATURIZE_ANIMATION_FRAMES_Z) + float cx[FRAMES], cy[FRAMES], cw[FRAMES], ch[FRAMES]; + int cxi[FRAMES], cyi[FRAMES], cwi[FRAMES], chi[FRAMES]; + float xstep, ystep, wstep, hstep; + int i, j; + GC frame_gc; + XGCValues gcv; + GdkColor color = { 65535, 65535, 65535 }; + Display *dpy; + Window root_win; + int screen; + int depth; + + dpy = gdk_x11_display_get_xdisplay (gdk_screen_get_display (gscreen)); + root_win = gdk_x11_drawable_get_xid (gdk_screen_get_root_window (gscreen)); + screen = gdk_screen_get_number (gscreen); + depth = gdk_drawable_get_depth (gdk_screen_get_root_window (gscreen)); + + /* frame GC */ + gdk_colormap_alloc_color ( + gdk_screen_get_system_colormap (gscreen), &color, FALSE, TRUE); + gcv.function = GXxor; + /* this will raise the probability of the XORed color being different + * of the original color in PseudoColor when not all color cells are + * initialized */ + if (DefaultVisual(dpy, screen)->class==PseudoColor) + gcv.plane_mask = (1<<(depth-1))|1; + else + gcv.plane_mask = AllPlanes; + gcv.foreground = color.pixel; + if (gcv.foreground == 0) + gcv.foreground = 1; + gcv.line_width = 1; + gcv.subwindow_mode = IncludeInferiors; + gcv.graphics_exposures = False; + + frame_gc = XCreateGC(dpy, root_win, GCForeground|GCGraphicsExposures + |GCFunction|GCSubwindowMode|GCLineWidth + |GCPlaneMask, &gcv); + + xstep = (float)(fx-x)/steps; + ystep = (float)(fy-y)/steps; + wstep = (float)(fw-w)/steps; + hstep = (float)(fh-h)/steps; + + for (j=0; j<FRAMES; j++) { + cx[j] = (float)x; + cy[j] = (float)y; + cw[j] = (float)w; + ch[j] = (float)h; + cxi[j] = (int)cx[j]; + cyi[j] = (int)cy[j]; + cwi[j] = (int)cw[j]; + chi[j] = (int)ch[j]; + } + XGrabServer(dpy); + for (i=0; i<steps; i++) { + for (j=0; j<FRAMES; j++) { + XDrawRectangle(dpy, root_win, frame_gc, cxi[j], cyi[j], cwi[j], chi[j]); + } + XFlush(dpy); +#if (MINIATURIZE_ANIMATION_DELAY_Z > 0) + usleep(MINIATURIZE_ANIMATION_DELAY_Z); +#else + usleep(10); +#endif + for (j=0; j<FRAMES; j++) { + XDrawRectangle(dpy, root_win, frame_gc, + cxi[j], cyi[j], cwi[j], chi[j]); + if (j<FRAMES-1) { + cx[j]=cx[j+1]; + cy[j]=cy[j+1]; + cw[j]=cw[j+1]; + ch[j]=ch[j+1]; + + cxi[j]=cxi[j+1]; + cyi[j]=cyi[j+1]; + cwi[j]=cwi[j+1]; + chi[j]=chi[j+1]; + + } else { + cx[j]+=xstep; + cy[j]+=ystep; + cw[j]+=wstep; + ch[j]+=hstep; + + cxi[j] = (int)cx[j]; + cyi[j] = (int)cy[j]; + cwi[j] = (int)cw[j]; + chi[j] = (int)ch[j]; + } + } + } + + for (j=0; j<FRAMES; j++) { + XDrawRectangle(dpy, root_win, frame_gc, + cxi[j], cyi[j], cwi[j], chi[j]); + } + XFlush(dpy); +#if (MINIATURIZE_ANIMATION_DELAY_Z > 0) + usleep(MINIATURIZE_ANIMATION_DELAY_Z); +#else + usleep(10); +#endif + for (j=0; j<FRAMES; j++) { + XDrawRectangle(dpy, root_win, frame_gc, + cxi[j], cyi[j], cwi[j], chi[j]); + } + + XUngrabServer(dpy); + XFreeGC (dpy, frame_gc); + gdk_colormap_free_colors (gdk_screen_get_system_colormap (gscreen), + &color, 1); +} +#undef FRAMES + +void +xstuff_zoom_animate (GtkWidget *widget, GdkRectangle *opt_rect) +{ + GdkScreen *gscreen; + GdkRectangle rect, dest; + GtkAllocation allocation; + int monitor; + + if (opt_rect) + rect = *opt_rect; + else { + gdk_window_get_origin (gtk_widget_get_window (widget), &rect.x, &rect.y); + gtk_widget_get_allocation (widget, &allocation); + if (!gtk_widget_get_has_window (widget)) { + rect.x += allocation.x; + rect.y += allocation.y; + } + rect.height = allocation.height; + rect.width = allocation.width; + } + + gscreen = gtk_widget_get_screen (widget); + monitor = gdk_screen_get_monitor_at_window (gscreen, gtk_widget_get_window (widget)); + gdk_screen_get_monitor_geometry (gscreen, monitor, &dest); + + draw_zoom_animation (gscreen, + rect.x, rect.y, rect.width, rect.height, + dest.x, dest.y, dest.width, dest.height, + MINIATURIZE_ANIMATION_STEPS_Z); +} + +int +xstuff_get_current_workspace (GdkScreen *screen) +{ + Window root_window; + Atom type = None; + gulong nitems; + gulong bytes_after; + gulong *num; + int format; + int result; + int retval; + + root_window = gdk_x11_drawable_get_xid ( + gdk_screen_get_root_window (screen)); + + gdk_error_trap_push (); + result = XGetWindowProperty (gdk_display, + root_window, + panel_atom_get ("_NET_CURRENT_DESKTOP"), + 0, G_MAXLONG, + False, XA_CARDINAL, &type, &format, &nitems, + &bytes_after, (gpointer) &num); + if (gdk_error_trap_pop () || result != Success) + return -1; + + if (type != XA_CARDINAL) { + XFree (num); + return -1; + } + + retval = *num; + + XFree (num); + + return retval; +} + +void +xstuff_grab_key_on_all_screens (int keycode, + guint modifiers, + gboolean grab) +{ + GdkDisplay *display; + int n_screens; + int i; + + display = gdk_display_get_default (); + n_screens = gdk_display_get_n_screens (display); + + for (i = 0; i < n_screens; i++) { + GdkWindow *root; + + root = gdk_screen_get_root_window ( + gdk_display_get_screen (display, i)); + + if (grab) + XGrabKey (gdk_x11_display_get_xdisplay (display), + keycode, modifiers, + gdk_x11_drawable_get_xid (root), + True, GrabModeAsync, GrabModeAsync); + else + XUngrabKey (gdk_x11_display_get_xdisplay (display), + keycode, modifiers, + gdk_x11_drawable_get_xid (root)); + } +} diff --git a/trashapplet/src/xstuff.h b/trashapplet/src/xstuff.h new file mode 100644 index 00000000..f927c220 --- /dev/null +++ b/trashapplet/src/xstuff.h @@ -0,0 +1,35 @@ +#ifndef __XSTUFF_H__ +#define __XSTUFF_H__ + +#include <gdk/gdk.h> +#include <gtk/gtk.h> + +void xstuff_delete_property (GdkWindow *window, + const char *name); +gboolean xstuff_is_compliant_wm (void); +gboolean xstuff_net_wm_supports (const char *hint); + +void xstuff_set_no_group (GdkWindow *win); + +void xstuff_unsetup_desktop_area (void); +void xstuff_set_pos_size (GdkWindow *window, + int x, int y, + int w, int h); +void xstuff_set_wmspec_dock_hints (GdkWindow *window, + gboolean autohide); +void xstuff_set_wmspec_strut (GdkWindow *window, + int left, + int right, + int top, + int bottom); + +void xstuff_zoom_animate (GtkWidget *widget, + GdkRectangle *opt_src_rect); + +int xstuff_get_current_workspace (GdkScreen *screen); + +void xstuff_grab_key_on_all_screens (int keycode, + guint modifiers, + gboolean grab); + +#endif /* __XSTUFF_H__ */ |