Don't use "cp -u" in build script; doesn't work on FreeBSD
[rox-filer.git] / ROX-Filer / src / icon.c
blobb098eacf447beb8b63046f7f2ff989fdf4f91aba
1 /*
2 * ROX-Filer, filer for the ROX desktop project
3 * Copyright (C) 2006, Thomas Leonard and others (see changelog for details).
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
17 * Place, Suite 330, Boston, MA 02111-1307 USA
20 /* icon.c - abstract base class for pinboard and panel icons.
22 * An Icon contains the full pathname of its file and the DirItem for that
23 * file. Icons emit the following signals:
25 * redraw - the image, details or selection state have changed.
26 * update - the name or path has changed.
27 * destroy - someone wishes to remove the icon.
29 * Note that an icon may be removed without emitting 'destroy'.
32 #include "config.h"
34 #include <stdarg.h>
35 #include <string.h>
36 #include <gtk/gtk.h>
37 #include <X11/keysym.h>
38 #include <gdk/gdkx.h>
40 #include "global.h"
42 #include "main.h"
43 #include "gui_support.h"
44 #include "support.h"
45 #include "icon.h"
46 #include "diritem.h"
47 #include "menu.h"
48 #include "appmenu.h"
49 #include "dnd.h"
50 #include "run.h"
51 #include "infobox.h"
52 #include "pixmaps.h"
53 #include "mount.h"
54 #include "type.h"
55 #include "usericons.h"
56 #include "pinboard.h" /* For pinboard_set_backdrop_box */
58 static gboolean have_primary = FALSE; /* We own the PRIMARY selection? */
60 GtkWidget *icon_menu; /* The popup icon menu */
61 static GtkWidget *icon_file_menu; /* The file submenu */
62 static GtkWidget *icon_file_item; /* 'File' label */
63 static GtkWidget *file_shift_item; /* 'Shift Open' label */
65 /* A list of selected Icons. Every icon in the list is from the same group
66 * (eg, you can't have icons from two different panels selected at the
67 * same time).
69 GList *icon_selection = NULL;
71 /* A list of Icons which have grabs in effect. The same combo may be
72 * listed more than once, but has only one X grab.
74 GList *icon_shortcuts = NULL;
76 #define CLICK_TO_SET _("(click to set)")
78 static unsigned int AltMask;
79 static unsigned int MetaMask;
80 static unsigned int NumLockMask;
81 static unsigned int ScrollLockMask;
82 static unsigned int CapsLockMask;
83 static unsigned int SuperMask;
84 static unsigned int HyperMask;
86 /* {MyKey -> Number of grabs} */
87 static GHashTable *grab_counter = NULL;
89 /* Each entry is a GList of Icons which have the given pathname.
90 * This allows us to update all necessary icons when something changes.
92 static GHashTable *icons_hash = NULL; /* path -> [Icon] */
94 static Icon *menu_icon = NULL; /* Item clicked if there is no selection */
96 /* Static prototypes */
97 static void rename_activate(GtkWidget *dialog);
98 static void menu_closed(GtkWidget *widget);
99 static void lose_selection(GtkClipboard *primary, gpointer data);
100 static void selection_get(GtkClipboard *primary,
101 GtkSelectionData *selection_data,
102 guint info,
103 gpointer data);
104 static void remove_items(gpointer data, guint action, GtkWidget *widget);
105 static void file_op(gpointer data, guint action, GtkWidget *widget);
106 static void show_rename_box(Icon *icon);
107 static void icon_set_selected_int(Icon *icon, gboolean selected);
108 static void icon_class_init(gpointer gclass, gpointer data);
109 static void icon_init(GTypeInstance *object, gpointer gclass);
110 static void icon_hash_path(Icon *icon);
111 static void icon_unhash_path(Icon *icon);
112 static void ungrab_key(Icon *icon);
113 static void grab_key(Icon *icon);
114 static void parseKeyString(MyKey *key, const char *str);
115 static void icon_wink(Icon *icon);
116 static void initModifiers(void);
117 static void create_menu(void);
119 enum {
120 ACTION_SHIFT,
121 ACTION_PROPERTIES,
122 ACTION_RUN_ACTION,
123 ACTION_SET_ICON,
124 ACTION_EDIT,
125 ACTION_LOCATION,
128 #undef N_
129 #define N_(x) x
130 static GtkItemFactoryEntry menu_def[] = {
131 {N_("ROX-Filer"), NULL, NULL, 0, "<Branch>"},
132 {">" N_("About ROX-Filer..."), NULL, menu_rox_help, HELP_ABOUT, "<StockItem>", GTK_STOCK_DIALOG_INFO},
133 {">" N_("Show Help Files"), NULL, menu_rox_help, HELP_DIR, "<StockItem>", GTK_STOCK_HELP},
134 {">" N_("Manual"), NULL, menu_rox_help, HELP_MANUAL, NULL},
135 {">", NULL, NULL, 0, "<Separator>"},
136 {">" N_("Options..."), NULL, menu_show_options, 0, "<StockItem>", GTK_STOCK_PREFERENCES},
137 {">" N_("Home Directory"), NULL, open_home, 0, "<StockItem>", GTK_STOCK_HOME},
138 {N_("File"), NULL, NULL, 0, "<Branch>"},
139 {">" N_("Shift Open"), NULL, file_op, ACTION_SHIFT, NULL},
140 {">" N_("Properties"), NULL, file_op, ACTION_PROPERTIES, "<StockItem>", GTK_STOCK_PROPERTIES},
141 {">" N_("Set Run Action..."), NULL, file_op, ACTION_RUN_ACTION, "<StockItem>", GTK_STOCK_EXECUTE},
142 {">" N_("Set Icon..."), NULL, file_op, ACTION_SET_ICON, NULL},
143 {N_("Edit Item"), NULL, file_op, ACTION_EDIT, "<StockItem>", GTK_STOCK_PROPERTIES},
144 {N_("Show Location"), NULL, file_op, ACTION_LOCATION, "<StockItem>", GTK_STOCK_JUMP_TO},
145 {N_("Remove Item(s)"), NULL, remove_items, 0, "<StockItem>", GTK_STOCK_REMOVE},
146 {"", NULL, NULL, 0, "<Separator>"},
149 /****************************************************************
150 * EXTERNAL INTERFACE *
151 ****************************************************************/
153 /* Called when the pointer moves over the icon */
154 void icon_may_update(Icon *icon)
156 MaskedPixmap *image;
157 int flags;
159 g_return_if_fail(icon != NULL);
161 image = di_image(icon->item);
162 flags = icon->item->flags;
164 if (image)
165 g_object_ref(image);
166 mount_update(FALSE);
167 diritem_restat(icon->path, icon->item, NULL);
169 if (di_image(icon->item) != image || icon->item->flags != flags)
171 /* Appearance changed; need to redraw */
172 g_signal_emit_by_name(icon, "redraw");
175 if (image)
176 g_object_unref(image);
179 /* If path is on an icon then it may have changed... check! */
180 void icons_may_update(const gchar *path)
182 GList *affected;
184 if (icons_hash)
186 affected = g_hash_table_lookup(icons_hash, path);
188 for (; affected; affected = affected->next)
189 icon_may_update((Icon *) affected->data);
193 typedef struct _CheckData CheckData;
194 struct _CheckData {
195 const gchar *path;
196 gboolean found;
199 static void check_has(gpointer key, GList *icons, CheckData *check)
201 Icon *icon;
203 g_return_if_fail(icons != NULL);
205 icon = icons->data;
207 if (is_sub_dir(icon->path, check->path))
208 check->found = TRUE;
211 /* Returns TRUE if any icon links to this file (or any file inside
212 * this directory). Used to check that it's OK to delete 'path'.
214 gboolean icons_require(const gchar *path)
216 CheckData check;
218 if (!icons_hash)
219 return FALSE;
221 check.path = path;
222 check.found = FALSE;
223 g_hash_table_foreach(icons_hash, (GHFunc) check_has, &check);
225 return check.found;
228 static gboolean any_selected_item_is_locked()
230 GList *next;
232 for (next = icon_selection; next; next = next->next)
234 Icon *icon = (Icon *)next->data;
236 if (icon->locked)
237 return TRUE;
240 return FALSE;
243 /* Menu was clicked over this icon. Set things up correctly (shade items,
244 * add app menu stuff, etc).
245 * You should show icon_menu after calling this...
246 * panel_name is NULL for the pinboard.
248 void icon_prepare_menu(Icon *icon, GtkWidget *options_item, ...)
250 gboolean shaded;
251 GSList *link;
252 va_list ap;
253 GtkWidget *trailing;
254 static GtkWidget *current_options_item; /* Pin/Pan Options */
255 static GSList *current_trailing_items = NULL;
257 appmenu_remove();
259 if (current_options_item)
261 gtk_widget_destroy(current_options_item);
262 current_options_item = NULL;
264 for (link = current_trailing_items; link; link = g_slist_next(link))
266 gtk_widget_destroy(link->data);
268 if (current_trailing_items)
270 g_slist_free(current_trailing_items);
271 current_trailing_items = NULL;
274 menu_icon = icon;
276 if (!icon_menu)
277 create_menu();
279 current_options_item = options_item;
280 add_stock_to_menu_item(options_item, GTK_STOCK_PREFERENCES);
282 gtk_menu_shell_append(GTK_MENU_SHELL(icon_menu), options_item);
283 gtk_widget_show_all(options_item);
285 va_start(ap, options_item);
286 while ((trailing = va_arg(ap, GtkWidget *)) != NULL)
288 current_trailing_items = g_slist_prepend(current_trailing_items,
289 trailing);
290 gtk_menu_shell_append(GTK_MENU_SHELL(icon_menu), trailing);
291 gtk_widget_show(trailing);
293 va_end(ap);
295 /* Shade Remove Item(s) if any item is locked or nothing is selected */
296 if (icon_selection)
297 shaded = any_selected_item_is_locked();
298 else if (menu_icon)
299 shaded = menu_icon->locked;
300 else
301 shaded = TRUE;
303 menu_set_items_shaded(icon_menu, shaded, 4, 1);
305 menu_show_shift_action(file_shift_item, icon ? icon->item : NULL,
306 FALSE);
308 /* Shade the File/Edit/Show items unless an item was clicked */
309 if (icon)
311 guchar *tmp;
313 menu_set_items_shaded(icon_menu, FALSE, 1, 3);
314 menu_set_items_shaded(icon_file_menu, FALSE, 0, 5);
315 if (!can_set_run_action(icon->item))
316 menu_set_items_shaded(icon_file_menu, TRUE, 2, 1);
318 tmp = g_strdup_printf(_("%s '%s'"),
319 basetype_name(icon->item),
320 icon->item->leafname);
321 gtk_label_set_text(GTK_LABEL(icon_file_item), tmp);
322 g_free(tmp);
324 /* Check for app-specific menu */
325 appmenu_add(icon->path, icon->item, icon_menu);
327 else
329 menu_set_items_shaded(icon_menu, TRUE, 1, 3);
330 menu_set_items_shaded(icon_file_menu, TRUE, 0, 5);
331 gtk_label_set_text(GTK_LABEL(icon_file_item), _("Nothing"));
335 /* Set whether this icon is selected. Will automatically clear the selection
336 * if it contains icons from a different group.
338 void icon_set_selected(Icon *icon, gboolean selected)
340 if (selected && icon_selection)
342 IconClass *iclass;
343 Icon *other = (Icon *) icon_selection->data;
345 iclass = (IconClass *) G_OBJECT_GET_CLASS(icon);
346 if (!iclass->same_group(icon, other))
348 icon_select_only(icon);
349 return;
353 icon_set_selected_int(icon, selected);
356 /* Clear everything, except 'select', which is selected.
357 * If select is NULL, unselects everything.
359 void icon_select_only(Icon *select)
361 GList *to_clear, *next;
363 if (select)
364 icon_set_selected_int(select, TRUE);
366 to_clear = g_list_copy(icon_selection);
368 if (select)
369 to_clear = g_list_remove(to_clear, select);
371 for (next = to_clear; next; next = next->next)
372 icon_set_selected_int((Icon *) next->data, FALSE);
374 g_list_free(to_clear);
377 /* Destroys this icon's widget, causing it to be removed from the screen */
378 void icon_destroy(Icon *icon)
380 g_return_if_fail(icon != NULL);
382 icon_set_selected_int(icon, FALSE);
384 if (!icon->locked)
385 g_signal_emit_by_name(icon, "destroy");
388 /* Return a text/uri-list of all the icons in the selection */
389 gchar *icon_create_uri_list(void)
391 GString *tmp;
392 guchar *retval;
393 GList *next;
395 tmp = g_string_new(NULL);
397 for (next = icon_selection; next; next = next->next)
399 Icon *icon = (Icon *) next->data;
400 EscapedPath *uri;
402 uri = encode_path_as_uri(icon->path);
403 g_string_append(tmp, (char *) uri);
404 g_free(uri);
405 g_string_append(tmp, "\r\n");
408 retval = tmp->str;
409 g_string_free(tmp, FALSE);
411 return retval;
414 GType icon_get_type(void)
416 static GType type = 0;
418 if (!type)
420 static const GTypeInfo info =
422 sizeof (IconClass),
423 NULL, /* base_init */
424 NULL, /* base_finalise */
425 icon_class_init,
426 NULL, /* class_finalise */
427 NULL, /* class_data */
428 sizeof(Icon),
429 0, /* n_preallocs */
430 icon_init
433 type = g_type_register_static(G_TYPE_OBJECT, "Icon",
434 &info, 0);
437 return type;
440 /* Sets, unsets or changes the pathname and name for an icon.
441 * Updates icons_hash and (re)stats the item.
442 * If name is NULL then gets the leafname from pathname.
443 * If pathname is NULL, frees everything.
445 void icon_set_path(Icon *icon, const char *pathname, const char *name)
447 if (icon->path)
449 icon_unhash_path(icon);
450 icon->src_path = NULL;
451 icon->path = NULL;
453 diritem_free(icon->item);
454 icon->item = NULL;
457 if (pathname)
459 if (g_utf8_validate(pathname, -1, NULL))
460 icon->src_path = g_strdup(pathname);
461 else
462 icon->src_path = to_utf8(pathname);
463 icon->path = expand_path(icon->src_path);
465 icon_hash_path(icon);
467 if (!name)
468 name = g_basename(icon->src_path);
470 icon->item = diritem_new(name);
471 diritem_restat(icon->path, icon->item, NULL);
475 void icon_set_shortcut(Icon *icon, const gchar *shortcut)
477 g_return_if_fail(icon != NULL);
479 if (shortcut && !*shortcut)
480 shortcut = NULL;
481 if (icon->shortcut == shortcut)
482 return;
483 if (icon->shortcut && shortcut && strcmp(icon->shortcut, shortcut) == 0)
484 return;
486 initModifiers();
488 ungrab_key(icon);
490 g_free(icon->shortcut);
491 icon->shortcut = g_strdup(shortcut);
492 parseKeyString(&icon->shortcut_key, shortcut);
494 grab_key(icon);
497 void icon_set_arguments(Icon *icon, const gchar *args)
499 g_return_if_fail(icon != NULL);
500 g_return_if_fail(args == NULL || icon->args != args);
502 if (args && !*args)
503 args = NULL;
504 if (icon->args && args && strcmp(icon->args, args) == 0)
505 return;
507 g_free(icon->args);
508 icon->args = g_strdup(args);
511 void icon_run(Icon *icon)
513 if (icon->args == NULL)
514 run_diritem(icon->path, icon->item, NULL, NULL, FALSE);
515 else
516 run_with_args(icon->path, icon->item, icon->args);
519 /****************************************************************
520 * INTERNAL FUNCTIONS *
521 ****************************************************************/
523 /* The icons_hash table allows us to convert from a path to a list
524 * of icons that use that path.
525 * Add this icon to the list for its path.
527 static void icon_hash_path(Icon *icon)
529 GList *list;
531 g_return_if_fail(icon != NULL);
533 /* g_print("[ hashing '%s' ]\n", icon->path); */
535 list = g_hash_table_lookup(icons_hash, icon->path);
536 list = g_list_prepend(list, icon);
537 g_hash_table_insert(icons_hash, icon->path, list);
540 /* Remove this icon from the icons_hash table */
541 static void icon_unhash_path(Icon *icon)
543 GList *list;
545 g_return_if_fail(icon != NULL);
547 /* g_print("[ unhashing '%s' ]\n", icon->path); */
549 list = g_hash_table_lookup(icons_hash, icon->path);
550 g_return_if_fail(list != NULL);
552 list = g_list_remove(list, icon);
554 /* Remove it first; the hash key may have changed address */
555 g_hash_table_remove(icons_hash, icon->path);
556 if (list)
557 g_hash_table_insert(icons_hash,
558 ((Icon *) list->data)->path, list);
561 static void rename_activate(GtkWidget *dialog)
563 GtkWidget *entry, *src, *shortcut, *arg, *lock_state;
564 Icon *icon;
565 const guchar *new_name, *new_src, *new_shortcut, *new_args;
566 gboolean new_lock_state;
568 entry = g_object_get_data(G_OBJECT(dialog), "new_name");
569 icon = g_object_get_data(G_OBJECT(dialog), "callback_icon");
570 src = g_object_get_data(G_OBJECT(dialog), "new_path");
571 shortcut = g_object_get_data(G_OBJECT(dialog), "new_shortcut");
572 arg = g_object_get_data(G_OBJECT(dialog), "new_arg");
573 lock_state = g_object_get_data(G_OBJECT(dialog), "new_lock_state");
575 g_return_if_fail(entry != NULL &&
576 src != NULL &&
577 icon != NULL &&
578 shortcut != NULL &&
579 arg != NULL &&
580 lock_state != NULL);
582 new_name = gtk_entry_get_text(GTK_ENTRY(entry));
583 new_src = gtk_entry_get_text(GTK_ENTRY(src));
584 new_shortcut = gtk_label_get_text(GTK_LABEL(shortcut));
585 if (strcmp(new_shortcut, CLICK_TO_SET) == 0)
586 new_shortcut = NULL;
587 new_args = gtk_entry_get_text(GTK_ENTRY(arg));
588 new_lock_state = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lock_state));
590 if (*new_src == '\0')
591 report_error(
592 _("The location must contain at least one character!"));
593 else
595 icon_set_path(icon, new_src, new_name);
596 icon_set_shortcut(icon, new_shortcut);
597 icon_set_arguments(icon, new_args);
598 icon->locked = new_lock_state;
599 g_signal_emit_by_name(icon, "update");
600 gtk_widget_destroy(dialog);
604 static void menu_closed(GtkWidget *widget)
606 appmenu_remove();
607 menu_icon = NULL;
610 /* Called when another application takes the selection away from us */
611 static void lose_selection(GtkClipboard *primary, gpointer data)
613 have_primary = FALSE;
614 icon_select_only(NULL);
617 /* Called when another application wants the contents of our selection */
618 static void selection_get(GtkClipboard *primary,
619 GtkSelectionData *selection_data,
620 guint info,
621 gpointer data)
623 gchar *text;
625 if (info == TARGET_URI_LIST)
626 text = icon_create_uri_list();
627 else
629 GList *next;
630 GString *str;
632 str = g_string_new(NULL);
634 for (next = icon_selection; next; next = next->next)
636 Icon *icon = (Icon *) next->data;
638 g_string_append(str, icon->path);
639 g_string_append_c(str, ' ');
642 text = str->str;
643 g_string_free(str, FALSE);
646 gtk_selection_data_set_text(selection_data, text, strlen(text));
649 static void remove_items(gpointer data, guint action, GtkWidget *widget)
651 IconClass *iclass;
653 if (menu_icon)
655 if (menu_icon->locked)
657 delayed_error(_("You must unlock '%s' before removing it"),
658 menu_icon->item->leafname);
659 return;
661 icon_set_selected(menu_icon, TRUE);
664 if (!icon_selection)
666 delayed_error(
667 _("You must first select some items to remove"));
668 return;
671 if (any_selected_item_is_locked())
673 delayed_error(_("An item must be unlocked before it can be removed."));
674 return;
677 iclass = (IconClass *)
678 G_OBJECT_GET_CLASS(G_OBJECT(icon_selection->data));
680 iclass->remove_items();
683 static void file_op(gpointer data, guint action, GtkWidget *widget)
685 if (!menu_icon)
687 delayed_error(_("You must open the menu over an item"));
688 return;
691 switch (action)
693 case ACTION_SHIFT:
694 run_diritem(menu_icon->path, menu_icon->item,
695 NULL, NULL, TRUE);
696 break;
697 case ACTION_EDIT:
698 show_rename_box(menu_icon);
699 break;
700 case ACTION_LOCATION:
701 open_to_show(menu_icon->path);
702 break;
703 case ACTION_PROPERTIES:
704 infobox_new(menu_icon->path);
705 break;
706 case ACTION_RUN_ACTION:
707 if (can_set_run_action(menu_icon->item))
708 type_set_handler_dialog(
709 menu_icon->item->mime_type);
710 else
711 report_error(
712 _("You can only set the run action for a "
713 "regular file"));
714 break;
715 case ACTION_SET_ICON:
716 icon_set_handler_dialog(menu_icon->item,
717 menu_icon->path);
718 break;
722 static void edit_response(GtkWidget *dialog, gint response, gpointer data)
724 if (response == GTK_RESPONSE_OK)
725 rename_activate(dialog);
726 else if (response == GTK_RESPONSE_CANCEL)
727 gtk_widget_destroy(dialog);
730 static GdkFilterReturn filter_get_key(GdkXEvent *xevent,
731 GdkEvent *event,
732 gpointer data)
734 XKeyEvent *kev = (XKeyEvent *) xevent;
735 GtkWidget *popup = (GtkWidget *) data;
736 Display *dpy = GDK_DISPLAY();
738 if (kev->type != KeyRelease && kev->type != ButtonPressMask)
739 return GDK_FILTER_CONTINUE;
741 initModifiers();
743 if (kev->type == KeyRelease)
745 gchar *str;
746 KeySym sym;
747 unsigned int m = kev->state;
749 sym = XKeycodeToKeysym(dpy, kev->keycode, 0);
750 if (!sym)
751 return GDK_FILTER_CONTINUE;
753 str = g_strdup_printf("%s%s%s%s%s%s%s",
754 m & ControlMask ? "Control+" : "",
755 m & ShiftMask ? "Shift+" : "",
756 m & AltMask ? "Alt+" : "",
757 m & MetaMask ? "Meta+" : "",
758 m & SuperMask ? "Super+" : "",
759 m & HyperMask ? "Hyper+" : "",
760 XKeysymToString(sym));
762 g_object_set_data(G_OBJECT(popup), "chosen-key", str);
765 gdk_window_remove_filter(popup->window, filter_get_key, data);
766 gtk_widget_destroy(popup);
768 return GDK_FILTER_REMOVE;
771 static void may_set_shortcut(GtkWidget *popup, GtkWidget *label)
773 gchar *str;
775 str = g_object_get_data(G_OBJECT(popup), "chosen-key");
776 if (str)
778 gtk_label_set_text(GTK_LABEL(label), str);
779 g_free(str);
780 g_object_set_data(G_OBJECT(popup), "chosen-key", NULL);
784 static void get_shortcut(GtkWidget *button, GtkWidget *label)
786 GtkWidget *popup, *frame, *msg;
787 Window xid;
788 Display *dpy = GDK_DISPLAY();
790 popup = gtk_window_new(GTK_WINDOW_POPUP);
792 gtk_window_set_position(GTK_WINDOW(popup), GTK_WIN_POS_CENTER);
794 frame = gtk_frame_new(NULL);
795 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
796 gtk_container_add(GTK_CONTAINER(popup), frame);
798 msg = gtk_label_new(_("Press the desired shortcut (eg, Control+F1)"));
800 gtk_misc_set_padding(GTK_MISC(msg), 20, 20);
801 gtk_container_add(GTK_CONTAINER(frame), msg);
803 gtk_window_set_modal(GTK_WINDOW(popup), TRUE);
805 gtk_widget_add_events(popup,
806 GDK_KEY_RELEASE_MASK | GDK_BUTTON_PRESS_MASK);
808 g_signal_connect(popup, "destroy",
809 G_CALLBACK(may_set_shortcut), label);
811 gtk_widget_show_all(popup);
813 gdk_window_add_filter(popup->window, filter_get_key, popup);
815 xid = gdk_x11_drawable_get_xid(popup->window);
817 if (XGrabKeyboard(dpy, xid, False, GrabModeAsync, GrabModeAsync,
818 gtk_get_current_event_time()) != Success)
820 delayed_error(_("Failed to get keyboard grab!"));
821 gtk_widget_destroy(popup);
824 if (XGrabPointer(dpy, xid, False, ButtonPressMask, GrabModeAsync,
825 GrabModeAsync, xid, None,
826 gtk_get_current_event_time()) != Success)
828 g_warning("Failed to get mouse grab");
832 static void clear_shortcut(GtkButton *clear, GtkLabel *label)
834 gtk_label_set_text(GTK_LABEL(label), CLICK_TO_SET);
837 /* Opens a box allowing the user to change the name of a pinned icon.
838 * If the icon is destroyed then the box will close.
839 * If the user chooses OK then the callback is called once the icon's
840 * name, src_path and path fields have been updated and the item field
841 * restatted.
843 static void show_rename_box(Icon *icon)
845 GtkDialog *dialog;
846 GtkWidget *label, *entry, *button, *button2, *hbox, *spacer, *lock_state;
847 GtkBox *vbox;
849 if (icon->dialog)
851 gtk_window_present(GTK_WINDOW(icon->dialog));
852 return;
855 icon->dialog = gtk_dialog_new();
856 gtk_dialog_set_has_separator(GTK_DIALOG(icon->dialog), FALSE);
857 g_signal_connect(icon->dialog, "destroy",
858 G_CALLBACK(gtk_widget_destroyed), &icon->dialog);
860 dialog = GTK_DIALOG(icon->dialog);
862 vbox = GTK_BOX(gtk_vbox_new(FALSE, 1));
863 gtk_box_pack_start(GTK_BOX(dialog->vbox), (GtkWidget *) vbox,
864 TRUE, TRUE, 0);
865 gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
867 gtk_window_set_title(GTK_WINDOW(dialog), _("Edit Item"));
868 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
870 label = gtk_label_new(_("Clicking the icon opens:"));
871 gtk_box_pack_start(vbox, label, TRUE, TRUE, 0);
873 entry = gtk_entry_new();
874 gtk_box_pack_start(vbox, entry, TRUE, FALSE, 2);
875 gtk_entry_set_text(GTK_ENTRY(entry), icon->src_path);
876 g_object_set_data(G_OBJECT(dialog), "new_path", entry);
877 g_signal_connect_swapped(entry, "activate",
878 G_CALLBACK(rename_activate), dialog);
880 label = gtk_label_new(_("Arguments to pass (for executables):"));
881 gtk_box_pack_start(vbox, label, TRUE, TRUE, 0);
883 entry = gtk_entry_new();
884 gtk_box_pack_start(vbox, entry, TRUE, FALSE, 2);
885 gtk_entry_set_text(GTK_ENTRY(entry), icon->args ? icon->args : "");
886 g_object_set_data(G_OBJECT(dialog), "new_arg", entry);
887 g_signal_connect_swapped(entry, "activate",
888 G_CALLBACK(rename_activate), dialog);
890 spacer = gtk_drawing_area_new();
891 gtk_widget_set_size_request(spacer, 4, 4);
892 gtk_box_pack_start(vbox, spacer, FALSE, FALSE, 0);
894 label = gtk_label_new(_("The text displayed under the icon is:"));
895 gtk_box_pack_start(vbox, label, TRUE, TRUE, 0);
896 entry = gtk_entry_new();
897 gtk_box_pack_start(vbox, entry, TRUE, FALSE, 2);
898 gtk_entry_set_text(GTK_ENTRY(entry), icon->item->leafname);
899 gtk_widget_grab_focus(entry);
900 g_object_set_data(G_OBJECT(dialog), "new_name", entry);
901 gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
903 spacer = gtk_drawing_area_new();
904 gtk_widget_set_size_request(spacer, 4, 4);
905 gtk_box_pack_start(vbox, spacer, FALSE, FALSE, 0);
907 label = gtk_label_new(_("The keyboard shortcut is:"));
908 gtk_box_pack_start(vbox, label, TRUE, TRUE, 0);
910 hbox = gtk_hbox_new(FALSE, 2);
911 gtk_box_pack_start(vbox, hbox, TRUE, FALSE, 0);
912 button = gtk_button_new_with_label(icon->shortcut
913 ? icon->shortcut
914 : CLICK_TO_SET);
915 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
916 g_object_set_data(G_OBJECT(dialog), "new_shortcut",
917 GTK_BIN(button)->child);
918 g_signal_connect(button, "clicked",
919 G_CALLBACK(get_shortcut),
920 GTK_BIN(button)->child);
921 button2 = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
922 gtk_box_pack_start(GTK_BOX(hbox), button2, FALSE, FALSE, 0);
923 g_signal_connect(button2, "clicked",
924 G_CALLBACK(clear_shortcut),
925 GTK_BIN(button)->child);
927 lock_state = gtk_check_button_new_with_label(_("Locked"));
928 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(lock_state), icon->locked);
929 gtk_box_pack_start(vbox, lock_state, TRUE, TRUE, 0);
930 g_object_set_data(G_OBJECT(dialog), "new_lock_state", lock_state);
931 gtk_tooltips_set_tip(tooltips, lock_state,
932 _("Locking an item prevents it from being accidentally removed"),
933 NULL);
935 g_object_set_data(G_OBJECT(dialog), "callback_icon", icon);
937 gtk_dialog_add_buttons(dialog,
938 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
939 GTK_STOCK_OK, GTK_RESPONSE_OK,
940 NULL);
941 gtk_dialog_set_default_response(dialog, GTK_RESPONSE_OK);
943 g_signal_connect(dialog, "response", G_CALLBACK(edit_response), NULL);
945 gtk_widget_show_all(GTK_WIDGET(dialog));
948 static gpointer parent_class = NULL;
950 static void icon_finialize(GObject *object)
952 Icon *icon = (Icon *) object;
954 g_return_if_fail(!icon->selected);
956 if (icon->dialog)
957 gtk_widget_destroy(icon->dialog);
959 if (icon == menu_icon)
960 menu_icon = NULL;
962 icon_set_path(icon, NULL, NULL);
963 icon_set_shortcut(icon, NULL);
964 icon_set_arguments(icon, NULL);
966 G_OBJECT_CLASS(parent_class)->finalize(object);
969 static void icon_class_init(gpointer gclass, gpointer data)
971 GObjectClass *object = (GObjectClass *) gclass;
972 IconClass *icon = (IconClass *) gclass;
974 parent_class = g_type_class_peek_parent(gclass);
976 object->finalize = icon_finialize;
977 icon->destroy = NULL;
978 icon->redraw = NULL;
979 icon->update = NULL;
980 icon->same_group = NULL;
981 icon->wink = NULL;
983 g_signal_new("update",
984 G_TYPE_FROM_CLASS(gclass),
985 G_SIGNAL_RUN_LAST,
986 G_STRUCT_OFFSET(IconClass, update),
987 NULL, NULL,
988 g_cclosure_marshal_VOID__VOID,
989 G_TYPE_NONE, 0);
991 g_signal_new("destroy",
992 G_TYPE_FROM_CLASS(gclass),
993 G_SIGNAL_RUN_LAST,
994 G_STRUCT_OFFSET(IconClass, destroy),
995 NULL, NULL,
996 g_cclosure_marshal_VOID__VOID,
997 G_TYPE_NONE, 0);
999 g_signal_new("redraw",
1000 G_TYPE_FROM_CLASS(gclass),
1001 G_SIGNAL_RUN_LAST,
1002 G_STRUCT_OFFSET(IconClass, redraw),
1003 NULL, NULL,
1004 g_cclosure_marshal_VOID__VOID,
1005 G_TYPE_NONE, 0);
1007 icons_hash = g_hash_table_new(g_str_hash, g_str_equal);
1010 static void icon_init(GTypeInstance *object, gpointer gclass)
1012 Icon *icon = (Icon *) object;
1014 icon->selected = FALSE;
1015 icon->src_path = NULL;
1016 icon->path = NULL;
1017 icon->item = NULL;
1018 icon->dialog = NULL;
1019 icon->shortcut = NULL;
1020 icon->shortcut_key.keycode = 0;
1021 icon->shortcut_key.modifier = 0;
1022 icon->args = NULL;
1023 icon->locked = FALSE;
1026 /* As icon_set_selected(), but doesn't automatically unselect incompatible
1027 * icons.
1029 static void icon_set_selected_int(Icon *icon, gboolean selected)
1031 static GtkClipboard *primary;
1033 g_return_if_fail(icon != NULL);
1035 if (icon->selected == selected)
1036 return;
1038 if (!primary)
1039 primary = gtk_clipboard_get(gdk_atom_intern("PRIMARY", FALSE));
1041 if (selected)
1043 icon_selection = g_list_prepend(icon_selection, icon);
1044 if (!have_primary)
1046 GtkTargetEntry target_table[] =
1048 {"text/uri-list", 0, TARGET_URI_LIST},
1049 {"UTF8", 0, TARGET_STRING},
1050 {"COMPOUND_TEXT", 0, TARGET_STRING},
1051 {"STRING", 0, TARGET_STRING},
1054 /* Grab selection */
1055 have_primary = gtk_clipboard_set_with_data(primary,
1056 target_table,
1057 sizeof(target_table) / sizeof(*target_table),
1058 selection_get, lose_selection, NULL);
1061 else
1063 icon_selection = g_list_remove(icon_selection, icon);
1064 if (have_primary && !icon_selection)
1066 have_primary = FALSE;
1067 gtk_clipboard_clear(primary);
1071 icon->selected = selected;
1072 g_signal_emit_by_name(icon, "redraw");
1075 /* From xfwm4 */
1076 static void initModifiers(void)
1078 static gboolean need_init = TRUE;
1079 Display *dpy = GDK_DISPLAY();
1080 XModifierKeymap *xmk = XGetModifierMapping(dpy);
1081 int m, k;
1083 if (!need_init)
1084 return;
1085 need_init = FALSE;
1087 AltMask = MetaMask = NumLockMask = ScrollLockMask = CapsLockMask =
1088 SuperMask = HyperMask = 0;
1090 /* Work out which mask to use for each modifier group */
1091 if (xmk)
1093 KeyCode *c = xmk->modifiermap;
1094 KeyCode numLockKeyCode;
1095 KeyCode scrollLockKeyCode;
1096 KeyCode capsLockKeyCode;
1097 KeyCode altKeyCode;
1098 KeyCode metaKeyCode;
1099 KeyCode superKeyCode;
1100 KeyCode hyperKeyCode;
1102 /* Find the codes to search for... */
1103 numLockKeyCode = XKeysymToKeycode(dpy, XK_Num_Lock);
1104 scrollLockKeyCode = XKeysymToKeycode(dpy, XK_Scroll_Lock);
1105 capsLockKeyCode = XKeysymToKeycode(dpy, XK_Caps_Lock);
1106 altKeyCode = XKeysymToKeycode(dpy, XK_Alt_L);
1107 metaKeyCode = XKeysymToKeycode(dpy, XK_Meta_L);
1108 superKeyCode = XKeysymToKeycode(dpy, XK_Super_L);
1109 hyperKeyCode = XKeysymToKeycode(dpy, XK_Hyper_L);
1111 /* If some are missing, try alternatives... */
1112 if (!altKeyCode)
1113 altKeyCode = XKeysymToKeycode(dpy, XK_Alt_R);
1114 if (!metaKeyCode)
1115 metaKeyCode = XKeysymToKeycode(dpy, XK_Meta_R);
1116 if (!superKeyCode)
1117 superKeyCode = XKeysymToKeycode(dpy, XK_Super_R);
1118 if (!hyperKeyCode)
1119 hyperKeyCode = XKeysymToKeycode(dpy, XK_Hyper_R);
1121 /* Check each of the eight modifier lists.
1122 * The idea (I think) is that we name the modifier group which
1123 * includes the Alt key as the 'Alt group', and so on for
1124 * the other modifiers.
1126 for (m = 0; m < 8; m++)
1128 for (k = 0; k < xmk->max_keypermod; k++, c++)
1130 if (*c == NoSymbol)
1131 continue;
1132 if (*c == numLockKeyCode)
1133 NumLockMask = (1 << m);
1134 if (*c == scrollLockKeyCode)
1135 ScrollLockMask = (1 << m);
1136 if (*c == capsLockKeyCode)
1137 CapsLockMask = (1 << m);
1138 if (*c == altKeyCode)
1139 AltMask = (1 << m);
1140 if (*c == metaKeyCode)
1141 MetaMask = (1 << m);
1142 if (*c == superKeyCode)
1143 SuperMask = (1 << m);
1144 if (*c == hyperKeyCode)
1145 HyperMask = (1 << m);
1148 XFreeModifiermap(xmk);
1151 if(MetaMask == AltMask)
1152 MetaMask = 0;
1154 if (AltMask != 0 && MetaMask == Mod1Mask)
1156 MetaMask = AltMask;
1157 AltMask = Mod1Mask;
1160 if (AltMask == 0 && MetaMask != 0)
1162 if (MetaMask != Mod1Mask)
1163 AltMask = Mod1Mask;
1164 else
1166 AltMask = MetaMask;
1167 MetaMask = 0;
1171 if (AltMask == 0)
1172 AltMask = Mod1Mask;
1176 /* Fill in key from str. Sets keycode to zero if str is NULL.
1177 * Stolen from xfwm4 and modified. Call initModifiers before this.
1179 static void parseKeyString(MyKey *key, const char *str)
1181 char *k;
1182 Display *dpy = GDK_DISPLAY();
1184 key->keycode = 0;
1185 key->modifier = 0;
1187 if (!str)
1188 return;
1190 k = strrchr(str, '+');
1191 key->keycode = XKeysymToKeycode(dpy, XStringToKeysym(k ? k + 1 : str));
1192 if (k)
1194 gchar *tmp;
1196 tmp = g_ascii_strdown(str, -1);
1198 if (strstr(tmp, "shift"))
1199 key->modifier = key->modifier | ShiftMask;
1200 if (strstr(tmp, "control"))
1201 key->modifier = key->modifier | ControlMask;
1202 if (strstr(tmp, "alt") || strstr(tmp, "mod1"))
1203 key->modifier = key->modifier | AltMask;
1204 if (strstr(tmp, "meta") || strstr(tmp, "mod2"))
1205 key->modifier = key->modifier | MetaMask;
1206 if (strstr(tmp, "hyper"))
1207 key->modifier = key->modifier | HyperMask;
1208 if (strstr(tmp, "super"))
1209 key->modifier = key->modifier | SuperMask;
1211 g_free(tmp);
1214 if (!key->keycode)
1215 g_warning("Can't parse key '%s'\n", str);
1218 static GdkFilterReturn filter_keys(GdkXEvent *xevent,
1219 GdkEvent *event,
1220 gpointer data)
1222 GList *next;
1223 XKeyEvent *kev = (XKeyEvent *) xevent;
1224 guint state;
1226 if (kev->type != KeyPress)
1227 return GDK_FILTER_CONTINUE;
1229 state = kev->state & (ShiftMask | ControlMask | AltMask | MetaMask |
1230 HyperMask | SuperMask);
1232 for (next = icon_shortcuts; next; next = next->next)
1234 Icon *icon = (Icon *) next->data;
1236 if (icon->shortcut_key.keycode == kev->keycode &&
1237 icon->shortcut_key.modifier == state)
1239 icon_wink(icon);
1240 icon_run(icon);
1244 return GDK_FILTER_CONTINUE;
1247 #define GRAB(key, mods) XGrabKey(dpy, key->keycode, key->modifier | mods, \
1248 root, False, GrabModeAsync, GrabModeAsync)
1249 #define UNGRAB(key, mods) XUngrabKey(dpy, key->keycode, key->modifier | mods, \
1250 root)
1252 static guint mykey_hash(gconstpointer key)
1254 MyKey *k = (MyKey *) key;
1256 return (k->keycode << 8) + k->modifier;
1259 static gboolean mykey_cmp(gconstpointer a, gconstpointer b)
1261 MyKey *ka = (MyKey *) a;
1262 MyKey *kb = (MyKey *) b;
1264 return ka->keycode == kb->keycode && kb->modifier == kb->modifier;
1267 /* Stolen from xfwm4 and modified.
1268 * FALSE on error. Call initModifiers before this.
1270 static gboolean grabKey(MyKey *key)
1272 Window root;
1273 Display *dpy = GDK_DISPLAY();
1274 static gboolean need_init = TRUE;
1276 if (need_init)
1278 need_init = FALSE;
1279 gdk_window_add_filter(gdk_get_default_root_window(),
1280 filter_keys, NULL);
1283 gdk_error_trap_push();
1285 root = GDK_ROOT_WINDOW();
1287 GRAB(key, 0);
1289 /* Here we grab all combinations of well known modifiers */
1290 GRAB(key, ScrollLockMask);
1291 GRAB(key, NumLockMask);
1292 GRAB(key, CapsLockMask);
1293 GRAB(key, ScrollLockMask | NumLockMask);
1294 GRAB(key, ScrollLockMask | CapsLockMask);
1295 GRAB(key, CapsLockMask | NumLockMask);
1296 GRAB(key, ScrollLockMask | CapsLockMask | NumLockMask);
1298 gdk_flush();
1299 return gdk_error_trap_pop() == Success;
1302 static gboolean ungrabKey(MyKey *key)
1304 Window root = GDK_ROOT_WINDOW();
1305 Display *dpy = GDK_DISPLAY();
1307 gdk_error_trap_push();
1309 UNGRAB(key, 0);
1311 UNGRAB(key, ScrollLockMask);
1312 UNGRAB(key, NumLockMask);
1313 UNGRAB(key, CapsLockMask);
1314 UNGRAB(key, ScrollLockMask | NumLockMask);
1315 UNGRAB(key, ScrollLockMask | CapsLockMask);
1316 UNGRAB(key, CapsLockMask | NumLockMask);
1317 UNGRAB(key, ScrollLockMask | CapsLockMask | NumLockMask);
1319 gdk_flush();
1320 return gdk_error_trap_pop() == Success;
1323 static void ungrab_key(Icon *icon)
1325 int *count;
1327 g_return_if_fail(icon != NULL);
1329 if (!icon->shortcut_key.keycode)
1330 return;
1332 icon_shortcuts = g_list_remove(icon_shortcuts, icon);
1334 count = g_hash_table_lookup(grab_counter, &icon->shortcut_key);
1335 g_return_if_fail(count != NULL);
1337 (*count)--;
1339 if (*count > 0)
1340 return;
1342 g_hash_table_remove(grab_counter, &icon->shortcut_key);
1344 if (!ungrabKey(&icon->shortcut_key))
1345 g_warning("Failed to ungrab shortcut '%s' for '%s' icon.",
1346 icon->shortcut, icon->item->leafname);
1349 static void grab_key(Icon *icon)
1351 MyKey *hash_key;
1352 int *count;
1354 g_return_if_fail(icon != NULL);
1356 g_return_if_fail(g_list_find(icon_shortcuts, icon) == NULL);
1358 if (!icon->shortcut_key.keycode)
1359 return;
1361 icon_shortcuts = g_list_prepend(icon_shortcuts, icon);
1363 if (!grab_counter)
1364 grab_counter = g_hash_table_new_full(mykey_hash, mykey_cmp,
1365 g_free, NULL);
1367 count = g_hash_table_lookup(grab_counter, &icon->shortcut_key);
1368 if (count)
1370 (*count)++;
1371 return; /* Already grabbed */
1374 hash_key = g_new(MyKey, 1);
1375 *hash_key = icon->shortcut_key;
1376 count = g_new(int, 1);
1377 *count = 1;
1378 g_hash_table_insert(grab_counter, hash_key, count);
1380 if (!grabKey(&icon->shortcut_key))
1381 g_warning("Failed to grab shortcut '%s' for '%s' icon.\n"
1382 "Some other application may be already using it!\n",
1383 icon->shortcut, icon->item->leafname);
1387 static void icon_wink(Icon *icon)
1389 IconClass *iclass;
1391 iclass = (IconClass *) G_OBJECT_GET_CLASS(icon);
1392 g_return_if_fail(iclass->wink != NULL);
1394 iclass->wink(icon);
1397 /* Sets icon_menu */
1398 static void create_menu(void)
1400 GList *items;
1401 guchar *tmp;
1402 GtkItemFactory *item_factory;
1404 g_return_if_fail(icon_menu == NULL);
1406 item_factory = menu_create(menu_def,
1407 sizeof(menu_def) / sizeof(*menu_def),
1408 "<icon>", NULL);
1410 tmp = g_strconcat("<icon>/", _("File"), NULL);
1411 icon_menu = gtk_item_factory_get_widget(item_factory, "<icon>");
1412 icon_file_menu = gtk_item_factory_get_widget(item_factory, tmp);
1413 g_free(tmp);
1415 /* File '' label... */
1416 items = gtk_container_get_children(GTK_CONTAINER(icon_menu));
1417 icon_file_item = GTK_BIN(g_list_nth(items, 1)->data)->child;
1418 g_list_free(items);
1420 /* Shift Open... label */
1421 items = gtk_container_get_children(GTK_CONTAINER(icon_file_menu));
1422 file_shift_item = GTK_BIN(items->data)->child;
1423 g_list_free(items);
1425 g_signal_connect(icon_menu, "unmap_event",
1426 G_CALLBACK(menu_closed), NULL);