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)
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
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'.
37 #include <X11/keysym.h>
43 #include "gui_support.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
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
,
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);
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
)
159 g_return_if_fail(icon
!= NULL
);
161 image
= di_image(icon
->item
);
162 flags
= icon
->item
->flags
;
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");
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
)
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
;
199 static void check_has(gpointer key
, GList
*icons
, CheckData
*check
)
203 g_return_if_fail(icons
!= NULL
);
207 if (is_sub_dir(icon
->path
, check
->path
))
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
)
223 g_hash_table_foreach(icons_hash
, (GHFunc
) check_has
, &check
);
228 static gboolean
any_selected_item_is_locked()
232 for (next
= icon_selection
; next
; next
= next
->next
)
234 Icon
*icon
= (Icon
*)next
->data
;
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
, ...)
254 static GtkWidget
*current_options_item
; /* Pin/Pan Options */
255 static GSList
*current_trailing_items
= NULL
;
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
;
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
,
290 gtk_menu_shell_append(GTK_MENU_SHELL(icon_menu
), trailing
);
291 gtk_widget_show(trailing
);
295 /* Shade Remove Item(s) if any item is locked or nothing is selected */
297 shaded
= any_selected_item_is_locked();
299 shaded
= menu_icon
->locked
;
303 menu_set_items_shaded(icon_menu
, shaded
, 4, 1);
305 menu_show_shift_action(file_shift_item
, icon
? icon
->item
: NULL
,
308 /* Shade the File/Edit/Show items unless an item was clicked */
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
);
324 /* Check for app-specific menu */
325 appmenu_add(icon
->path
, icon
->item
, icon_menu
);
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
)
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
);
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
;
364 icon_set_selected_int(select
, TRUE
);
366 to_clear
= g_list_copy(icon_selection
);
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
);
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)
395 tmp
= g_string_new(NULL
);
397 for (next
= icon_selection
; next
; next
= next
->next
)
399 Icon
*icon
= (Icon
*) next
->data
;
402 uri
= encode_path_as_uri(icon
->path
);
403 g_string_append(tmp
, (char *) uri
);
405 g_string_append(tmp
, "\r\n");
409 g_string_free(tmp
, FALSE
);
414 GType
icon_get_type(void)
416 static GType type
= 0;
420 static const GTypeInfo info
=
423 NULL
, /* base_init */
424 NULL
, /* base_finalise */
426 NULL
, /* class_finalise */
427 NULL
, /* class_data */
433 type
= g_type_register_static(G_TYPE_OBJECT
, "Icon",
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
)
449 icon_unhash_path(icon
);
450 icon
->src_path
= NULL
;
453 diritem_free(icon
->item
);
459 if (g_utf8_validate(pathname
, -1, NULL
))
460 icon
->src_path
= g_strdup(pathname
);
462 icon
->src_path
= to_utf8(pathname
);
463 icon
->path
= expand_path(icon
->src_path
);
465 icon_hash_path(icon
);
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
)
481 if (icon
->shortcut
== shortcut
)
483 if (icon
->shortcut
&& shortcut
&& strcmp(icon
->shortcut
, shortcut
) == 0)
490 g_free(icon
->shortcut
);
491 icon
->shortcut
= g_strdup(shortcut
);
492 parseKeyString(&icon
->shortcut_key
, shortcut
);
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
);
504 if (icon
->args
&& args
&& strcmp(icon
->args
, args
) == 0)
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
);
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
)
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
)
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
);
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
;
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
&&
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)
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')
592 _("The location must contain at least one character!"));
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
)
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
,
625 if (info
== TARGET_URI_LIST
)
626 text
= icon_create_uri_list();
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
, ' ');
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
)
655 if (menu_icon
->locked
)
657 delayed_error(_("You must unlock '%s' before removing it"),
658 menu_icon
->item
->leafname
);
661 icon_set_selected(menu_icon
, TRUE
);
667 _("You must first select some items to remove"));
671 if (any_selected_item_is_locked())
673 delayed_error(_("An item must be unlocked before it can be removed."));
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
)
687 delayed_error(_("You must open the menu over an item"));
694 run_diritem(menu_icon
->path
, menu_icon
->item
,
698 show_rename_box(menu_icon
);
700 case ACTION_LOCATION
:
701 open_to_show(menu_icon
->path
);
703 case ACTION_PROPERTIES
:
704 infobox_new(menu_icon
->path
);
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
);
712 _("You can only set the run action for a "
715 case ACTION_SET_ICON
:
716 icon_set_handler_dialog(menu_icon
->item
,
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
,
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
;
743 if (kev
->type
== KeyRelease
)
747 unsigned int m
= kev
->state
;
749 sym
= XKeycodeToKeysym(dpy
, kev
->keycode
, 0);
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
)
775 str
= g_object_get_data(G_OBJECT(popup
), "chosen-key");
778 gtk_label_set_text(GTK_LABEL(label
), 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
;
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
843 static void show_rename_box(Icon
*icon
)
846 GtkWidget
*label
, *entry
, *button
, *button2
, *hbox
, *spacer
, *lock_state
;
851 gtk_window_present(GTK_WINDOW(icon
->dialog
));
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
,
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
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"),
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
,
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
);
957 gtk_widget_destroy(icon
->dialog
);
959 if (icon
== menu_icon
)
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
;
980 icon
->same_group
= NULL
;
983 g_signal_new("update",
984 G_TYPE_FROM_CLASS(gclass
),
986 G_STRUCT_OFFSET(IconClass
, update
),
988 g_cclosure_marshal_VOID__VOID
,
991 g_signal_new("destroy",
992 G_TYPE_FROM_CLASS(gclass
),
994 G_STRUCT_OFFSET(IconClass
, destroy
),
996 g_cclosure_marshal_VOID__VOID
,
999 g_signal_new("redraw",
1000 G_TYPE_FROM_CLASS(gclass
),
1002 G_STRUCT_OFFSET(IconClass
, redraw
),
1004 g_cclosure_marshal_VOID__VOID
,
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
;
1018 icon
->dialog
= NULL
;
1019 icon
->shortcut
= NULL
;
1020 icon
->shortcut_key
.keycode
= 0;
1021 icon
->shortcut_key
.modifier
= 0;
1023 icon
->locked
= FALSE
;
1026 /* As icon_set_selected(), but doesn't automatically unselect incompatible
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
)
1039 primary
= gtk_clipboard_get(gdk_atom_intern("PRIMARY", FALSE
));
1043 icon_selection
= g_list_prepend(icon_selection
, icon
);
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
,
1057 sizeof(target_table
) / sizeof(*target_table
),
1058 selection_get
, lose_selection
, NULL
);
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");
1076 static void initModifiers(void)
1078 static gboolean need_init
= TRUE
;
1079 Display
*dpy
= GDK_DISPLAY();
1080 XModifierKeymap
*xmk
= XGetModifierMapping(dpy
);
1087 AltMask
= MetaMask
= NumLockMask
= ScrollLockMask
= CapsLockMask
=
1088 SuperMask
= HyperMask
= 0;
1090 /* Work out which mask to use for each modifier group */
1093 KeyCode
*c
= xmk
->modifiermap
;
1094 KeyCode numLockKeyCode
;
1095 KeyCode scrollLockKeyCode
;
1096 KeyCode capsLockKeyCode
;
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... */
1113 altKeyCode
= XKeysymToKeycode(dpy
, XK_Alt_R
);
1115 metaKeyCode
= XKeysymToKeycode(dpy
, XK_Meta_R
);
1117 superKeyCode
= XKeysymToKeycode(dpy
, XK_Super_R
);
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
++)
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
)
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
)
1154 if (AltMask
!= 0 && MetaMask
== Mod1Mask
)
1160 if (AltMask
== 0 && MetaMask
!= 0)
1162 if (MetaMask
!= 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
)
1182 Display
*dpy
= GDK_DISPLAY();
1190 k
= strrchr(str
, '+');
1191 key
->keycode
= XKeysymToKeycode(dpy
, XStringToKeysym(k
? k
+ 1 : str
));
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
;
1215 g_warning("Can't parse key '%s'\n", str
);
1218 static GdkFilterReturn
filter_keys(GdkXEvent
*xevent
,
1223 XKeyEvent
*kev
= (XKeyEvent
*) xevent
;
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
)
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, \
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
)
1273 Display
*dpy
= GDK_DISPLAY();
1274 static gboolean need_init
= TRUE
;
1279 gdk_window_add_filter(gdk_get_default_root_window(),
1283 gdk_error_trap_push();
1285 root
= GDK_ROOT_WINDOW();
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
);
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();
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
);
1320 return gdk_error_trap_pop() == Success
;
1323 static void ungrab_key(Icon
*icon
)
1327 g_return_if_fail(icon
!= NULL
);
1329 if (!icon
->shortcut_key
.keycode
)
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
);
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
)
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
)
1361 icon_shortcuts
= g_list_prepend(icon_shortcuts
, icon
);
1364 grab_counter
= g_hash_table_new_full(mykey_hash
, mykey_cmp
,
1367 count
= g_hash_table_lookup(grab_counter
, &icon
->shortcut_key
);
1371 return; /* Already grabbed */
1374 hash_key
= g_new(MyKey
, 1);
1375 *hash_key
= icon
->shortcut_key
;
1376 count
= g_new(int, 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
)
1391 iclass
= (IconClass
*) G_OBJECT_GET_CLASS(icon
);
1392 g_return_if_fail(iclass
->wink
!= NULL
);
1397 /* Sets icon_menu */
1398 static void create_menu(void)
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
),
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
);
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
;
1420 /* Shift Open... label */
1421 items
= gtk_container_get_children(GTK_CONTAINER(icon_file_menu
));
1422 file_shift_item
= GTK_BIN(items
->data
)->child
;
1425 g_signal_connect(icon_menu
, "unmap_event",
1426 G_CALLBACK(menu_closed
), NULL
);