r4275: If we can't find libc.so.6 for xattr support, try libc.so in case we only...
[rox-filer.git] / ROX-Filer / src / icon.c
blob81ac6054978dd0c05d7156274f8488d3c441ef7c
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2005, 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_box */
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 */
65 static GtkWidget *current_options_item; /* Pin/Pan Options */
67 /* A list of selected Icons. Every icon in the list is from the same group
68 * (eg, you can't have icons from two different panels selected at the
69 * same time).
71 GList *icon_selection = NULL;
73 /* A list of Icons which have grabs in effect. The same combo may be
74 * listed more than once, but has only one X grab.
76 GList *icon_shortcuts = NULL;
78 #define CLICK_TO_SET _("(click to set)")
80 static unsigned int AltMask;
81 static unsigned int MetaMask;
82 static unsigned int NumLockMask;
83 static unsigned int ScrollLockMask;
84 static unsigned int CapsLockMask;
85 static unsigned int SuperMask;
86 static unsigned int HyperMask;
88 /* {MyKey -> Number of grabs} */
89 static GHashTable *grab_counter = NULL;
91 /* Each entry is a GList of Icons which have the given pathname.
92 * This allows us to update all necessary icons when something changes.
94 static GHashTable *icons_hash = NULL; /* path -> [Icon] */
96 static Icon *menu_icon = NULL; /* Item clicked if there is no selection */
98 /* Static prototypes */
99 static void rename_activate(GtkWidget *dialog);
100 static void menu_closed(GtkWidget *widget);
101 static void lose_selection(GtkClipboard *primary, gpointer data);
102 static void selection_get(GtkClipboard *primary,
103 GtkSelectionData *selection_data,
104 guint info,
105 gpointer data);
106 static void remove_items(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 ungrab_key(Icon *icon);
115 static void grab_key(Icon *icon);
116 static void parseKeyString(MyKey *key, const char *str);
117 static void icon_wink(Icon *icon);
118 static void initModifiers(void);
119 static void create_menu(void);
121 enum {
122 ACTION_SHIFT,
123 ACTION_PROPERTIES,
124 ACTION_RUN_ACTION,
125 ACTION_SET_ICON,
126 ACTION_EDIT,
127 ACTION_LOCATION,
130 #undef N_
131 #define N_(x) x
132 static GtkItemFactoryEntry menu_def[] = {
133 {N_("ROX-Filer"), NULL, NULL, 0, "<Branch>"},
134 {">" N_("About ROX-Filer..."), NULL, menu_rox_help, HELP_ABOUT, "<StockItem>", GTK_STOCK_DIALOG_INFO},
135 {">" N_("Show Help Files"), NULL, menu_rox_help, HELP_DIR, "<StockItem>", GTK_STOCK_HELP},
136 {">" N_("Manual"), NULL, menu_rox_help, HELP_MANUAL, NULL},
137 {">", NULL, NULL, 0, "<Separator>"},
138 {">" N_("Options..."), NULL, menu_show_options, 0, "<StockItem>", GTK_STOCK_PREFERENCES},
139 {">" N_("Home Directory"), NULL, open_home, 0, "<StockItem>", GTK_STOCK_HOME},
140 {N_("File"), NULL, NULL, 0, "<Branch>"},
141 {">" N_("Shift Open"), NULL, file_op, ACTION_SHIFT, NULL},
142 {">" N_("Properties"), NULL, file_op, ACTION_PROPERTIES, "<StockItem>", GTK_STOCK_PROPERTIES},
143 {">" N_("Set Run Action..."), NULL, file_op, ACTION_RUN_ACTION, "<StockItem>", GTK_STOCK_EXECUTE},
144 {">" N_("Set Icon..."), NULL, file_op, ACTION_SET_ICON, NULL},
145 {N_("Edit Item"), NULL, file_op, ACTION_EDIT, "<StockItem>", GTK_STOCK_PROPERTIES},
146 {N_("Show Location"), NULL, file_op, ACTION_LOCATION, "<StockItem>", GTK_STOCK_JUMP_TO},
147 {N_("Remove Item(s)"), NULL, remove_items, 0, "<StockItem>", GTK_STOCK_REMOVE},
148 {"", NULL, NULL, 0, "<Separator>"},
151 /****************************************************************
152 * EXTERNAL INTERFACE *
153 ****************************************************************/
155 /* Called when the pointer moves over the icon */
156 void icon_may_update(Icon *icon)
158 MaskedPixmap *image;
159 int flags;
161 g_return_if_fail(icon != NULL);
163 image = di_image(icon->item);
164 flags = icon->item->flags;
166 if (image)
167 g_object_ref(image);
168 mount_update(FALSE);
169 diritem_restat(icon->path, icon->item, NULL);
171 if (di_image(icon->item) != image || icon->item->flags != flags)
173 /* Appearance changed; need to redraw */
174 g_signal_emit_by_name(icon, "redraw");
177 if (image)
178 g_object_unref(image);
181 /* If path is on an icon then it may have changed... check! */
182 void icons_may_update(const gchar *path)
184 GList *affected;
186 if (icons_hash)
188 affected = g_hash_table_lookup(icons_hash, path);
190 for (; affected; affected = affected->next)
191 icon_may_update((Icon *) affected->data);
195 typedef struct _CheckData CheckData;
196 struct _CheckData {
197 const gchar *path;
198 gboolean found;
201 static void check_has(gpointer key, GList *icons, CheckData *check)
203 Icon *icon;
205 g_return_if_fail(icons != NULL);
207 icon = icons->data;
209 if (is_sub_dir(icon->path, check->path))
210 check->found = TRUE;
213 /* Returns TRUE if any icon links to this file (or any file inside
214 * this directory). Used to check that it's OK to delete 'path'.
216 gboolean icons_require(const gchar *path)
218 CheckData check;
220 if (!icons_hash)
221 return FALSE;
223 check.path = path;
224 check.found = FALSE;
225 g_hash_table_foreach(icons_hash, (GHFunc) check_has, &check);
227 return check.found;
230 /* Menu was clicked over this icon. Set things up correctly (shade items,
231 * add app menu stuff, etc).
232 * You should show icon_menu after calling this...
233 * panel_name is NULL for the pinboard.
235 void icon_prepare_menu(Icon *icon, GtkWidget *options_item)
237 GtkWidget *image;
239 appmenu_remove();
241 if (current_options_item)
243 gtk_widget_destroy(current_options_item);
244 current_options_item = NULL;
247 menu_icon = icon;
249 if (!icon_menu)
250 create_menu();
252 current_options_item = options_item;
253 image = gtk_image_new_from_stock(GTK_STOCK_PREFERENCES,
254 GTK_ICON_SIZE_MENU);
255 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(options_item), image);
257 gtk_menu_shell_append(GTK_MENU_SHELL(icon_menu), options_item);
258 gtk_widget_show_all(options_item);
260 /* Shade Remove Item(s) unless there is a selection */
261 menu_set_items_shaded(icon_menu,
262 (icon_selection || menu_icon) ? FALSE : TRUE,
263 4, 1);
265 menu_show_shift_action(file_shift_item, icon ? icon->item : NULL,
266 FALSE);
268 /* Shade the File/Edit/Show items unless an item was clicked */
269 if (icon)
271 guchar *tmp;
273 menu_set_items_shaded(icon_menu, FALSE, 1, 3);
274 menu_set_items_shaded(icon_file_menu, FALSE, 0, 5);
275 if (!can_set_run_action(icon->item))
276 menu_set_items_shaded(icon_file_menu, TRUE, 2, 1);
278 tmp = g_strdup_printf(_("%s '%s'"),
279 basetype_name(icon->item),
280 icon->item->leafname);
281 gtk_label_set_text(GTK_LABEL(icon_file_item), tmp);
282 g_free(tmp);
284 /* Check for app-specific menu */
285 appmenu_add(icon->path, icon->item, icon_menu);
287 else
289 menu_set_items_shaded(icon_menu, TRUE, 1, 3);
290 menu_set_items_shaded(icon_file_menu, TRUE, 0, 5);
291 gtk_label_set_text(GTK_LABEL(icon_file_item), _("Nothing"));
295 /* Set whether this icon is selected. Will automatically clear the selection
296 * if it contains icons from a different group.
298 void icon_set_selected(Icon *icon, gboolean selected)
300 if (selected && icon_selection)
302 IconClass *iclass;
303 Icon *other = (Icon *) icon_selection->data;
305 iclass = (IconClass *) G_OBJECT_GET_CLASS(icon);
306 if (!iclass->same_group(icon, other))
308 icon_select_only(icon);
309 return;
313 icon_set_selected_int(icon, selected);
316 /* Clear everything, except 'select', which is selected.
317 * If select is NULL, unselects everything.
319 void icon_select_only(Icon *select)
321 GList *to_clear, *next;
323 if (select)
324 icon_set_selected_int(select, TRUE);
326 to_clear = g_list_copy(icon_selection);
328 if (select)
329 to_clear = g_list_remove(to_clear, select);
331 for (next = to_clear; next; next = next->next)
332 icon_set_selected_int((Icon *) next->data, FALSE);
334 g_list_free(to_clear);
337 /* Destroys this icon's widget, causing it to be removed from the screen */
338 void icon_destroy(Icon *icon)
340 g_return_if_fail(icon != NULL);
342 icon_set_selected_int(icon, FALSE);
344 g_signal_emit_by_name(icon, "destroy");
347 /* Return a text/uri-list of all the icons in the selection */
348 gchar *icon_create_uri_list(void)
350 GString *tmp;
351 guchar *retval;
352 GList *next;
354 tmp = g_string_new(NULL);
356 for (next = icon_selection; next; next = next->next)
358 Icon *icon = (Icon *) next->data;
359 EscapedPath *uri;
361 uri = encode_path_as_uri(icon->path);
362 g_string_append(tmp, (char *) uri);
363 g_free(uri);
364 g_string_append(tmp, "\r\n");
367 retval = tmp->str;
368 g_string_free(tmp, FALSE);
370 return retval;
373 GType icon_get_type(void)
375 static GType type = 0;
377 if (!type)
379 static const GTypeInfo info =
381 sizeof (IconClass),
382 NULL, /* base_init */
383 NULL, /* base_finalise */
384 icon_class_init,
385 NULL, /* class_finalise */
386 NULL, /* class_data */
387 sizeof(Icon),
388 0, /* n_preallocs */
389 icon_init
392 type = g_type_register_static(G_TYPE_OBJECT, "Icon",
393 &info, 0);
396 return type;
399 /* Sets, unsets or changes the pathname and name for an icon.
400 * Updates icons_hash and (re)stats the item.
401 * If name is NULL then gets the leafname from pathname.
402 * If pathname is NULL, frees everything.
404 void icon_set_path(Icon *icon, const char *pathname, const char *name)
406 if (icon->path)
408 icon_unhash_path(icon);
409 icon->src_path = NULL;
410 icon->path = NULL;
412 diritem_free(icon->item);
413 icon->item = NULL;
416 if (pathname)
418 if (g_utf8_validate(pathname, -1, NULL))
419 icon->src_path = g_strdup(pathname);
420 else
421 icon->src_path = to_utf8(pathname);
422 icon->path = expand_path(icon->src_path);
424 icon_hash_path(icon);
426 if (!name)
427 name = g_basename(icon->src_path);
429 icon->item = diritem_new(name);
430 diritem_restat(icon->path, icon->item, NULL);
434 void icon_set_shortcut(Icon *icon, const gchar *shortcut)
436 g_return_if_fail(icon != NULL);
438 if (shortcut && !*shortcut)
439 shortcut = NULL;
440 if (icon->shortcut == shortcut)
441 return;
442 if (icon->shortcut && shortcut && strcmp(icon->shortcut, shortcut) == 0)
443 return;
445 initModifiers();
447 ungrab_key(icon);
449 g_free(icon->shortcut);
450 icon->shortcut = g_strdup(shortcut);
451 parseKeyString(&icon->shortcut_key, shortcut);
453 grab_key(icon);
456 void icon_set_arguments(Icon *icon, const gchar *args)
458 g_return_if_fail(icon != NULL);
459 g_return_if_fail(args == NULL || icon->args != args);
461 if (args && !*args)
462 args = NULL;
463 if (icon->args && args && strcmp(icon->args, args) == 0)
464 return;
466 g_free(icon->args);
467 icon->args = g_strdup(args);
470 void icon_run(Icon *icon)
472 if (icon->args == NULL)
473 run_diritem(icon->path, icon->item, NULL, NULL, FALSE);
474 else
475 run_with_args(icon->path, icon->item, icon->args);
478 /****************************************************************
479 * INTERNAL FUNCTIONS *
480 ****************************************************************/
482 /* The icons_hash table allows us to convert from a path to a list
483 * of icons that use that path.
484 * Add this icon to the list for its path.
486 static void icon_hash_path(Icon *icon)
488 GList *list;
490 g_return_if_fail(icon != NULL);
492 /* g_print("[ hashing '%s' ]\n", icon->path); */
494 list = g_hash_table_lookup(icons_hash, icon->path);
495 list = g_list_prepend(list, icon);
496 g_hash_table_insert(icons_hash, icon->path, list);
499 /* Remove this icon from the icons_hash table */
500 static void icon_unhash_path(Icon *icon)
502 GList *list;
504 g_return_if_fail(icon != NULL);
506 /* g_print("[ unhashing '%s' ]\n", icon->path); */
508 list = g_hash_table_lookup(icons_hash, icon->path);
509 g_return_if_fail(list != NULL);
511 list = g_list_remove(list, icon);
513 /* Remove it first; the hash key may have changed address */
514 g_hash_table_remove(icons_hash, icon->path);
515 if (list)
516 g_hash_table_insert(icons_hash,
517 ((Icon *) list->data)->path, list);
520 static void rename_activate(GtkWidget *dialog)
522 GtkWidget *entry, *src, *shortcut, *arg;
523 Icon *icon;
524 const guchar *new_name, *new_src, *new_shortcut, *new_args;
526 entry = g_object_get_data(G_OBJECT(dialog), "new_name");
527 icon = g_object_get_data(G_OBJECT(dialog), "callback_icon");
528 src = g_object_get_data(G_OBJECT(dialog), "new_path");
529 shortcut = g_object_get_data(G_OBJECT(dialog), "new_shortcut");
530 arg = g_object_get_data(G_OBJECT(dialog), "new_arg");
532 g_return_if_fail(entry != NULL &&
533 src != NULL &&
534 icon != NULL &&
535 shortcut != NULL &&
536 arg != NULL);
538 new_name = gtk_entry_get_text(GTK_ENTRY(entry));
539 new_src = gtk_entry_get_text(GTK_ENTRY(src));
540 new_shortcut = gtk_label_get_text(GTK_LABEL(shortcut));
541 if (strcmp(new_shortcut, CLICK_TO_SET) == 0)
542 new_shortcut = NULL;
543 new_args = gtk_entry_get_text(GTK_ENTRY(arg));
545 if (*new_src == '\0')
546 report_error(
547 _("The location must contain at least one character!"));
548 else
550 icon_set_path(icon, new_src, new_name);
551 icon_set_shortcut(icon, new_shortcut);
552 icon_set_arguments(icon, new_args);
553 g_signal_emit_by_name(icon, "update");
554 gtk_widget_destroy(dialog);
558 static void menu_closed(GtkWidget *widget)
560 appmenu_remove();
561 menu_icon = NULL;
564 /* Called when another application takes the selection away from us */
565 static void lose_selection(GtkClipboard *primary, gpointer data)
567 have_primary = FALSE;
568 icon_select_only(NULL);
571 /* Called when another application wants the contents of our selection */
572 static void selection_get(GtkClipboard *primary,
573 GtkSelectionData *selection_data,
574 guint info,
575 gpointer data)
577 gchar *text;
579 if (info == TARGET_URI_LIST)
580 text = icon_create_uri_list();
581 else
583 GList *next;
584 GString *str;
586 str = g_string_new(NULL);
588 for (next = icon_selection; next; next = next->next)
590 Icon *icon = (Icon *) next->data;
592 g_string_append(str, icon->path);
593 g_string_append_c(str, ' ');
596 text = str->str;
597 g_string_free(str, FALSE);
600 gtk_selection_data_set_text(selection_data, text, strlen(text));
603 static void remove_items(gpointer data, guint action, GtkWidget *widget)
605 IconClass *iclass;
607 if (menu_icon)
608 icon_set_selected(menu_icon, TRUE);
610 if (!icon_selection)
612 delayed_error(
613 _("You must first select some items to remove"));
614 return;
617 iclass = (IconClass *)
618 G_OBJECT_GET_CLASS(G_OBJECT(icon_selection->data));
620 iclass->remove_items();
623 static void file_op(gpointer data, guint action, GtkWidget *widget)
625 if (!menu_icon)
627 delayed_error(_("You must open the menu over an item"));
628 return;
631 switch (action)
633 case ACTION_SHIFT:
634 run_diritem(menu_icon->path, menu_icon->item,
635 NULL, NULL, TRUE);
636 break;
637 case ACTION_EDIT:
638 show_rename_box(menu_icon);
639 break;
640 case ACTION_LOCATION:
641 open_to_show(menu_icon->path);
642 break;
643 case ACTION_PROPERTIES:
644 infobox_new(menu_icon->path);
645 break;
646 case ACTION_RUN_ACTION:
647 if (can_set_run_action(menu_icon->item))
648 type_set_handler_dialog(
649 menu_icon->item->mime_type);
650 else
651 report_error(
652 _("You can only set the run action for a "
653 "regular file"));
654 break;
655 case ACTION_SET_ICON:
656 icon_set_handler_dialog(menu_icon->item,
657 menu_icon->path);
658 break;
662 static void edit_response(GtkWidget *dialog, gint response, gpointer data)
664 if (response == GTK_RESPONSE_OK)
665 rename_activate(dialog);
666 else if (response == GTK_RESPONSE_CANCEL)
667 gtk_widget_destroy(dialog);
670 static GdkFilterReturn filter_get_key(GdkXEvent *xevent,
671 GdkEvent *event,
672 gpointer data)
674 XKeyEvent *kev = (XKeyEvent *) xevent;
675 GtkWidget *popup = (GtkWidget *) data;
676 Display *dpy = GDK_DISPLAY();
678 if (kev->type != KeyRelease && kev->type != ButtonPressMask)
679 return GDK_FILTER_CONTINUE;
681 initModifiers();
683 if (kev->type == KeyRelease)
685 gchar *str;
686 KeySym sym;
687 unsigned int m = kev->state;
689 sym = XKeycodeToKeysym(dpy, kev->keycode, 0);
690 if (!sym)
691 return GDK_FILTER_CONTINUE;
693 str = g_strdup_printf("%s%s%s%s%s%s%s",
694 m & ControlMask ? "Control+" : "",
695 m & ShiftMask ? "Shift+" : "",
696 m & AltMask ? "Alt+" : "",
697 m & MetaMask ? "Meta+" : "",
698 m & SuperMask ? "Super+" : "",
699 m & HyperMask ? "Hyper+" : "",
700 XKeysymToString(sym));
702 g_object_set_data(G_OBJECT(popup), "chosen-key", str);
705 gdk_window_remove_filter(popup->window, filter_get_key, data);
706 gtk_widget_destroy(popup);
708 return GDK_FILTER_REMOVE;
711 static void may_set_shortcut(GtkWidget *popup, GtkWidget *label)
713 gchar *str;
715 str = g_object_get_data(G_OBJECT(popup), "chosen-key");
716 if (str)
718 gtk_label_set_text(GTK_LABEL(label), str);
719 g_free(str);
720 g_object_set_data(G_OBJECT(popup), "chosen-key", NULL);
724 static void get_shortcut(GtkWidget *button, GtkWidget *label)
726 GtkWidget *popup, *frame, *msg;
727 Window xid;
728 Display *dpy = GDK_DISPLAY();
730 popup = gtk_window_new(GTK_WINDOW_POPUP);
732 gtk_window_set_position(GTK_WINDOW(popup), GTK_WIN_POS_CENTER);
734 frame = gtk_frame_new(NULL);
735 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
736 gtk_container_add(GTK_CONTAINER(popup), frame);
738 msg = gtk_label_new(_("Press the desired shortcut (eg, Control+F1)"));
740 gtk_misc_set_padding(GTK_MISC(msg), 20, 20);
741 gtk_container_add(GTK_CONTAINER(frame), msg);
743 gtk_window_set_modal(GTK_WINDOW(popup), TRUE);
745 gtk_widget_add_events(popup,
746 GDK_KEY_RELEASE_MASK | GDK_BUTTON_PRESS_MASK);
748 g_signal_connect(popup, "destroy",
749 G_CALLBACK(may_set_shortcut), label);
751 gtk_widget_show_all(popup);
753 gdk_window_add_filter(popup->window, filter_get_key, popup);
755 xid = gdk_x11_drawable_get_xid(popup->window);
757 if (XGrabKeyboard(dpy, xid, False, GrabModeAsync, GrabModeAsync,
758 gtk_get_current_event_time()) != Success)
760 delayed_error(_("Failed to get keyboard grab!"));
761 gtk_widget_destroy(popup);
764 if (XGrabPointer(dpy, xid, False, ButtonPressMask, GrabModeAsync,
765 GrabModeAsync, xid, None,
766 gtk_get_current_event_time()) != Success)
768 g_warning("Failed to get mouse grab");
772 static void clear_shortcut(GtkButton *clear, GtkLabel *label)
774 gtk_label_set_text(GTK_LABEL(label), CLICK_TO_SET);
777 /* Opens a box allowing the user to change the name of a pinned icon.
778 * If the icon is destroyed then the box will close.
779 * If the user chooses OK then the callback is called once the icon's
780 * name, src_path and path fields have been updated and the item field
781 * restatted.
783 static void show_rename_box(Icon *icon)
785 GtkDialog *dialog;
786 GtkWidget *label, *entry, *button, *button2, *hbox, *spacer;
787 GtkBox *vbox;
789 if (icon->dialog)
791 gtk_window_present(GTK_WINDOW(icon->dialog));
792 return;
795 icon->dialog = gtk_dialog_new();
796 g_signal_connect(icon->dialog, "destroy",
797 G_CALLBACK(gtk_widget_destroyed), &icon->dialog);
799 dialog = GTK_DIALOG(icon->dialog);
801 vbox = GTK_BOX(gtk_vbox_new(FALSE, 1));
802 gtk_box_pack_start(GTK_BOX(dialog->vbox), (GtkWidget *) vbox,
803 TRUE, TRUE, 0);
804 gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
806 gtk_window_set_title(GTK_WINDOW(dialog), _("Edit Item"));
807 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
809 label = gtk_label_new(_("Clicking the icon opens:"));
810 gtk_box_pack_start(vbox, label, TRUE, TRUE, 0);
812 entry = gtk_entry_new();
813 gtk_box_pack_start(vbox, entry, TRUE, FALSE, 2);
814 gtk_entry_set_text(GTK_ENTRY(entry), icon->src_path);
815 g_object_set_data(G_OBJECT(dialog), "new_path", entry);
816 g_signal_connect_swapped(entry, "activate",
817 G_CALLBACK(rename_activate), dialog);
819 label = gtk_label_new(_("Arguments to pass (for executables):"));
820 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->args ? icon->args : "");
825 g_object_set_data(G_OBJECT(dialog), "new_arg", entry);
826 g_signal_connect_swapped(entry, "activate",
827 G_CALLBACK(rename_activate), dialog);
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 text displayed under the icon is:"));
834 gtk_box_pack_start(vbox, label, TRUE, TRUE, 0);
835 entry = gtk_entry_new();
836 gtk_box_pack_start(vbox, entry, TRUE, FALSE, 2);
837 gtk_entry_set_text(GTK_ENTRY(entry), icon->item->leafname);
838 gtk_widget_grab_focus(entry);
839 g_object_set_data(G_OBJECT(dialog), "new_name", entry);
840 gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
842 spacer = gtk_drawing_area_new();
843 gtk_widget_set_size_request(spacer, 4, 4);
844 gtk_box_pack_start(vbox, spacer, FALSE, FALSE, 0);
846 label = gtk_label_new(_("The keyboard shortcut is:"));
847 gtk_box_pack_start(vbox, label, TRUE, TRUE, 0);
849 hbox = gtk_hbox_new(FALSE, 2);
850 gtk_box_pack_start(vbox, hbox, TRUE, FALSE, 0);
851 button = gtk_button_new_with_label(icon->shortcut
852 ? icon->shortcut
853 : CLICK_TO_SET);
854 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
855 g_object_set_data(G_OBJECT(dialog), "new_shortcut",
856 GTK_BIN(button)->child);
857 g_signal_connect(button, "clicked",
858 G_CALLBACK(get_shortcut),
859 GTK_BIN(button)->child);
860 button2 = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
861 gtk_box_pack_start(GTK_BOX(hbox), button2, FALSE, FALSE, 0);
862 g_signal_connect(button2, "clicked",
863 G_CALLBACK(clear_shortcut),
864 GTK_BIN(button)->child);
866 g_object_set_data(G_OBJECT(dialog), "callback_icon", icon);
868 gtk_dialog_add_buttons(dialog,
869 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
870 GTK_STOCK_OK, GTK_RESPONSE_OK,
871 NULL);
872 gtk_dialog_set_default_response(dialog, GTK_RESPONSE_OK);
874 g_signal_connect(dialog, "response", G_CALLBACK(edit_response), NULL);
876 gtk_widget_show_all(GTK_WIDGET(dialog));
879 static gpointer parent_class = NULL;
881 static void icon_finialize(GObject *object)
883 Icon *icon = (Icon *) object;
885 g_return_if_fail(!icon->selected);
887 if (icon->dialog)
888 gtk_widget_destroy(icon->dialog);
890 if (icon == menu_icon)
891 menu_icon = NULL;
893 icon_set_path(icon, NULL, NULL);
894 icon_set_shortcut(icon, NULL);
895 icon_set_arguments(icon, NULL);
897 G_OBJECT_CLASS(parent_class)->finalize(object);
900 static void icon_class_init(gpointer gclass, gpointer data)
902 GObjectClass *object = (GObjectClass *) gclass;
903 IconClass *icon = (IconClass *) gclass;
905 parent_class = g_type_class_peek_parent(gclass);
907 object->finalize = icon_finialize;
908 icon->destroy = NULL;
909 icon->redraw = NULL;
910 icon->update = NULL;
911 icon->same_group = NULL;
912 icon->wink = NULL;
914 g_signal_new("update",
915 G_TYPE_FROM_CLASS(gclass),
916 G_SIGNAL_RUN_LAST,
917 G_STRUCT_OFFSET(IconClass, update),
918 NULL, NULL,
919 g_cclosure_marshal_VOID__VOID,
920 G_TYPE_NONE, 0);
922 g_signal_new("destroy",
923 G_TYPE_FROM_CLASS(gclass),
924 G_SIGNAL_RUN_LAST,
925 G_STRUCT_OFFSET(IconClass, destroy),
926 NULL, NULL,
927 g_cclosure_marshal_VOID__VOID,
928 G_TYPE_NONE, 0);
930 g_signal_new("redraw",
931 G_TYPE_FROM_CLASS(gclass),
932 G_SIGNAL_RUN_LAST,
933 G_STRUCT_OFFSET(IconClass, redraw),
934 NULL, NULL,
935 g_cclosure_marshal_VOID__VOID,
936 G_TYPE_NONE, 0);
938 icons_hash = g_hash_table_new(g_str_hash, g_str_equal);
941 static void icon_init(GTypeInstance *object, gpointer gclass)
943 Icon *icon = (Icon *) object;
945 icon->selected = FALSE;
946 icon->src_path = NULL;
947 icon->path = NULL;
948 icon->item = NULL;
949 icon->dialog = NULL;
950 icon->shortcut = NULL;
951 icon->shortcut_key.keycode = 0;
952 icon->shortcut_key.modifier = 0;
953 icon->args = NULL;
956 /* As icon_set_selected(), but doesn't automatically unselect incompatible
957 * icons.
959 static void icon_set_selected_int(Icon *icon, gboolean selected)
961 static GtkClipboard *primary;
963 g_return_if_fail(icon != NULL);
965 if (icon->selected == selected)
966 return;
968 if (!primary)
969 primary = gtk_clipboard_get(gdk_atom_intern("PRIMARY", FALSE));
971 if (selected)
973 icon_selection = g_list_prepend(icon_selection, icon);
974 if (!have_primary)
976 GtkTargetEntry target_table[] =
978 {"text/uri-list", 0, TARGET_URI_LIST},
979 {"UTF8", 0, TARGET_STRING},
980 {"COMPOUND_TEXT", 0, TARGET_STRING},
981 {"STRING", 0, TARGET_STRING},
984 /* Grab selection */
985 have_primary = gtk_clipboard_set_with_data(primary,
986 target_table,
987 sizeof(target_table) / sizeof(*target_table),
988 selection_get, lose_selection, NULL);
991 else
993 icon_selection = g_list_remove(icon_selection, icon);
994 if (have_primary && !icon_selection)
996 have_primary = FALSE;
997 gtk_clipboard_clear(primary);
1001 icon->selected = selected;
1002 g_signal_emit_by_name(icon, "redraw");
1005 /* From xfwm4 */
1006 static void initModifiers(void)
1008 static gboolean need_init = TRUE;
1009 Display *dpy = GDK_DISPLAY();
1010 XModifierKeymap *xmk = XGetModifierMapping(dpy);
1011 int m, k;
1013 if (!need_init)
1014 return;
1015 need_init = FALSE;
1017 AltMask = MetaMask = NumLockMask = ScrollLockMask = CapsLockMask =
1018 SuperMask = HyperMask = 0;
1020 /* Work out which mask to use for each modifier group */
1021 if (xmk)
1023 KeyCode *c = xmk->modifiermap;
1024 KeyCode numLockKeyCode;
1025 KeyCode scrollLockKeyCode;
1026 KeyCode capsLockKeyCode;
1027 KeyCode altKeyCode;
1028 KeyCode metaKeyCode;
1029 KeyCode superKeyCode;
1030 KeyCode hyperKeyCode;
1032 /* Find the codes to search for... */
1033 numLockKeyCode = XKeysymToKeycode(dpy, XK_Num_Lock);
1034 scrollLockKeyCode = XKeysymToKeycode(dpy, XK_Scroll_Lock);
1035 capsLockKeyCode = XKeysymToKeycode(dpy, XK_Caps_Lock);
1036 altKeyCode = XKeysymToKeycode(dpy, XK_Alt_L);
1037 metaKeyCode = XKeysymToKeycode(dpy, XK_Meta_L);
1038 superKeyCode = XKeysymToKeycode(dpy, XK_Super_L);
1039 hyperKeyCode = XKeysymToKeycode(dpy, XK_Hyper_L);
1041 /* If some are missing, try alternatives... */
1042 if (!altKeyCode)
1043 altKeyCode = XKeysymToKeycode(dpy, XK_Alt_R);
1044 if (!metaKeyCode)
1045 metaKeyCode = XKeysymToKeycode(dpy, XK_Meta_R);
1046 if (!superKeyCode)
1047 superKeyCode = XKeysymToKeycode(dpy, XK_Super_R);
1048 if (!hyperKeyCode)
1049 hyperKeyCode = XKeysymToKeycode(dpy, XK_Hyper_R);
1051 /* Check each of the eight modifier lists.
1052 * The idea (I think) is that we name the modifier group which
1053 * includes the Alt key as the 'Alt group', and so on for
1054 * the other modifiers.
1056 for (m = 0; m < 8; m++)
1058 for (k = 0; k < xmk->max_keypermod; k++, c++)
1060 if (*c == NoSymbol)
1061 continue;
1062 if (*c == numLockKeyCode)
1063 NumLockMask = (1 << m);
1064 if (*c == scrollLockKeyCode)
1065 ScrollLockMask = (1 << m);
1066 if (*c == capsLockKeyCode)
1067 CapsLockMask = (1 << m);
1068 if (*c == altKeyCode)
1069 AltMask = (1 << m);
1070 if (*c == metaKeyCode)
1071 MetaMask = (1 << m);
1072 if (*c == superKeyCode)
1073 SuperMask = (1 << m);
1074 if (*c == hyperKeyCode)
1075 HyperMask = (1 << m);
1078 XFreeModifiermap(xmk);
1081 if(MetaMask == AltMask)
1082 MetaMask = 0;
1084 if (AltMask != 0 && MetaMask == Mod1Mask)
1086 MetaMask = AltMask;
1087 AltMask = Mod1Mask;
1090 if (AltMask == 0 && MetaMask != 0)
1092 if (MetaMask != Mod1Mask)
1093 AltMask = Mod1Mask;
1094 else
1096 AltMask = MetaMask;
1097 MetaMask = 0;
1101 if (AltMask == 0)
1102 AltMask = Mod1Mask;
1106 /* Fill in key from str. Sets keycode to zero if str is NULL.
1107 * Stolen from xfwm4 and modified. Call initModifiers before this.
1109 static void parseKeyString(MyKey *key, const char *str)
1111 char *k;
1112 Display *dpy = GDK_DISPLAY();
1114 key->keycode = 0;
1115 key->modifier = 0;
1117 if (!str)
1118 return;
1120 k = strrchr(str, '+');
1121 key->keycode = XKeysymToKeycode(dpy, XStringToKeysym(k ? k + 1 : str));
1122 if (k)
1124 gchar *tmp;
1126 tmp = g_ascii_strdown(str, -1);
1128 if (strstr(tmp, "shift"))
1129 key->modifier = key->modifier | ShiftMask;
1130 if (strstr(tmp, "control"))
1131 key->modifier = key->modifier | ControlMask;
1132 if (strstr(tmp, "alt") || strstr(tmp, "mod1"))
1133 key->modifier = key->modifier | AltMask;
1134 if (strstr(tmp, "meta") || strstr(tmp, "mod2"))
1135 key->modifier = key->modifier | MetaMask;
1136 if (strstr(tmp, "hyper"))
1137 key->modifier = key->modifier | HyperMask;
1138 if (strstr(tmp, "super"))
1139 key->modifier = key->modifier | SuperMask;
1141 g_free(tmp);
1144 if (!key->keycode)
1145 g_warning("Can't parse key '%s'\n", str);
1148 static GdkFilterReturn filter_keys(GdkXEvent *xevent,
1149 GdkEvent *event,
1150 gpointer data)
1152 GList *next;
1153 XKeyEvent *kev = (XKeyEvent *) xevent;
1154 guint state;
1156 if (kev->type != KeyPress)
1157 return GDK_FILTER_CONTINUE;
1159 state = kev->state & (ShiftMask | ControlMask | AltMask | MetaMask |
1160 HyperMask | SuperMask);
1162 for (next = icon_shortcuts; next; next = next->next)
1164 Icon *icon = (Icon *) next->data;
1166 if (icon->shortcut_key.keycode == kev->keycode &&
1167 icon->shortcut_key.modifier == state)
1169 icon_wink(icon);
1170 icon_run(icon);
1174 return GDK_FILTER_CONTINUE;
1177 #define GRAB(key, mods) XGrabKey(dpy, key->keycode, key->modifier | mods, \
1178 root, False, GrabModeAsync, GrabModeAsync)
1179 #define UNGRAB(key, mods) XUngrabKey(dpy, key->keycode, key->modifier | mods, \
1180 root)
1182 static guint mykey_hash(gconstpointer key)
1184 MyKey *k = (MyKey *) key;
1186 return (k->keycode << 8) + k->modifier;
1189 static gboolean mykey_cmp(gconstpointer a, gconstpointer b)
1191 MyKey *ka = (MyKey *) a;
1192 MyKey *kb = (MyKey *) b;
1194 return ka->keycode == kb->keycode && kb->modifier == kb->modifier;
1197 /* Stolen from xfwm4 and modified.
1198 * FALSE on error. Call initModifiers before this.
1200 static gboolean grabKey(MyKey *key)
1202 Window root;
1203 Display *dpy = GDK_DISPLAY();
1204 static gboolean need_init = TRUE;
1206 if (need_init)
1208 need_init = FALSE;
1209 gdk_window_add_filter(gdk_get_default_root_window(),
1210 filter_keys, NULL);
1213 gdk_error_trap_push();
1215 root = GDK_ROOT_WINDOW();
1217 GRAB(key, 0);
1219 /* Here we grab all combinations of well known modifiers */
1220 GRAB(key, ScrollLockMask);
1221 GRAB(key, NumLockMask);
1222 GRAB(key, CapsLockMask);
1223 GRAB(key, ScrollLockMask | NumLockMask);
1224 GRAB(key, ScrollLockMask | CapsLockMask);
1225 GRAB(key, CapsLockMask | NumLockMask);
1226 GRAB(key, ScrollLockMask | CapsLockMask | NumLockMask);
1228 gdk_flush();
1229 return gdk_error_trap_pop() == Success;
1232 static gboolean ungrabKey(MyKey *key)
1234 Window root = GDK_ROOT_WINDOW();
1235 Display *dpy = GDK_DISPLAY();
1237 gdk_error_trap_push();
1239 UNGRAB(key, 0);
1241 UNGRAB(key, ScrollLockMask);
1242 UNGRAB(key, NumLockMask);
1243 UNGRAB(key, CapsLockMask);
1244 UNGRAB(key, ScrollLockMask | NumLockMask);
1245 UNGRAB(key, ScrollLockMask | CapsLockMask);
1246 UNGRAB(key, CapsLockMask | NumLockMask);
1247 UNGRAB(key, ScrollLockMask | CapsLockMask | NumLockMask);
1249 gdk_flush();
1250 return gdk_error_trap_pop() == Success;
1253 static void ungrab_key(Icon *icon)
1255 int *count;
1257 g_return_if_fail(icon != NULL);
1259 if (!icon->shortcut_key.keycode)
1260 return;
1262 icon_shortcuts = g_list_remove(icon_shortcuts, icon);
1264 count = g_hash_table_lookup(grab_counter, &icon->shortcut_key);
1265 g_return_if_fail(count != NULL);
1267 (*count)--;
1269 if (*count > 0)
1270 return;
1272 g_hash_table_remove(grab_counter, &icon->shortcut_key);
1274 if (!ungrabKey(&icon->shortcut_key))
1275 g_warning("Failed to ungrab shortcut '%s' for '%s' icon.",
1276 icon->shortcut, icon->item->leafname);
1279 static void grab_key(Icon *icon)
1281 MyKey *hash_key;
1282 int *count;
1284 g_return_if_fail(icon != NULL);
1286 g_return_if_fail(g_list_find(icon_shortcuts, icon) == NULL);
1288 if (!icon->shortcut_key.keycode)
1289 return;
1291 icon_shortcuts = g_list_prepend(icon_shortcuts, icon);
1293 if (!grab_counter)
1294 grab_counter = g_hash_table_new_full(mykey_hash, mykey_cmp,
1295 g_free, NULL);
1297 count = g_hash_table_lookup(grab_counter, &icon->shortcut_key);
1298 if (count)
1300 (*count)++;
1301 return; /* Already grabbed */
1304 hash_key = g_new(MyKey, 1);
1305 *hash_key = icon->shortcut_key;
1306 count = g_new(int, 1);
1307 *count = 1;
1308 g_hash_table_insert(grab_counter, hash_key, count);
1310 if (!grabKey(&icon->shortcut_key))
1311 g_warning("Failed to grab shortcut '%s' for '%s' icon.\n"
1312 "Some other application may be already using it!\n",
1313 icon->shortcut, icon->item->leafname);
1317 static void icon_wink(Icon *icon)
1319 IconClass *iclass;
1321 iclass = (IconClass *) G_OBJECT_GET_CLASS(icon);
1322 g_return_if_fail(iclass->wink != NULL);
1324 iclass->wink(icon);
1327 /* Sets icon_menu */
1328 static void create_menu(void)
1330 GList *items;
1331 guchar *tmp;
1332 GtkItemFactory *item_factory;
1334 g_return_if_fail(icon_menu == NULL);
1336 item_factory = menu_create(menu_def,
1337 sizeof(menu_def) / sizeof(*menu_def),
1338 "<icon>", NULL);
1340 tmp = g_strconcat("<icon>/", _("File"), NULL);
1341 icon_menu = gtk_item_factory_get_widget(item_factory, "<icon>");
1342 icon_file_menu = gtk_item_factory_get_widget(item_factory, tmp);
1343 g_free(tmp);
1345 /* File '' label... */
1346 items = gtk_container_get_children(GTK_CONTAINER(icon_menu));
1347 icon_file_item = GTK_BIN(g_list_nth(items, 1)->data)->child;
1348 g_list_free(items);
1350 /* Shift Open... label */
1351 items = gtk_container_get_children(GTK_CONTAINER(icon_file_menu));
1352 file_shift_item = GTK_BIN(items->data)->child;
1353 g_list_free(items);
1355 g_signal_connect(icon_menu, "unmap_event",
1356 G_CALLBACK(menu_closed), NULL);