r3660: Panel and pinboard icons can now have a single argument set for them.
[rox-filer/dt.git] / ROX-Filer / src / icon.c
blobfb2ec162f005e05d67ad8c507efd2b80125db422
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2003, the ROX-Filer team.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* icon.c - abstract base class for pinboard and panel icons.
24 * An Icon contains the full pathname of its file and the DirItem for that
25 * file. Icons emit the following signals:
27 * redraw - the image, details or selection state have changed.
28 * update - the name or path has changed.
29 * destroy - someone wishes to remove the icon.
31 * Note that an icon may be removed without emitting 'destroy'.
34 #include "config.h"
36 #include <string.h>
37 #include <gtk/gtk.h>
38 #include <X11/keysym.h>
39 #include <gdk/gdkx.h>
41 #include "global.h"
43 #include "main.h"
44 #include "gui_support.h"
45 #include "support.h"
46 #include "icon.h"
47 #include "diritem.h"
48 #include "menu.h"
49 #include "appmenu.h"
50 #include "dnd.h"
51 #include "run.h"
52 #include "infobox.h"
53 #include "pixmaps.h"
54 #include "mount.h"
55 #include "type.h"
56 #include "usericons.h"
57 #include "pinboard.h" /* For pinboard_set_backdrop */
59 static gboolean have_primary = FALSE; /* We own the PRIMARY selection? */
61 GtkWidget *icon_menu; /* The popup icon menu */
62 static GtkWidget *icon_file_menu; /* The file submenu */
63 static GtkWidget *icon_file_item; /* 'File' label */
64 static GtkWidget *file_shift_item; /* 'Shift Open' label */
66 /* A list of selected Icons. Every icon in the list is from the same group
67 * (eg, you can't have icons from two different panels selected at the
68 * same time).
70 GList *icon_selection = NULL;
72 /* A list of Icons which have grabs in effect. The same combo may be
73 * listed more than once, but has only one X grab.
75 GList *icon_shortcuts = NULL;
77 #define CLICK_TO_SET _("(click to set)")
79 static unsigned int AltMask;
80 static unsigned int MetaMask;
81 static unsigned int NumLockMask;
82 static unsigned int ScrollLockMask;
83 static unsigned int CapsLockMask;
84 static unsigned int SuperMask;
85 static unsigned int HyperMask;
87 /* {MyKey -> Number of grabs} */
88 static GHashTable *grab_counter = NULL;
90 /* Each entry is a GList of Icons which have the given pathname.
91 * This allows us to update all necessary icons when something changes.
93 static GHashTable *icons_hash = NULL; /* path -> [Icon] */
95 static Icon *menu_icon = NULL; /* Item clicked if there is no selection */
97 /* Static prototypes */
98 static void rename_activate(GtkWidget *dialog);
99 static void menu_closed(GtkWidget *widget);
100 static void lose_selection(GtkClipboard *primary, gpointer data);
101 static void selection_get(GtkClipboard *primary,
102 GtkSelectionData *selection_data,
103 guint info,
104 gpointer data);
105 static void remove_items(gpointer data, guint action, GtkWidget *widget);
106 static void set_backdrop(gpointer data, guint action, GtkWidget *widget);
107 static void file_op(gpointer data, guint action, GtkWidget *widget);
108 static void show_rename_box(Icon *icon);
109 static void icon_set_selected_int(Icon *icon, gboolean selected);
110 static void icon_class_init(gpointer gclass, gpointer data);
111 static void icon_init(GTypeInstance *object, gpointer gclass);
112 static void icon_hash_path(Icon *icon);
113 static void icon_unhash_path(Icon *icon);
114 static void menu_set_hidden(GtkWidget *menu, gboolean hidden, int from, int n);
115 static void ungrab_key(Icon *icon);
116 static void grab_key(Icon *icon);
117 static void parseKeyString(MyKey *key, const char *str);
118 static void icon_wink(Icon *icon);
119 static void initModifiers(void);
120 static void create_menu(void);
122 enum {
123 ACTION_SHIFT,
124 ACTION_PROPERTIES,
125 ACTION_RUN_ACTION,
126 ACTION_SET_ICON,
127 ACTION_EDIT,
128 ACTION_LOCATION,
131 #undef N_
132 #define N_(x) x
133 static GtkItemFactoryEntry menu_def[] = {
134 {N_("ROX-Filer"), NULL, NULL, 0, "<Branch>"},
135 {">" N_("About ROX-Filer..."), NULL, menu_rox_help, HELP_ABOUT, "<StockItem>", GTK_STOCK_DIALOG_INFO},
136 {">" N_("Show Help Files"), NULL, menu_rox_help, HELP_DIR, "<StockItem>", GTK_STOCK_HELP},
137 {">" N_("Manual"), NULL, menu_rox_help, HELP_MANUAL, NULL},
138 {">", NULL, NULL, 0, "<Separator>"},
139 {">" N_("Options..."), NULL, menu_show_options, 0, "<StockItem>", GTK_STOCK_PREFERENCES},
140 {">" N_("Home Directory"), NULL, open_home, 0, "<StockItem>", GTK_STOCK_HOME},
141 {N_("File"), NULL, NULL, 0, "<Branch>"},
142 {">" N_("Shift Open"), NULL, file_op, ACTION_SHIFT, NULL},
143 {">" N_("Properties"), NULL, file_op, ACTION_PROPERTIES, "<StockItem>", GTK_STOCK_PROPERTIES},
144 {">" N_("Set Run Action..."), NULL, file_op, ACTION_RUN_ACTION, "<StockItem>", GTK_STOCK_EXECUTE},
145 {">" N_("Set Icon..."), NULL, file_op, ACTION_SET_ICON, NULL},
146 {N_("Edit Item"), NULL, file_op, ACTION_EDIT, "<StockItem>", GTK_STOCK_PROPERTIES},
147 {N_("Show Location"), NULL, file_op, ACTION_LOCATION, "<StockItem>", GTK_STOCK_JUMP_TO},
148 {N_("Remove Item(s)"), NULL, remove_items, 0, "<StockItem>", GTK_STOCK_REMOVE},
149 {"", NULL, NULL, 0, "<Separator>"},
150 {N_("Backdrop..."), NULL, set_backdrop, 0, NULL},
153 /****************************************************************
154 * EXTERNAL INTERFACE *
155 ****************************************************************/
157 /* Called when the pointer moves over the icon */
158 void icon_may_update(Icon *icon)
160 MaskedPixmap *image;
161 int flags;
163 g_return_if_fail(icon != NULL);
165 image = icon->item->image;
166 flags = icon->item->flags;
168 if (image)
169 g_object_ref(image);
170 mount_update(FALSE);
171 diritem_restat(icon->path, icon->item, NULL);
173 if (icon->item->image != image || icon->item->flags != flags)
175 /* Appearance changed; need to redraw */
176 g_signal_emit_by_name(icon, "update");
179 if (image)
180 g_object_unref(image);
183 /* If path is on an icon then it may have changed... check! */
184 void icons_may_update(const gchar *path)
186 GList *affected;
188 if (icons_hash)
190 affected = g_hash_table_lookup(icons_hash, path);
192 for (; affected; affected = affected->next)
193 icon_may_update((Icon *) affected->data);
197 typedef struct _CheckData CheckData;
198 struct _CheckData {
199 const gchar *path;
200 gboolean found;
203 static void check_has(gpointer key, GList *icons, CheckData *check)
205 Icon *icon;
207 g_return_if_fail(icons != NULL);
209 icon = icons->data;
211 if (is_sub_dir(icon->path, check->path))
212 check->found = TRUE;
215 /* Returns TRUE if any icon links to this file (or any file inside
216 * this directory). Used to check that it's OK to delete 'path'.
218 gboolean icons_require(const gchar *path)
220 CheckData check;
222 if (!icons_hash)
223 return FALSE;
225 check.path = path;
226 check.found = FALSE;
227 g_hash_table_foreach(icons_hash, (GHFunc) check_has, &check);
229 return check.found;
232 /* Menu was clicked over this icon. Set things up correctly (shade items,
233 * add app menu stuff, etc).
234 * You should show icon_menu after calling this...
236 void icon_prepare_menu(Icon *icon, gboolean pinboard)
238 appmenu_remove();
240 menu_icon = icon;
242 if (!icon_menu)
243 create_menu();
245 menu_set_hidden(icon_menu, !pinboard, 5, 2);
247 /* Shade Remove Item(s) unless there is a selection */
248 menu_set_items_shaded(icon_menu,
249 (icon_selection || menu_icon) ? FALSE : TRUE,
250 4, 1);
252 menu_show_shift_action(file_shift_item, icon ? icon->item : NULL,
253 FALSE);
255 /* Shade the File/Edit/Show items unless an item was clicked */
256 if (icon)
258 guchar *tmp;
260 menu_set_items_shaded(icon_menu, FALSE, 1, 3);
261 menu_set_items_shaded(icon_file_menu, FALSE, 0, 5);
262 if (!can_set_run_action(icon->item))
263 menu_set_items_shaded(icon_file_menu, TRUE, 2, 1);
265 tmp = g_strdup_printf(_("%s '%s'"),
266 basetype_name(icon->item),
267 icon->item->leafname);
268 gtk_label_set_text(GTK_LABEL(icon_file_item), tmp);
269 g_free(tmp);
271 /* Check for app-specific menu */
272 appmenu_add(icon->path, icon->item, icon_menu);
274 else
276 menu_set_items_shaded(icon_menu, TRUE, 1, 3);
277 menu_set_items_shaded(icon_file_menu, TRUE, 0, 5);
278 gtk_label_set_text(GTK_LABEL(icon_file_item), _("Nothing"));
282 /* Set whether this icon is selected. Will automatically clear the selection
283 * if it contains icons from a different group.
285 void icon_set_selected(Icon *icon, gboolean selected)
287 if (selected && icon_selection)
289 IconClass *iclass;
290 Icon *other = (Icon *) icon_selection->data;
292 iclass = (IconClass *) G_OBJECT_GET_CLASS(icon);
293 if (!iclass->same_group(icon, other))
295 icon_select_only(icon);
296 return;
300 icon_set_selected_int(icon, selected);
303 /* Clear everything, except 'select', which is selected.
304 * If select is NULL, unselects everything.
306 void icon_select_only(Icon *select)
308 GList *to_clear, *next;
310 if (select)
311 icon_set_selected_int(select, TRUE);
313 to_clear = g_list_copy(icon_selection);
315 if (select)
316 to_clear = g_list_remove(to_clear, select);
318 for (next = to_clear; next; next = next->next)
319 icon_set_selected_int((Icon *) next->data, FALSE);
321 g_list_free(to_clear);
324 /* Destroys this icon's widget, causing it to be removed from the screen */
325 void icon_destroy(Icon *icon)
327 g_return_if_fail(icon != NULL);
329 icon_set_selected_int(icon, FALSE);
331 g_signal_emit_by_name(icon, "destroy");
334 /* Return a text/uri-list of all the icons in the selection */
335 gchar *icon_create_uri_list(void)
337 GString *tmp;
338 guchar *retval;
339 GList *next;
341 tmp = g_string_new(NULL);
343 for (next = icon_selection; next; next = next->next)
345 Icon *icon = (Icon *) next->data;
346 gchar *uri;
348 uri = encode_path_as_uri(icon->path);
349 g_string_append(tmp, uri);
350 g_free(uri);
351 g_string_append(tmp, "\r\n");
354 retval = tmp->str;
355 g_string_free(tmp, FALSE);
357 return retval;
360 GType icon_get_type(void)
362 static GType type = 0;
364 if (!type)
366 static const GTypeInfo info =
368 sizeof (IconClass),
369 NULL, /* base_init */
370 NULL, /* base_finalise */
371 icon_class_init,
372 NULL, /* class_finalise */
373 NULL, /* class_data */
374 sizeof(Icon),
375 0, /* n_preallocs */
376 icon_init
379 type = g_type_register_static(G_TYPE_OBJECT, "Icon",
380 &info, 0);
383 return type;
386 /* Sets, unsets or changes the pathname and name for an icon.
387 * Updates icons_hash and (re)stats the item.
388 * If name is NULL then gets the leafname from pathname.
389 * If pathname is NULL, frees everything.
391 void icon_set_path(Icon *icon, const char *pathname, const char *name)
393 if (icon->path)
395 icon_unhash_path(icon);
396 icon->src_path = NULL;
397 icon->path = NULL;
399 diritem_free(icon->item);
400 icon->item = NULL;
403 if (pathname)
405 if (g_utf8_validate(pathname, -1, NULL))
406 icon->src_path = g_strdup(pathname);
407 else
408 icon->src_path = to_utf8(pathname);
409 icon->path = expand_path(icon->src_path);
411 icon_hash_path(icon);
413 if (!name)
414 name = g_basename(icon->src_path);
416 icon->item = diritem_new(name);
417 diritem_restat(icon->path, icon->item, NULL);
421 void icon_set_shortcut(Icon *icon, const gchar *shortcut)
423 g_return_if_fail(icon != NULL);
425 if (shortcut && !*shortcut)
426 shortcut = NULL;
427 if (icon->shortcut == shortcut)
428 return;
429 if (icon->shortcut && shortcut && strcmp(icon->shortcut, shortcut) == 0)
430 return;
432 initModifiers();
434 ungrab_key(icon);
436 g_free(icon->shortcut);
437 icon->shortcut = g_strdup(shortcut);
438 parseKeyString(&icon->shortcut_key, shortcut);
440 grab_key(icon);
443 void icon_set_argument(Icon *icon, const gchar *arg)
445 g_return_if_fail(icon != NULL);
447 if (arg && !*arg)
448 arg = NULL;
449 if (icon->arg == arg)
450 return;
451 if (icon->arg && arg && strcmp(icon->arg, arg) == 0)
452 return;
454 g_free(icon->arg);
455 icon->arg = g_strdup(arg);
458 /****************************************************************
459 * INTERNAL FUNCTIONS *
460 ****************************************************************/
462 /* The icons_hash table allows us to convert from a path to a list
463 * of icons that use that path.
464 * Add this icon to the list for its path.
466 static void icon_hash_path(Icon *icon)
468 GList *list;
470 g_return_if_fail(icon != NULL);
472 /* g_print("[ hashing '%s' ]\n", icon->path); */
474 list = g_hash_table_lookup(icons_hash, icon->path);
475 list = g_list_prepend(list, icon);
476 g_hash_table_insert(icons_hash, icon->path, list);
479 /* Remove this icon from the icons_hash table */
480 static void icon_unhash_path(Icon *icon)
482 GList *list;
484 g_return_if_fail(icon != NULL);
486 /* g_print("[ unhashing '%s' ]\n", icon->path); */
488 list = g_hash_table_lookup(icons_hash, icon->path);
489 g_return_if_fail(list != NULL);
491 list = g_list_remove(list, icon);
493 /* Remove it first; the hash key may have changed address */
494 g_hash_table_remove(icons_hash, icon->path);
495 if (list)
496 g_hash_table_insert(icons_hash,
497 ((Icon *) list->data)->path, list);
500 static void rename_activate(GtkWidget *dialog)
502 GtkWidget *entry, *src, *shortcut, *arg;
503 Icon *icon;
504 const guchar *new_name, *new_src, *new_shortcut, *new_arg;
506 entry = g_object_get_data(G_OBJECT(dialog), "new_name");
507 icon = g_object_get_data(G_OBJECT(dialog), "callback_icon");
508 src = g_object_get_data(G_OBJECT(dialog), "new_path");
509 shortcut = g_object_get_data(G_OBJECT(dialog), "new_shortcut");
510 arg = g_object_get_data(G_OBJECT(dialog), "new_arg");
512 g_return_if_fail(entry != NULL &&
513 src != NULL &&
514 icon != NULL &&
515 shortcut != NULL &&
516 arg != NULL);
518 new_name = gtk_entry_get_text(GTK_ENTRY(entry));
519 new_src = gtk_entry_get_text(GTK_ENTRY(src));
520 new_shortcut = gtk_label_get_text(GTK_LABEL(shortcut));
521 if (strcmp(new_shortcut, CLICK_TO_SET) == 0)
522 new_shortcut = NULL;
523 new_arg = gtk_entry_get_text(GTK_ENTRY(arg));
525 if (*new_src == '\0')
526 report_error(
527 _("The location must contain at least one character!"));
528 else
530 icon_set_path(icon, new_src, new_name);
531 icon_set_shortcut(icon, new_shortcut);
532 icon_set_argument(icon, new_arg);
533 g_signal_emit_by_name(icon, "update");
534 gtk_widget_destroy(dialog);
538 static void menu_closed(GtkWidget *widget)
540 appmenu_remove();
541 menu_icon = NULL;
544 /* Called when another application takes the selection away from us */
545 static void lose_selection(GtkClipboard *primary, gpointer data)
547 have_primary = FALSE;
548 icon_select_only(NULL);
551 /* Called when another application wants the contents of our selection */
552 static void selection_get(GtkClipboard *primary,
553 GtkSelectionData *selection_data,
554 guint info,
555 gpointer data)
557 gchar *text;
559 if (info == TARGET_URI_LIST)
560 text = icon_create_uri_list();
561 else
563 GList *next;
564 GString *str;
566 str = g_string_new(NULL);
568 for (next = icon_selection; next; next = next->next)
570 Icon *icon = (Icon *) next->data;
572 g_string_append(str, icon->path);
573 g_string_append_c(str, ' ');
576 text = str->str;
577 g_string_free(str, FALSE);
580 gtk_selection_data_set(selection_data,
581 gdk_atom_intern("STRING", FALSE),
582 8, text, strlen(text));
585 static void set_backdrop(gpointer data, guint action, GtkWidget *widget)
587 pinboard_set_backdrop_box();
590 static void remove_items(gpointer data, guint action, GtkWidget *widget)
592 IconClass *iclass;
594 if (menu_icon)
595 icon_set_selected(menu_icon, TRUE);
597 if (!icon_selection)
599 delayed_error(
600 _("You must first select some items to remove"));
601 return;
604 iclass = (IconClass *)
605 G_OBJECT_GET_CLASS(G_OBJECT(icon_selection->data));
607 iclass->remove_items();
610 static void file_op(gpointer data, guint action, GtkWidget *widget)
612 if (!menu_icon)
614 delayed_error(_("You must open the menu over an item"));
615 return;
618 switch (action)
620 case ACTION_SHIFT:
621 run_diritem(menu_icon->path, menu_icon->item,
622 NULL, NULL, TRUE);
623 break;
624 case ACTION_EDIT:
625 show_rename_box(menu_icon);
626 break;
627 case ACTION_LOCATION:
628 open_to_show(menu_icon->path);
629 break;
630 case ACTION_PROPERTIES:
631 infobox_new(menu_icon->path);
632 break;
633 case ACTION_RUN_ACTION:
634 if (can_set_run_action(menu_icon->item))
635 type_set_handler_dialog(
636 menu_icon->item->mime_type);
637 else
638 report_error(
639 _("You can only set the run action for a "
640 "regular file"));
641 break;
642 case ACTION_SET_ICON:
643 icon_set_handler_dialog(menu_icon->item,
644 menu_icon->path);
645 break;
649 static void edit_response(GtkWidget *dialog, gint response, gpointer data)
651 if (response == GTK_RESPONSE_OK)
652 rename_activate(dialog);
653 else if (response == GTK_RESPONSE_CANCEL)
654 gtk_widget_destroy(dialog);
657 static GdkFilterReturn filter_get_key(GdkXEvent *xevent,
658 GdkEvent *event,
659 gpointer data)
661 XKeyEvent *kev = (XKeyEvent *) xevent;
662 GtkWidget *popup = (GtkWidget *) data;
663 Display *dpy = GDK_DISPLAY();
665 if (kev->type != KeyRelease && kev->type != ButtonPressMask)
666 return GDK_FILTER_CONTINUE;
668 initModifiers();
670 if (kev->type == KeyRelease)
672 gchar *str;
673 KeySym sym;
674 unsigned int m = kev->state;
676 sym = XKeycodeToKeysym(dpy, kev->keycode, 0);
677 if (!sym)
678 return GDK_FILTER_CONTINUE;
680 str = g_strdup_printf("%s%s%s%s%s%s%s",
681 m & ControlMask ? "Control+" : "",
682 m & ShiftMask ? "Shift+" : "",
683 m & AltMask ? "Alt+" : "",
684 m & MetaMask ? "Meta+" : "",
685 m & SuperMask ? "Super+" : "",
686 m & HyperMask ? "Hyper+" : "",
687 XKeysymToString(sym));
689 g_object_set_data(G_OBJECT(popup), "chosen-key", str);
692 gdk_window_remove_filter(popup->window, filter_get_key, data);
693 gtk_widget_destroy(popup);
695 return GDK_FILTER_REMOVE;
698 static void may_set_shortcut(GtkWidget *popup, GtkWidget *label)
700 gchar *str;
702 str = g_object_get_data(G_OBJECT(popup), "chosen-key");
703 if (str)
705 gtk_label_set_text(GTK_LABEL(label), str);
706 g_free(str);
707 g_object_set_data(G_OBJECT(popup), "chosen-key", NULL);
711 static void get_shortcut(GtkWidget *button, GtkWidget *label)
713 GtkWidget *popup, *frame, *msg;
714 Window xid;
715 Display *dpy = GDK_DISPLAY();
717 popup = gtk_window_new(GTK_WINDOW_POPUP);
719 gtk_window_set_position(GTK_WINDOW(popup), GTK_WIN_POS_CENTER);
721 frame = gtk_frame_new(NULL);
722 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
723 gtk_container_add(GTK_CONTAINER(popup), frame);
725 msg = gtk_label_new(_("Press the desired shortcut (eg, Control+F1)"));
727 gtk_misc_set_padding(GTK_MISC(msg), 20, 20);
728 gtk_container_add(GTK_CONTAINER(frame), msg);
730 gtk_window_set_modal(GTK_WINDOW(popup), TRUE);
732 gtk_widget_add_events(popup,
733 GDK_KEY_RELEASE_MASK | GDK_BUTTON_PRESS_MASK);
735 g_signal_connect(popup, "destroy",
736 G_CALLBACK(may_set_shortcut), label);
738 gtk_widget_show_all(popup);
740 gdk_window_add_filter(popup->window, filter_get_key, popup);
742 xid = gdk_x11_drawable_get_xid(popup->window);
744 if (XGrabKeyboard(dpy, xid, False, GrabModeAsync, GrabModeAsync,
745 gtk_get_current_event_time()) != Success)
747 delayed_error(_("Failed to get keyboard grab!"));
748 gtk_widget_destroy(popup);
751 if (XGrabPointer(dpy, xid, False, ButtonPressMask, GrabModeAsync,
752 GrabModeAsync, xid, None,
753 gtk_get_current_event_time()) != Success)
755 g_warning("Failed to get mouse grab");
759 static void clear_shortcut(GtkButton *clear, GtkLabel *label)
761 gtk_label_set_text(GTK_LABEL(label), CLICK_TO_SET);
764 /* Opens a box allowing the user to change the name of a pinned icon.
765 * If the icon is destroyed then the box will close.
766 * If the user chooses OK then the callback is called once the icon's
767 * name, src_path and path fields have been updated and the item field
768 * restatted.
770 static void show_rename_box(Icon *icon)
772 GtkDialog *dialog;
773 GtkWidget *label, *entry, *button, *button2, *hbox, *spacer;
774 GtkBox *vbox;
776 if (icon->dialog)
778 gtk_window_present(GTK_WINDOW(icon->dialog));
779 return;
782 icon->dialog = gtk_dialog_new();
783 g_signal_connect(icon->dialog, "destroy",
784 G_CALLBACK(gtk_widget_destroyed), &icon->dialog);
786 dialog = GTK_DIALOG(icon->dialog);
788 vbox = GTK_BOX(gtk_vbox_new(FALSE, 1));
789 gtk_box_pack_start(GTK_BOX(dialog->vbox), (GtkWidget *) vbox,
790 TRUE, TRUE, 0);
791 gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
793 gtk_window_set_title(GTK_WINDOW(dialog), _("Edit Item"));
794 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
796 label = gtk_label_new(_("Clicking the icon opens:"));
797 gtk_box_pack_start(vbox, label, TRUE, TRUE, 0);
799 entry = gtk_entry_new();
800 gtk_box_pack_start(vbox, entry, TRUE, FALSE, 2);
801 gtk_entry_set_text(GTK_ENTRY(entry), icon->src_path);
802 g_object_set_data(G_OBJECT(dialog), "new_path", entry);
803 g_signal_connect_swapped(entry, "activate",
804 G_CALLBACK(rename_activate), dialog);
806 label = gtk_label_new(_("Argument to pass (for executables):"));
807 gtk_box_pack_start(vbox, label, TRUE, TRUE, 0);
809 entry = gtk_entry_new();
810 gtk_box_pack_start(vbox, entry, TRUE, FALSE, 2);
811 gtk_entry_set_text(GTK_ENTRY(entry), icon->arg? icon->arg: "");
812 g_object_set_data(G_OBJECT(dialog), "new_arg", entry);
813 g_signal_connect_swapped(entry, "activate",
814 G_CALLBACK(rename_activate), dialog);
816 spacer = gtk_drawing_area_new();
817 gtk_widget_set_size_request(spacer, 4, 4);
818 gtk_box_pack_start(vbox, spacer, FALSE, FALSE, 0);
820 label = gtk_label_new(_("The text displayed under the icon is:"));
821 gtk_box_pack_start(vbox, label, TRUE, TRUE, 0);
822 entry = gtk_entry_new();
823 gtk_box_pack_start(vbox, entry, TRUE, FALSE, 2);
824 gtk_entry_set_text(GTK_ENTRY(entry), icon->item->leafname);
825 gtk_widget_grab_focus(entry);
826 g_object_set_data(G_OBJECT(dialog), "new_name", entry);
827 gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
829 spacer = gtk_drawing_area_new();
830 gtk_widget_set_size_request(spacer, 4, 4);
831 gtk_box_pack_start(vbox, spacer, FALSE, FALSE, 0);
833 label = gtk_label_new(_("The keyboard shortcut is:"));
834 gtk_box_pack_start(vbox, label, TRUE, TRUE, 0);
836 hbox = gtk_hbox_new(FALSE, 2);
837 gtk_box_pack_start(vbox, hbox, TRUE, FALSE, 0);
838 button = gtk_button_new_with_label(icon->shortcut
839 ? icon->shortcut
840 : CLICK_TO_SET);
841 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
842 g_object_set_data(G_OBJECT(dialog), "new_shortcut",
843 GTK_BIN(button)->child);
844 g_signal_connect(button, "clicked",
845 G_CALLBACK(get_shortcut),
846 GTK_BIN(button)->child);
847 button2 = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
848 gtk_box_pack_start(GTK_BOX(hbox), button2, FALSE, FALSE, 0);
849 g_signal_connect(button2, "clicked",
850 G_CALLBACK(clear_shortcut),
851 GTK_BIN(button)->child);
853 g_object_set_data(G_OBJECT(dialog), "callback_icon", icon);
855 gtk_dialog_add_buttons(dialog,
856 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
857 GTK_STOCK_OK, GTK_RESPONSE_OK,
858 NULL);
859 gtk_dialog_set_default_response(dialog, GTK_RESPONSE_OK);
861 g_signal_connect(dialog, "response", G_CALLBACK(edit_response), NULL);
863 gtk_widget_show_all(GTK_WIDGET(dialog));
866 static gpointer parent_class = NULL;
868 static void icon_finialize(GObject *object)
870 Icon *icon = (Icon *) object;
872 g_return_if_fail(!icon->selected);
874 if (icon->dialog)
875 gtk_widget_destroy(icon->dialog);
877 if (icon == menu_icon)
878 menu_icon = NULL;
880 icon_set_path(icon, NULL, NULL);
881 icon_set_shortcut(icon, NULL);
882 icon_set_argument(icon, NULL);
884 G_OBJECT_CLASS(parent_class)->finalize(object);
887 static void icon_class_init(gpointer gclass, gpointer data)
889 GObjectClass *object = (GObjectClass *) gclass;
890 IconClass *icon = (IconClass *) gclass;
892 parent_class = g_type_class_peek_parent(gclass);
894 object->finalize = icon_finialize;
895 icon->destroy = NULL;
896 icon->redraw = NULL;
897 icon->update = NULL;
898 icon->same_group = NULL;
899 icon->wink = NULL;
901 g_signal_new("update",
902 G_TYPE_FROM_CLASS(gclass),
903 G_SIGNAL_RUN_LAST,
904 G_STRUCT_OFFSET(IconClass, update),
905 NULL, NULL,
906 g_cclosure_marshal_VOID__VOID,
907 G_TYPE_NONE, 0);
909 g_signal_new("destroy",
910 G_TYPE_FROM_CLASS(gclass),
911 G_SIGNAL_RUN_LAST,
912 G_STRUCT_OFFSET(IconClass, destroy),
913 NULL, NULL,
914 g_cclosure_marshal_VOID__VOID,
915 G_TYPE_NONE, 0);
917 g_signal_new("redraw",
918 G_TYPE_FROM_CLASS(gclass),
919 G_SIGNAL_RUN_LAST,
920 G_STRUCT_OFFSET(IconClass, redraw),
921 NULL, NULL,
922 g_cclosure_marshal_VOID__VOID,
923 G_TYPE_NONE, 0);
925 icons_hash = g_hash_table_new(g_str_hash, g_str_equal);
928 static void icon_init(GTypeInstance *object, gpointer gclass)
930 Icon *icon = (Icon *) object;
932 icon->selected = FALSE;
933 icon->src_path = NULL;
934 icon->path = NULL;
935 icon->item = NULL;
936 icon->dialog = NULL;
937 icon->shortcut = NULL;
938 icon->shortcut_key.keycode = 0;
939 icon->shortcut_key.modifier = 0;
940 icon->arg = NULL;
943 /* As icon_set_selected(), but doesn't automatically unselect incompatible
944 * icons.
946 static void icon_set_selected_int(Icon *icon, gboolean selected)
948 static GtkClipboard *primary;
950 g_return_if_fail(icon != NULL);
952 if (icon->selected == selected)
953 return;
955 if (!primary)
956 primary = gtk_clipboard_get(gdk_atom_intern("PRIMARY", FALSE));
958 if (selected)
960 icon_selection = g_list_prepend(icon_selection, icon);
961 if (!have_primary)
963 GtkTargetEntry target_table[] =
965 {"text/uri-list", 0, TARGET_URI_LIST},
966 {"UTF8", 0, TARGET_STRING},
967 {"COMPOUND_TEXT", 0, TARGET_STRING},
968 {"STRING", 0, TARGET_STRING},
971 /* Grab selection */
972 have_primary = gtk_clipboard_set_with_data(primary,
973 target_table,
974 sizeof(target_table) / sizeof(*target_table),
975 selection_get, lose_selection, NULL);
978 else
980 icon_selection = g_list_remove(icon_selection, icon);
981 if (have_primary && !icon_selection)
983 have_primary = FALSE;
984 gtk_clipboard_clear(primary);
988 icon->selected = selected;
989 g_signal_emit_by_name(icon, "redraw");
992 static void menu_set_hidden(GtkWidget *menu, gboolean hidden, int from, int n)
994 GList *items, *item;
996 items = gtk_container_get_children(GTK_CONTAINER(menu));
998 item = g_list_nth(items, from);
999 while (item && n--)
1001 if (hidden)
1002 gtk_widget_hide(GTK_WIDGET(item->data));
1003 else
1004 gtk_widget_show(GTK_WIDGET(item->data));
1005 item = item->next;
1007 g_list_free(items);
1010 /* Stolen from xfwm4 */
1011 static void initModifiers(void)
1013 static gboolean need_init = TRUE;
1014 Display *dpy = GDK_DISPLAY();
1015 XModifierKeymap *xmk = XGetModifierMapping(dpy);
1016 int m, k;
1018 if (!need_init)
1019 return;
1020 need_init = FALSE;
1022 AltMask = MetaMask = NumLockMask = ScrollLockMask = CapsLockMask =
1023 SuperMask = HyperMask = 0;
1025 /* Work out which mask to use for each modifier group */
1026 if (xmk)
1028 KeyCode *c = xmk->modifiermap;
1029 KeyCode numLockKeyCode;
1030 KeyCode scrollLockKeyCode;
1031 KeyCode capsLockKeyCode;
1032 KeyCode altKeyCode;
1033 KeyCode metaKeyCode;
1034 KeyCode superKeyCode;
1035 KeyCode hyperKeyCode;
1037 /* Find the codes to search for... */
1038 numLockKeyCode = XKeysymToKeycode(dpy, XK_Num_Lock);
1039 scrollLockKeyCode = XKeysymToKeycode(dpy, XK_Scroll_Lock);
1040 capsLockKeyCode = XKeysymToKeycode(dpy, XK_Caps_Lock);
1041 altKeyCode = XKeysymToKeycode(dpy, XK_Alt_L);
1042 metaKeyCode = XKeysymToKeycode(dpy, XK_Meta_L);
1043 superKeyCode = XKeysymToKeycode(dpy, XK_Super_L);
1044 hyperKeyCode = XKeysymToKeycode(dpy, XK_Hyper_L);
1046 /* If some are missing, try alternatives... */
1047 if (!altKeyCode)
1048 altKeyCode = XKeysymToKeycode(dpy, XK_Alt_R);
1049 if (!metaKeyCode)
1050 metaKeyCode = XKeysymToKeycode(dpy, XK_Meta_R);
1051 if (!superKeyCode)
1052 superKeyCode = XKeysymToKeycode(dpy, XK_Super_R);
1053 if (!hyperKeyCode)
1054 hyperKeyCode = XKeysymToKeycode(dpy, XK_Hyper_R);
1056 /* Check each of the eight modifier lists.
1057 * The idea (I think) is that we name the modifier group which
1058 * includes the Alt key as the 'Alt group', and so on for
1059 * the other modifiers.
1061 for (m = 0; m < 8; m++)
1063 for (k = 0; k < xmk->max_keypermod; k++, c++)
1065 if (*c == NoSymbol)
1066 continue;
1067 if (*c == numLockKeyCode)
1068 NumLockMask = (1 << m);
1069 if (*c == scrollLockKeyCode)
1070 ScrollLockMask = (1 << m);
1071 if (*c == capsLockKeyCode)
1072 CapsLockMask = (1 << m);
1073 if (*c == altKeyCode)
1074 AltMask = (1 << m);
1075 if (*c == metaKeyCode)
1076 MetaMask = (1 << m);
1077 if (*c == superKeyCode)
1078 SuperMask = (1 << m);
1079 if (*c == hyperKeyCode)
1080 HyperMask = (1 << m);
1083 XFreeModifiermap(xmk);
1086 if(MetaMask == AltMask)
1087 MetaMask = 0;
1089 if (AltMask != 0 && MetaMask == Mod1Mask)
1091 MetaMask = AltMask;
1092 AltMask = Mod1Mask;
1095 if (AltMask == 0 && MetaMask != 0)
1097 if (MetaMask != Mod1Mask)
1098 AltMask = Mod1Mask;
1099 else
1101 AltMask = MetaMask;
1102 MetaMask = 0;
1106 if (AltMask == 0)
1107 AltMask = Mod1Mask;
1111 /* Fill in key from str. Sets keycode to zero if str is NULL.
1112 * Stolen from xfwm4 and modified. Call initModifiers before this.
1114 static void parseKeyString(MyKey *key, const char *str)
1116 char *k;
1117 Display *dpy = GDK_DISPLAY();
1119 key->keycode = 0;
1120 key->modifier = 0;
1122 if (!str)
1123 return;
1125 k = strrchr(str, '+');
1126 key->keycode = XKeysymToKeycode(dpy, XStringToKeysym(k ? k + 1 : str));
1127 if (k)
1129 gchar *tmp;
1131 tmp = g_ascii_strdown(str, -1);
1133 if (strstr(tmp, "shift"))
1134 key->modifier = key->modifier | ShiftMask;
1135 if (strstr(tmp, "control"))
1136 key->modifier = key->modifier | ControlMask;
1137 if (strstr(tmp, "alt") || strstr(tmp, "mod1"))
1138 key->modifier = key->modifier | AltMask;
1139 if (strstr(tmp, "meta") || strstr(tmp, "mod2"))
1140 key->modifier = key->modifier | MetaMask;
1141 if (strstr(tmp, "hyper"))
1142 key->modifier = key->modifier | HyperMask;
1143 if (strstr(tmp, "super"))
1144 key->modifier = key->modifier | SuperMask;
1146 g_free(tmp);
1149 if (!key->keycode)
1150 g_warning("Can't parse key '%s'\n", str);
1153 static GdkFilterReturn filter_keys(GdkXEvent *xevent,
1154 GdkEvent *event,
1155 gpointer data)
1157 GList *next;
1158 XKeyEvent *kev = (XKeyEvent *) xevent;
1159 guint state;
1161 if (kev->type != KeyPress)
1162 return GDK_FILTER_CONTINUE;
1164 state = kev->state & (ShiftMask | ControlMask | AltMask | MetaMask |
1165 HyperMask | SuperMask);
1167 for (next = icon_shortcuts; next; next = next->next)
1169 Icon *icon = (Icon *) next->data;
1171 if (icon->shortcut_key.keycode == kev->keycode &&
1172 icon->shortcut_key.modifier == state)
1174 icon_wink(icon);
1175 run_diritem_with_arg(icon->path, icon->item, icon->arg,
1176 NULL, NULL, FALSE);
1180 return GDK_FILTER_CONTINUE;
1183 #define GRAB(key, mods) XGrabKey(dpy, key->keycode, key->modifier | mods, \
1184 root, False, GrabModeAsync, GrabModeAsync)
1185 #define UNGRAB(key, mods) XUngrabKey(dpy, key->keycode, key->modifier | mods, \
1186 root)
1188 static guint mykey_hash(gconstpointer key)
1190 MyKey *k = (MyKey *) key;
1192 return (k->keycode << 8) + k->modifier;
1195 static gboolean mykey_cmp(gconstpointer a, gconstpointer b)
1197 MyKey *ka = (MyKey *) a;
1198 MyKey *kb = (MyKey *) b;
1200 return ka->keycode == kb->keycode && kb->modifier == kb->modifier;
1203 /* Stolen from xfwm4 and modified.
1204 * FALSE on error. Call initModifiers before this.
1206 static gboolean grabKey(MyKey *key)
1208 Window root;
1209 Display *dpy = GDK_DISPLAY();
1210 static gboolean need_init = TRUE;
1212 if (need_init)
1214 need_init = FALSE;
1215 gdk_window_add_filter(gdk_get_default_root_window(),
1216 filter_keys, NULL);
1219 gdk_error_trap_push();
1221 root = GDK_ROOT_WINDOW();
1223 GRAB(key, 0);
1225 /* Here we grab all combinations of well known modifiers */
1226 GRAB(key, ScrollLockMask);
1227 GRAB(key, NumLockMask);
1228 GRAB(key, CapsLockMask);
1229 GRAB(key, ScrollLockMask | NumLockMask);
1230 GRAB(key, ScrollLockMask | CapsLockMask);
1231 GRAB(key, CapsLockMask | NumLockMask);
1232 GRAB(key, ScrollLockMask | CapsLockMask | NumLockMask);
1234 gdk_flush();
1235 return gdk_error_trap_pop() == Success;
1238 static gboolean ungrabKey(MyKey *key)
1240 Window root = GDK_ROOT_WINDOW();
1241 Display *dpy = GDK_DISPLAY();
1243 gdk_error_trap_push();
1245 UNGRAB(key, 0);
1247 UNGRAB(key, ScrollLockMask);
1248 UNGRAB(key, NumLockMask);
1249 UNGRAB(key, CapsLockMask);
1250 UNGRAB(key, ScrollLockMask | NumLockMask);
1251 UNGRAB(key, ScrollLockMask | CapsLockMask);
1252 UNGRAB(key, CapsLockMask | NumLockMask);
1253 UNGRAB(key, ScrollLockMask | CapsLockMask | NumLockMask);
1255 gdk_flush();
1256 return gdk_error_trap_pop() == Success;
1259 static void ungrab_key(Icon *icon)
1261 int *count;
1263 g_return_if_fail(icon != NULL);
1265 if (!icon->shortcut_key.keycode)
1266 return;
1268 icon_shortcuts = g_list_remove(icon_shortcuts, icon);
1270 count = g_hash_table_lookup(grab_counter, &icon->shortcut_key);
1271 g_return_if_fail(count != NULL);
1273 (*count)--;
1275 if (*count > 0)
1276 return;
1278 g_hash_table_remove(grab_counter, &icon->shortcut_key);
1280 if (!ungrabKey(&icon->shortcut_key))
1281 g_warning("Failed to ungrab shortcut '%s' for '%s' icon.",
1282 icon->shortcut, icon->item->leafname);
1285 static void grab_key(Icon *icon)
1287 MyKey *hash_key;
1288 int *count;
1290 g_return_if_fail(icon != NULL);
1292 g_return_if_fail(g_list_find(icon_shortcuts, icon) == NULL);
1294 if (!icon->shortcut_key.keycode)
1295 return;
1297 icon_shortcuts = g_list_prepend(icon_shortcuts, icon);
1299 if (!grab_counter)
1300 grab_counter = g_hash_table_new_full(mykey_hash, mykey_cmp,
1301 g_free, NULL);
1303 count = g_hash_table_lookup(grab_counter, &icon->shortcut_key);
1304 if (count)
1306 (*count)++;
1307 return; /* Already grabbed */
1310 hash_key = g_new(MyKey, 1);
1311 *hash_key = icon->shortcut_key;
1312 count = g_new(int, 1);
1313 *count = 1;
1314 g_hash_table_insert(grab_counter, hash_key, count);
1316 if (!grabKey(&icon->shortcut_key))
1317 g_warning("Failed to grab shortcut '%s' for '%s' icon.\n"
1318 "Some other application may be already using it!\n",
1319 icon->shortcut, icon->item->leafname);
1323 static void icon_wink(Icon *icon)
1325 IconClass *iclass;
1327 iclass = (IconClass *) G_OBJECT_GET_CLASS(icon);
1328 g_return_if_fail(iclass->wink != NULL);
1330 iclass->wink(icon);
1333 /* Sets icon_menu */
1334 static void create_menu(void)
1336 GList *items;
1337 guchar *tmp;
1338 GtkItemFactory *item_factory;
1340 g_return_if_fail(icon_menu == NULL);
1342 item_factory = menu_create(menu_def,
1343 sizeof(menu_def) / sizeof(*menu_def),
1344 "<icon>", NULL);
1346 tmp = g_strconcat("<icon>/", _("File"), NULL);
1347 icon_menu = gtk_item_factory_get_widget(item_factory, "<icon>");
1348 icon_file_menu = gtk_item_factory_get_widget(item_factory, tmp);
1349 g_free(tmp);
1351 /* File '' label... */
1352 items = gtk_container_get_children(GTK_CONTAINER(icon_menu));
1353 icon_file_item = GTK_BIN(g_list_nth(items, 1)->data)->child;
1354 g_list_free(items);
1356 /* Shift Open... label */
1357 items = gtk_container_get_children(GTK_CONTAINER(icon_file_menu));
1358 file_shift_item = GTK_BIN(items->data)->child;
1359 g_list_free(items);
1361 g_signal_connect(icon_menu, "unmap_event",
1362 G_CALLBACK(menu_closed), NULL);