1 /*
2  * ROX-Filer, filer for the ROX desktop project
3  * Copyright (C) 2006, Thomas Leonard and others (see changelog for details).
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the Free
7  * Software Foundation; either version 2 of the License, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  *
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
18  */
19 
20 /* icon.c - abstract base class for pinboard and panel icons.
21  *
22  * An Icon contains the full pathname of its file and the DirItem for that
23  * file. Icons emit the following signals:
24  *
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.
28  *
29  * Note that an icon may be removed without emitting 'destroy'.
30  */
31 
32 #include "config.h"
33 
34 #include <stdarg.h>
35 #include <string.h>
36 #include <gtk/gtk.h>
37 #include <X11/keysym.h>
38 #include <gdk/gdkx.h>
39 
40 #include "global.h"
41 
42 #include "main.h"
43 #include "gui_support.h"
44 #include "support.h"
45 #include "icon.h"
46 #include "diritem.h"
47 #include "menu.h"
48 #include "appmenu.h"
49 #include "dnd.h"
50 #include "run.h"
51 #include "infobox.h"
52 #include "pixmaps.h"
53 #include "mount.h"
54 #include "type.h"
55 #include "usericons.h"
56 #include "pinboard.h"	/* For pinboard_set_backdrop_box */
57 
58 static gboolean have_primary = FALSE;	/* We own the PRIMARY selection? */
59 
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 */
64 
65 /* A list of selected Icons. Every icon in the list is from the same group
66  * (eg, you can't have icons from two different panels selected at the
67  * same time).
68  */
69 GList *icon_selection = NULL;
70 
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.
73  */
74 GList *icon_shortcuts = NULL;
75 
76 #define CLICK_TO_SET _("(click to set)")
77 
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;
85 
86 /* {MyKey -> Number of grabs} */
87 static GHashTable *grab_counter = NULL;
88 
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.
91  */
92 static GHashTable *icons_hash = NULL;	/* path -> [Icon] */
93 
94 static Icon *menu_icon = NULL;	/* Item clicked if there is no selection */
95 
96 /* Static prototypes */
97 static void rename_activate(GtkWidget *dialog);
98 static void menu_closed(GtkWidget *widget);
99 static void lose_selection(GtkClipboard *primary, gpointer data);
100 static void selection_get(GtkClipboard *primary,
101 		       GtkSelectionData *selection_data,
102 		       guint      info,
103 		       gpointer   data);
104 static void remove_items(gpointer data, guint action, GtkWidget *widget);
105 static void file_op(gpointer data, guint action, GtkWidget *widget);
106 static void show_rename_box(Icon *icon);
107 static void icon_set_selected_int(Icon *icon, gboolean selected);
108 static void icon_class_init(gpointer gclass, gpointer data);
109 static void icon_init(GTypeInstance *object, gpointer gclass);
110 static void icon_hash_path(Icon *icon);
111 static void icon_unhash_path(Icon *icon);
112 static void ungrab_key(Icon *icon);
113 static void grab_key(Icon *icon);
114 static void parseKeyString(MyKey *key, const char *str);
115 static void icon_wink(Icon *icon);
116 static void initModifiers(void);
117 static void create_menu(void);
118 
119 enum {
120 	ACTION_SHIFT,
121 	ACTION_PROPERTIES,
122 	ACTION_RUN_ACTION,
123 	ACTION_SET_ICON,
124 	ACTION_EDIT,
125 	ACTION_LOCATION,
126 };
127 
128 #undef N_
129 #define N_(x) x
130 static GtkItemFactoryEntry menu_def[] = {
131 {N_("ROX-Filer"),		NULL, NULL, 0, "<Branch>"},
132 {">" N_("About ROX-Filer..."),	NULL, menu_rox_help, HELP_ABOUT, "<StockItem>", GTK_STOCK_DIALOG_INFO},
133 {">" N_("Show Help Files"),	NULL, menu_rox_help, HELP_DIR, "<StockItem>", GTK_STOCK_HELP},
134 {">" N_("Manual"),		NULL, menu_rox_help, HELP_MANUAL, NULL},
135 {">",				NULL, NULL, 0, "<Separator>"},
136 {">" N_("Options..."),		NULL, menu_show_options, 0, "<StockItem>", GTK_STOCK_PREFERENCES},
137 {">" N_("Home Directory"),	NULL, open_home, 0, "<StockItem>", GTK_STOCK_HOME},
138 {N_("File"),			NULL, NULL, 0, "<Branch>"},
139 {">" N_("Shift Open"),   	NULL, file_op, ACTION_SHIFT, NULL},
140 {">" N_("Properties"),    	NULL, file_op, ACTION_PROPERTIES, "<StockItem>", GTK_STOCK_PROPERTIES},
141 {">" N_("Set Run Action..."),	NULL, file_op, ACTION_RUN_ACTION, "<StockItem>", GTK_STOCK_EXECUTE},
142 {">" N_("Set Icon..."),		NULL, file_op, ACTION_SET_ICON, NULL},
143 {N_("Edit Item"),  		NULL, file_op, ACTION_EDIT, "<StockItem>", GTK_STOCK_PROPERTIES},
144 {N_("Show Location"),  		NULL, file_op, ACTION_LOCATION, "<StockItem>", GTK_STOCK_JUMP_TO},
145 {N_("Remove Item(s)"),		NULL, remove_items, 0, "<StockItem>", GTK_STOCK_REMOVE},
146 {"",				NULL, NULL, 0, "<Separator>"},
147 };
148 
149 /****************************************************************
150  *			EXTERNAL INTERFACE			*
151  ****************************************************************/
152 
153 /* Called when the pointer moves over the icon */
icon_may_update(Icon * icon)154 void icon_may_update(Icon *icon)
155 {
156 	MaskedPixmap	*image;
157 	int		flags;
158 
159 	g_return_if_fail(icon != NULL);
160 
161 	image = di_image(icon->item);
162 	flags = icon->item->flags;
163 
164 	if (image)
165 		g_object_ref(image);
166 	mount_update(FALSE);
167 	diritem_restat(icon->path, icon->item, NULL);
168 
169 	if (di_image(icon->item) != image || icon->item->flags != flags)
170 	{
171 		/* Appearance changed; need to redraw */
172 		g_signal_emit_by_name(icon, "redraw");
173 	}
174 
175 	if (image)
176 		g_object_unref(image);
177 }
178 
179 /* If path is on an icon then it may have changed... check! */
icons_may_update(const gchar * path)180 void icons_may_update(const gchar *path)
181 {
182 	GList	*affected;
183 
184 	if (icons_hash)
185 	{
186 		affected = g_hash_table_lookup(icons_hash, path);
187 
188 		for (; affected; affected = affected->next)
189 			icon_may_update((Icon *) affected->data);
190 	}
191 }
192 
193 typedef struct _CheckData CheckData;
194 struct _CheckData {
195 	const gchar *path;
196 	gboolean    found;
197 };
198 
check_has(gpointer key,GList * icons,CheckData * check)199 static void check_has(gpointer key, GList *icons, CheckData *check)
200 {
201 	Icon	*icon;
202 
203 	g_return_if_fail(icons != NULL);
204 
205 	icon = icons->data;
206 
207 	if (is_sub_dir(icon->path, check->path))
208 		check->found = TRUE;
209 }
210 
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'.
213  */
icons_require(const gchar * path)214 gboolean icons_require(const gchar *path)
215 {
216 	CheckData	check;
217 
218 	if (!icons_hash)
219 		return FALSE;
220 
221 	check.path = path;
222 	check.found = FALSE;
223 	g_hash_table_foreach(icons_hash, (GHFunc) check_has, &check);
224 
225 	return check.found;
226 }
227 
any_selected_item_is_locked()228 static gboolean any_selected_item_is_locked()
229 {
230 	GList *next;
231 
232 	for (next = icon_selection; next; next = next->next)
233 	{
234 		Icon *icon = (Icon *)next->data;
235 
236 		if (icon->locked)
237 			return TRUE;
238 	}
239 
240 	return FALSE;
241 }
242 
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.
247  */
icon_prepare_menu(Icon * icon,GtkWidget * options_item,...)248 void icon_prepare_menu(Icon *icon, GtkWidget *options_item, ...)
249 {
250 	gboolean shaded;
251 	GSList *link;
252 	va_list ap;
253 	GtkWidget *trailing;
254 	static GtkWidget *current_options_item;	/* Pin/Pan Options */
255 	static GSList *current_trailing_items = NULL;
256 
257 	appmenu_remove();
258 
259 	if (current_options_item)
260 	{
261 		gtk_widget_destroy(current_options_item);
262 		current_options_item = NULL;
263 	}
264 	for (link = current_trailing_items; link; link = g_slist_next(link))
265 	{
266 		gtk_widget_destroy(link->data);
267 	}
268 	if (current_trailing_items)
269 	{
270 		g_slist_free(current_trailing_items);
271 		current_trailing_items = NULL;
272 	}
273 
274 	menu_icon = icon;
275 
276 	if (!icon_menu)
277 		create_menu();
278 
279 	current_options_item = options_item;
280 	add_stock_to_menu_item(options_item, GTK_STOCK_PREFERENCES);
281 
282 	gtk_menu_shell_append(GTK_MENU_SHELL(icon_menu), options_item);
283 	gtk_widget_show_all(options_item);
284 
285 	va_start(ap, options_item);
286 	while ((trailing = va_arg(ap, GtkWidget *)) != NULL)
287 	{
288 		current_trailing_items = g_slist_prepend(current_trailing_items,
289 				trailing);
290 		gtk_menu_shell_append(GTK_MENU_SHELL(icon_menu), trailing);
291 		gtk_widget_show(trailing);
292 	}
293 	va_end(ap);
294 
295 	/* Shade Remove Item(s) if any item is locked or nothing is selected */
296 	if (icon_selection)
297 		shaded = any_selected_item_is_locked();
298 	else if (menu_icon)
299 		shaded = menu_icon->locked;
300 	else
301 		shaded = TRUE;
302 
303 	menu_set_items_shaded(icon_menu, shaded, 4, 1);
304 
305 	menu_show_shift_action(file_shift_item, icon ? icon->item : NULL,
306 			FALSE);
307 
308 	/* Shade the File/Edit/Show items unless an item was clicked */
309 	if (icon)
310 	{
311 		guchar *tmp;
312 
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);
317 
318 		tmp = g_strdup_printf(_("%s '%s'"),
319 				basetype_name(icon->item),
320 				icon->item->leafname);
321 		gtk_label_set_text(GTK_LABEL(icon_file_item), tmp);
322 		g_free(tmp);
323 
324 		/* Check for app-specific menu */
325 		appmenu_add(icon->path, icon->item, icon_menu);
326 	}
327 	else
328 	{
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"));
332 	}
333 }
334 
335 /* Set whether this icon is selected. Will automatically clear the selection
336  * if it contains icons from a different group.
337  */
icon_set_selected(Icon * icon,gboolean selected)338 void icon_set_selected(Icon *icon, gboolean selected)
339 {
340 	if (selected && icon_selection)
341 	{
342 		IconClass	*iclass;
343 		Icon		*other = (Icon *) icon_selection->data;
344 
345 		iclass = (IconClass *) G_OBJECT_GET_CLASS(icon);
346 		if (!iclass->same_group(icon, other))
347 		{
348 			icon_select_only(icon);
349 			return;
350 		}
351 	}
352 
353 	icon_set_selected_int(icon, selected);
354 }
355 
356 /* Clear everything, except 'select', which is selected.
357  * If select is NULL, unselects everything.
358  */
icon_select_only(Icon * select)359 void icon_select_only(Icon *select)
360 {
361 	GList	*to_clear, *next;
362 
363 	if (select)
364 		icon_set_selected_int(select, TRUE);
365 
366 	to_clear = g_list_copy(icon_selection);
367 
368 	if (select)
369 		to_clear = g_list_remove(to_clear, select);
370 
371 	for (next = to_clear; next; next = next->next)
372 		icon_set_selected_int((Icon *) next->data, FALSE);
373 
374 	g_list_free(to_clear);
375 }
376 
377 /* Destroys this icon's widget, causing it to be removed from the screen */
icon_destroy(Icon * icon)378 void icon_destroy(Icon *icon)
379 {
380 	g_return_if_fail(icon != NULL);
381 
382 	icon_set_selected_int(icon, FALSE);
383 
384 	if (!icon->locked)
385 		g_signal_emit_by_name(icon, "destroy");
386 }
387 
388 /* Return a text/uri-list of all the icons in the selection */
icon_create_uri_list(void)389 gchar *icon_create_uri_list(void)
390 {
391 	GString	*tmp;
392 	guchar	*retval;
393 	GList	*next;
394 
395 	tmp = g_string_new(NULL);
396 
397 	for (next = icon_selection; next; next = next->next)
398 	{
399 		Icon *icon = (Icon *) next->data;
400 		EscapedPath *uri;
401 
402 		uri = encode_path_as_uri(icon->path);
403 		g_string_append(tmp, (char *) uri);
404 		g_free(uri);
405 		g_string_append(tmp, "\r\n");
406 	}
407 
408 	retval = tmp->str;
409 	g_string_free(tmp, FALSE);
410 
411 	return retval;
412 }
413 
icon_get_type(void)414 GType icon_get_type(void)
415 {
416 	static GType type = 0;
417 
418 	if (!type)
419 	{
420 		static const GTypeInfo info =
421 		{
422 			sizeof (IconClass),
423 			NULL,			/* base_init */
424 			NULL,			/* base_finalise */
425 			icon_class_init,
426 			NULL,			/* class_finalise */
427 			NULL,			/* class_data */
428 			sizeof(Icon),
429 			0,			/* n_preallocs */
430 			icon_init
431 		};
432 
433 		type = g_type_register_static(G_TYPE_OBJECT, "Icon",
434 					      &info, 0);
435 	}
436 
437 	return type;
438 }
439 
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.
444  */
icon_set_path(Icon * icon,const char * pathname,const char * name)445 void icon_set_path(Icon *icon, const char *pathname, const char *name)
446 {
447 	if (icon->path)
448 	{
449 		icon_unhash_path(icon);
450 		icon->src_path = NULL;
451 		icon->path = NULL;
452 
453 		diritem_free(icon->item);
454 		icon->item = NULL;
455 	}
456 
457 	if (pathname)
458 	{
459 		if (g_utf8_validate(pathname, -1, NULL))
460 			icon->src_path = g_strdup(pathname);
461 		else
462 			icon->src_path = to_utf8(pathname);
463 		icon->path = expand_path(icon->src_path);
464 
465 		icon_hash_path(icon);
466 
467 		if (!name)
468 			name = g_basename(icon->src_path);
469 
470 		icon->item = diritem_new(name);
471 		diritem_restat(icon->path, icon->item, NULL);
472 	}
473 }
474 
icon_set_shortcut(Icon * icon,const gchar * shortcut)475 void icon_set_shortcut(Icon *icon, const gchar *shortcut)
476 {
477 	g_return_if_fail(icon != NULL);
478 
479 	if (shortcut && !*shortcut)
480 		shortcut = NULL;
481 	if (icon->shortcut == shortcut)
482 		return;
483 	if (icon->shortcut && shortcut && strcmp(icon->shortcut, shortcut) == 0)
484 		return;
485 
486 	initModifiers();
487 
488 	ungrab_key(icon);
489 
490 	g_free(icon->shortcut);
491 	icon->shortcut = g_strdup(shortcut);
492 	parseKeyString(&icon->shortcut_key, shortcut);
493 
494 	grab_key(icon);
495 }
496 
icon_set_arguments(Icon * icon,const gchar * args)497 void icon_set_arguments(Icon *icon, const gchar *args)
498 {
499 	g_return_if_fail(icon != NULL);
500 	g_return_if_fail(args == NULL || icon->args != args);
501 
502 	if (args && !*args)
503 		args = NULL;
504 	if (icon->args && args && strcmp(icon->args, args) == 0)
505 		return;
506 
507 	g_free(icon->args);
508 	icon->args = g_strdup(args);
509 }
510 
icon_run(Icon * icon)511 void icon_run(Icon *icon)
512 {
513 	if (icon->args == NULL)
514 		run_diritem(icon->path, icon->item, NULL, NULL, FALSE);
515 	else
516 		run_with_args(icon->path, icon->item, icon->args);
517 }
518 
519 /****************************************************************
520  *			INTERNAL FUNCTIONS			*
521  ****************************************************************/
522 
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.
526  */
icon_hash_path(Icon * icon)527 static void icon_hash_path(Icon *icon)
528 {
529 	GList	*list;
530 
531 	g_return_if_fail(icon != NULL);
532 
533 	/* g_print("[ hashing '%s' ]\n", icon->path); */
534 
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);
538 }
539 
540 /* Remove this icon from the icons_hash table */
icon_unhash_path(Icon * icon)541 static void icon_unhash_path(Icon *icon)
542 {
543 	GList	*list;
544 
545 	g_return_if_fail(icon != NULL);
546 
547 	/* g_print("[ unhashing '%s' ]\n", icon->path); */
548 
549 	list = g_hash_table_lookup(icons_hash, icon->path);
550 	g_return_if_fail(list != NULL);
551 
552 	list = g_list_remove(list, icon);
553 
554 	/* Remove it first; the hash key may have changed address */
555 	g_hash_table_remove(icons_hash, icon->path);
556 	if (list)
557 		g_hash_table_insert(icons_hash,
558 				((Icon *) list->data)->path, list);
559 }
560 
rename_activate(GtkWidget * dialog)561 static void rename_activate(GtkWidget *dialog)
562 {
563 	GtkWidget *entry, *src, *shortcut, *arg, *lock_state;
564 	Icon *icon;
565 	const guchar *new_name, *new_src, *new_shortcut, *new_args;
566 	gboolean new_lock_state;
567 
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");
574 
575 	g_return_if_fail(entry != NULL &&
576 			 src != NULL &&
577 			 icon != NULL &&
578 			 shortcut != NULL &&
579 			 arg != NULL &&
580 			 lock_state != NULL);
581 
582 	new_name = gtk_entry_get_text(GTK_ENTRY(entry));
583 	new_src = gtk_entry_get_text(GTK_ENTRY(src));
584 	new_shortcut = gtk_label_get_text(GTK_LABEL(shortcut));
585 	if (strcmp(new_shortcut, CLICK_TO_SET) == 0)
586 		new_shortcut = NULL;
587 	new_args = gtk_entry_get_text(GTK_ENTRY(arg));
588 	new_lock_state = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lock_state));
589 
590 	if (*new_src == '\0')
591 		report_error(
592 			_("The location must contain at least one character!"));
593 	else
594 	{
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);
601 	}
602 }
603 
menu_closed(GtkWidget * widget)604 static void menu_closed(GtkWidget *widget)
605 {
606 	appmenu_remove();
607 	menu_icon = NULL;
608 }
609 
610 /* Called when another application takes the selection away from us */
lose_selection(GtkClipboard * primary,gpointer data)611 static void lose_selection(GtkClipboard *primary, gpointer data)
612 {
613 	have_primary = FALSE;
614 	icon_select_only(NULL);
615 }
616 
617 /* Called when another application wants the contents of our selection */
selection_get(GtkClipboard * primary,GtkSelectionData * selection_data,guint info,gpointer data)618 static void selection_get(GtkClipboard	*primary,
619 		          GtkSelectionData *selection_data,
620 		          guint		info,
621 		          gpointer	data)
622 {
623 	gchar *text;
624 
625 	if (info == TARGET_URI_LIST)
626 		text = icon_create_uri_list();
627 	else
628 	{
629 		GList	*next;
630 		GString	*str;
631 
632 		str = g_string_new(NULL);
633 
634 		for (next = icon_selection; next; next = next->next)
635 		{
636 			Icon	*icon = (Icon *) next->data;
637 
638 			g_string_append(str, icon->path);
639 			g_string_append_c(str, ' ');
640 		}
641 
642 		text = str->str;
643 		g_string_free(str, FALSE);
644 	}
645 
646 	gtk_selection_data_set_text(selection_data, text, strlen(text));
647 }
648 
remove_items(gpointer data,guint action,GtkWidget * widget)649 static void remove_items(gpointer data, guint action, GtkWidget *widget)
650 {
651 	IconClass	*iclass;
652 
653 	if (menu_icon)
654 	{
655 		if (menu_icon->locked)
656 		{
657 			delayed_error(_("You must unlock '%s' before removing it"),
658 					menu_icon->item->leafname);
659 			return;
660 		}
661 		icon_set_selected(menu_icon, TRUE);
662 	}
663 
664 	if (!icon_selection)
665 	{
666 		delayed_error(
667 			_("You must first select some items to remove"));
668 		return;
669 	}
670 
671 	if (any_selected_item_is_locked())
672 	{
673 		delayed_error(_("An item must be unlocked before it can be removed."));
674 		return;
675 	}
676 
677 	iclass = (IconClass *)
678 		  G_OBJECT_GET_CLASS(G_OBJECT(icon_selection->data));
679 
680 	iclass->remove_items();
681 }
682 
file_op(gpointer data,guint action,GtkWidget * widget)683 static void file_op(gpointer data, guint action, GtkWidget *widget)
684 {
685 	if (!menu_icon)
686 	{
687 		delayed_error(_("You must open the menu over an item"));
688 		return;
689 	}
690 
691 	switch (action)
692 	{
693 		case ACTION_SHIFT:
694 			run_diritem(menu_icon->path, menu_icon->item,
695 					NULL, NULL, TRUE);
696 			break;
697 		case ACTION_EDIT:
698 			show_rename_box(menu_icon);
699 			break;
700 		case ACTION_LOCATION:
701 			open_to_show(menu_icon->path);
702 			break;
703 		case ACTION_PROPERTIES:
704 			infobox_new(menu_icon->path);
705 			break;
706 		case ACTION_RUN_ACTION:
707 			if (can_set_run_action(menu_icon->item))
708 				type_set_handler_dialog(
709 						menu_icon->item->mime_type);
710 			else
711 				report_error(
712 				_("You can only set the run action for a "
713 				"regular file"));
714 			break;
715 		case ACTION_SET_ICON:
716 			icon_set_handler_dialog(menu_icon->item,
717 						menu_icon->path);
718 			break;
719 	}
720 }
721 
edit_response(GtkWidget * dialog,gint response,gpointer data)722 static void edit_response(GtkWidget *dialog, gint response, gpointer data)
723 {
724 	if (response == GTK_RESPONSE_OK)
725 		rename_activate(dialog);
726 	else if (response == GTK_RESPONSE_CANCEL)
727 		gtk_widget_destroy(dialog);
728 }
729 
filter_get_key(GdkXEvent * xevent,GdkEvent * event,gpointer data)730 static GdkFilterReturn filter_get_key(GdkXEvent *xevent,
731 				      GdkEvent *event,
732 				      gpointer data)
733 {
734 	XKeyEvent *kev = (XKeyEvent *) xevent;
735 	GtkWidget *popup = (GtkWidget *) data;
736 	Display *dpy = GDK_DISPLAY();
737 
738 	if (kev->type != KeyRelease && kev->type != ButtonPressMask)
739 		return GDK_FILTER_CONTINUE;
740 
741 	initModifiers();
742 
743 	if (kev->type == KeyRelease)
744 	{
745 		gchar *str;
746 		KeySym sym;
747 		unsigned int m = kev->state;
748 
749 		sym = XKeycodeToKeysym(dpy, kev->keycode, 0);
750 		if (!sym)
751 			return GDK_FILTER_CONTINUE;
752 
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));
761 
762 		g_object_set_data(G_OBJECT(popup), "chosen-key", str);
763 	}
764 
765 	gdk_window_remove_filter(popup->window, filter_get_key, data);
766 	gtk_widget_destroy(popup);
767 
768 	return GDK_FILTER_REMOVE;
769 }
770 
may_set_shortcut(GtkWidget * popup,GtkWidget * label)771 static void may_set_shortcut(GtkWidget *popup, GtkWidget *label)
772 {
773 	gchar *str;
774 
775 	str = g_object_get_data(G_OBJECT(popup), "chosen-key");
776 	if (str)
777 	{
778 		gtk_label_set_text(GTK_LABEL(label), str);
779 		g_free(str);
780 		g_object_set_data(G_OBJECT(popup), "chosen-key", NULL);
781 	}
782 }
783 
get_shortcut(GtkWidget * button,GtkWidget * label)784 static void get_shortcut(GtkWidget *button, GtkWidget *label)
785 {
786 	GtkWidget *popup, *frame, *msg;
787 	Window    xid;
788 	Display	  *dpy = GDK_DISPLAY();
789 
790 	popup = gtk_window_new(GTK_WINDOW_POPUP);
791 
792 	gtk_window_set_position(GTK_WINDOW(popup), GTK_WIN_POS_CENTER);
793 
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);
797 
798 	msg = gtk_label_new(_("Press the desired shortcut (eg, Control+F1)"));
799 
800 	gtk_misc_set_padding(GTK_MISC(msg), 20, 20);
801 	gtk_container_add(GTK_CONTAINER(frame), msg);
802 
803 	gtk_window_set_modal(GTK_WINDOW(popup), TRUE);
804 
805 	gtk_widget_add_events(popup,
806 			GDK_KEY_RELEASE_MASK | GDK_BUTTON_PRESS_MASK);
807 
808 	g_signal_connect(popup, "destroy",
809 			G_CALLBACK(may_set_shortcut), label);
810 
811 	gtk_widget_show_all(popup);
812 
813 	gdk_window_add_filter(popup->window, filter_get_key, popup);
814 
815 	xid = gdk_x11_drawable_get_xid(popup->window);
816 
817 	if (XGrabKeyboard(dpy, xid, False, GrabModeAsync, GrabModeAsync,
818 			gtk_get_current_event_time()) != Success)
819 	{
820 		delayed_error(_("Failed to get keyboard grab!"));
821 		gtk_widget_destroy(popup);
822 	}
823 
824 	if (XGrabPointer(dpy, xid, False, ButtonPressMask, GrabModeAsync,
825 				GrabModeAsync, xid, None,
826 				gtk_get_current_event_time()) != Success)
827 	{
828 		g_warning("Failed to get mouse grab");
829 	}
830 }
831 
clear_shortcut(GtkButton * clear,GtkLabel * label)832 static void clear_shortcut(GtkButton *clear, GtkLabel *label)
833 {
834 	gtk_label_set_text(GTK_LABEL(label), CLICK_TO_SET);
835 }
836 
837 /* Opens a box allowing the user to change the name of a pinned icon.
838  * If the icon is destroyed then the box will close.
839  * If the user chooses OK then the callback is called once the icon's
840  * name, src_path and path fields have been updated and the item field
841  * restatted.
842  */
show_rename_box(Icon * icon)843 static void show_rename_box(Icon *icon)
844 {
845 	GtkDialog	*dialog;
846 	GtkWidget	*label, *entry, *button, *button2, *hbox, *spacer, *lock_state;
847 	GtkBox		*vbox;
848 
849 	if (icon->dialog)
850 	{
851 		gtk_window_present(GTK_WINDOW(icon->dialog));
852 		return;
853 	}
854 
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);
859 
860 	dialog = GTK_DIALOG(icon->dialog);
861 
862 	vbox = GTK_BOX(gtk_vbox_new(FALSE, 1));
863 	gtk_box_pack_start(GTK_BOX(dialog->vbox), (GtkWidget *) vbox,
864 			   TRUE, TRUE, 0);
865 	gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
866 
867 	gtk_window_set_title(GTK_WINDOW(dialog), _("Edit Item"));
868 	gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
869 
870 	label = gtk_label_new(_("Clicking the icon opens:"));
871 	gtk_box_pack_start(vbox, label, TRUE, TRUE, 0);
872 
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);
879 
880 	label = gtk_label_new(_("Arguments to pass (for executables):"));
881 	gtk_box_pack_start(vbox, label, TRUE, TRUE, 0);
882 
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);
889 
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);
893 
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);
902 
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);
906 
907 	label = gtk_label_new(_("The keyboard shortcut is:"));
908 	gtk_box_pack_start(vbox, label, TRUE, TRUE, 0);
909 
910 	hbox = gtk_hbox_new(FALSE, 2);
911 	gtk_box_pack_start(vbox, hbox, TRUE, FALSE, 0);
912 	button = gtk_button_new_with_label(icon->shortcut
913 						? icon->shortcut
914 						: CLICK_TO_SET);
915 	gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
916 	g_object_set_data(G_OBJECT(dialog), "new_shortcut",
917 				GTK_BIN(button)->child);
918 	g_signal_connect(button, "clicked",
919 			G_CALLBACK(get_shortcut),
920 			GTK_BIN(button)->child);
921 	button2 = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
922 	gtk_box_pack_start(GTK_BOX(hbox), button2, FALSE, FALSE, 0);
923 	g_signal_connect(button2, "clicked",
924 			G_CALLBACK(clear_shortcut),
925 			GTK_BIN(button)->child);
926 
927 	lock_state = gtk_check_button_new_with_label(_("Locked"));
928 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(lock_state), icon->locked);
929 	gtk_box_pack_start(vbox, lock_state, TRUE, TRUE, 0);
930 	g_object_set_data(G_OBJECT(dialog), "new_lock_state", lock_state);
931 	gtk_tooltips_set_tip(tooltips, lock_state,
932 			_("Locking an item prevents it from being accidentally removed"),
933 			NULL);
934 
935 	g_object_set_data(G_OBJECT(dialog), "callback_icon", icon);
936 
937 	gtk_dialog_add_buttons(dialog,
938 			GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
939 			GTK_STOCK_OK, GTK_RESPONSE_OK,
940 			NULL);
941 	gtk_dialog_set_default_response(dialog, GTK_RESPONSE_OK);
942 
943 	g_signal_connect(dialog, "response", G_CALLBACK(edit_response), NULL);
944 
945 	gtk_widget_show_all(GTK_WIDGET(dialog));
946 }
947 
948 static gpointer parent_class = NULL;
949 
icon_finialize(GObject * object)950 static void icon_finialize(GObject *object)
951 {
952 	Icon *icon = (Icon *) object;
953 
954 	g_return_if_fail(!icon->selected);
955 
956 	if (icon->dialog)
957 		gtk_widget_destroy(icon->dialog);
958 
959 	if (icon == menu_icon)
960 		menu_icon = NULL;
961 
962 	icon_set_path(icon, NULL, NULL);
963 	icon_set_shortcut(icon, NULL);
964 	icon_set_arguments(icon, NULL);
965 
966 	G_OBJECT_CLASS(parent_class)->finalize(object);
967 }
968 
icon_class_init(gpointer gclass,gpointer data)969 static void icon_class_init(gpointer gclass, gpointer data)
970 {
971 	GObjectClass *object = (GObjectClass *) gclass;
972 	IconClass *icon = (IconClass *) gclass;
973 
974 	parent_class = g_type_class_peek_parent(gclass);
975 
976 	object->finalize = icon_finialize;
977 	icon->destroy = NULL;
978 	icon->redraw = NULL;
979 	icon->update = NULL;
980 	icon->same_group = NULL;
981 	icon->wink = NULL;
982 
983 	g_signal_new("update",
984 			G_TYPE_FROM_CLASS(gclass),
985 			G_SIGNAL_RUN_LAST,
986 			G_STRUCT_OFFSET(IconClass, update),
987 			NULL, NULL,
988 			g_cclosure_marshal_VOID__VOID,
989 			G_TYPE_NONE, 0);
990 
991 	g_signal_new("destroy",
992 			G_TYPE_FROM_CLASS(gclass),
993 			G_SIGNAL_RUN_LAST,
994 			G_STRUCT_OFFSET(IconClass, destroy),
995 			NULL, NULL,
996 			g_cclosure_marshal_VOID__VOID,
997 			G_TYPE_NONE, 0);
998 
999 	g_signal_new("redraw",
1000 			G_TYPE_FROM_CLASS(gclass),
1001 			G_SIGNAL_RUN_LAST,
1002 			G_STRUCT_OFFSET(IconClass, redraw),
1003 			NULL, NULL,
1004 			g_cclosure_marshal_VOID__VOID,
1005 			G_TYPE_NONE, 0);
1006 
1007 	icons_hash = g_hash_table_new(g_str_hash, g_str_equal);
1008 }
1009 
icon_init(GTypeInstance * object,gpointer gclass)1010 static void icon_init(GTypeInstance *object, gpointer gclass)
1011 {
1012 	Icon *icon = (Icon *) object;
1013 
1014 	icon->selected = FALSE;
1015 	icon->src_path = NULL;
1016 	icon->path = NULL;
1017 	icon->item = NULL;
1018 	icon->dialog = NULL;
1019 	icon->shortcut = NULL;
1020 	icon->shortcut_key.keycode = 0;
1021 	icon->shortcut_key.modifier = 0;
1022 	icon->args = NULL;
1023 	icon->locked = FALSE;
1024 }
1025 
1026 /* As icon_set_selected(), but doesn't automatically unselect incompatible
1027  * icons.
1028  */
icon_set_selected_int(Icon * icon,gboolean selected)1029 static void icon_set_selected_int(Icon *icon, gboolean selected)
1030 {
1031 	static GtkClipboard *primary;
1032 
1033 	g_return_if_fail(icon != NULL);
1034 
1035 	if (icon->selected == selected)
1036 		return;
1037 
1038 	if (!primary)
1039 		primary = gtk_clipboard_get(gdk_atom_intern("PRIMARY", FALSE));
1040 
1041 	if (selected)
1042 	{
1043 		icon_selection = g_list_prepend(icon_selection, icon);
1044 		if (!have_primary)
1045 		{
1046 			GtkTargetEntry target_table[] =
1047 			{
1048 				{"text/uri-list", 0, TARGET_URI_LIST},
1049 				{"UTF8", 0, TARGET_STRING},
1050 				{"COMPOUND_TEXT", 0, TARGET_STRING},
1051 				{"STRING", 0, TARGET_STRING},
1052 			};
1053 
1054 			/* Grab selection */
1055 			have_primary = gtk_clipboard_set_with_data(primary,
1056 				target_table,
1057 				sizeof(target_table) / sizeof(*target_table),
1058 				selection_get, lose_selection, NULL);
1059 		}
1060 	}
1061 	else
1062 	{
1063 		icon_selection = g_list_remove(icon_selection, icon);
1064 		if (have_primary && !icon_selection)
1065 		{
1066 			have_primary = FALSE;
1067 			gtk_clipboard_clear(primary);
1068 		}
1069 	}
1070 
1071 	icon->selected = selected;
1072 	g_signal_emit_by_name(icon, "redraw");
1073 }
1074 
1075 /* From xfwm4 */
initModifiers(void)1076 static void initModifiers(void)
1077 {
1078 	static gboolean need_init = TRUE;
1079 	Display *dpy = GDK_DISPLAY();
1080 	XModifierKeymap *xmk = XGetModifierMapping(dpy);
1081 	int m, k;
1082 
1083 	if (!need_init)
1084 		return;
1085 	need_init = FALSE;
1086 
1087 	AltMask = MetaMask = NumLockMask = ScrollLockMask = CapsLockMask =
1088 		SuperMask = HyperMask = 0;
1089 
1090 	/* Work out which mask to use for each modifier group */
1091 	if (xmk)
1092 	{
1093 		KeyCode *c = xmk->modifiermap;
1094 		KeyCode numLockKeyCode;
1095 		KeyCode scrollLockKeyCode;
1096 		KeyCode capsLockKeyCode;
1097 		KeyCode altKeyCode;
1098 		KeyCode metaKeyCode;
1099 		KeyCode superKeyCode;
1100 		KeyCode hyperKeyCode;
1101 
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);
1110 
1111 		/* If some are missing, try alternatives... */
1112 		if (!altKeyCode)
1113 			altKeyCode = XKeysymToKeycode(dpy, XK_Alt_R);
1114 		if (!metaKeyCode)
1115 			metaKeyCode = XKeysymToKeycode(dpy, XK_Meta_R);
1116 		if (!superKeyCode)
1117 			superKeyCode = XKeysymToKeycode(dpy, XK_Super_R);
1118 		if (!hyperKeyCode)
1119 			hyperKeyCode = XKeysymToKeycode(dpy, XK_Hyper_R);
1120 
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.
1125 		 */
1126 		for (m = 0; m < 8; m++)
1127 		{
1128 			for (k = 0; k < xmk->max_keypermod; k++, c++)
1129 			{
1130 				if (*c == NoSymbol)
1131 					continue;
1132 				if (*c == numLockKeyCode)
1133 					NumLockMask = (1 << m);
1134 				if (*c == scrollLockKeyCode)
1135 					ScrollLockMask = (1 << m);
1136 				if (*c == capsLockKeyCode)
1137 					CapsLockMask = (1 << m);
1138 				if (*c == altKeyCode)
1139 					AltMask = (1 << m);
1140 				if (*c == metaKeyCode)
1141 					MetaMask = (1 << m);
1142 				if (*c == superKeyCode)
1143 					SuperMask = (1 << m);
1144 				if (*c == hyperKeyCode)
1145 					HyperMask = (1 << m);
1146 			}
1147 		}
1148 		XFreeModifiermap(xmk);
1149 	}
1150 
1151 	if(MetaMask == AltMask)
1152 		MetaMask = 0;
1153 
1154 	if (AltMask != 0 && MetaMask == Mod1Mask)
1155 	{
1156 		MetaMask = AltMask;
1157 		AltMask = Mod1Mask;
1158 	}
1159 
1160 	if (AltMask == 0 && MetaMask != 0)
1161 	{
1162 		if (MetaMask != Mod1Mask)
1163 			AltMask = Mod1Mask;
1164 		else
1165 		{
1166 			AltMask = MetaMask;
1167 			MetaMask = 0;
1168 		}
1169 	}
1170 
1171 	if (AltMask == 0)
1172 		AltMask = Mod1Mask;
1173 }
1174 
1175 
1176 /* Fill in key from str. Sets keycode to zero if str is NULL.
1177  * Stolen from xfwm4 and modified. Call initModifiers before this.
1178  */
parseKeyString(MyKey * key,const char * str)1179 static void parseKeyString(MyKey *key, const char *str)
1180 {
1181 	char *k;
1182 	Display *dpy = GDK_DISPLAY();
1183 
1184 	key->keycode = 0;
1185 	key->modifier = 0;
1186 
1187 	if (!str)
1188 		return;
1189 
1190 	k = strrchr(str, '+');
1191 	key->keycode = XKeysymToKeycode(dpy, XStringToKeysym(k ? k + 1 : str));
1192 	if (k)
1193 	{
1194 		gchar *tmp;
1195 
1196 		tmp = g_ascii_strdown(str, -1);
1197 
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;
1210 
1211 		g_free(tmp);
1212 	}
1213 
1214 	if (!key->keycode)
1215 		g_warning("Can't parse key '%s'\n", str);
1216 }
1217 
filter_keys(GdkXEvent * xevent,GdkEvent * event,gpointer data)1218 static GdkFilterReturn filter_keys(GdkXEvent *xevent,
1219 				   GdkEvent *event,
1220 				   gpointer  data)
1221 {
1222 	GList *next;
1223 	XKeyEvent *kev = (XKeyEvent *) xevent;
1224 	guint state;
1225 
1226 	if (kev->type != KeyPress)
1227 		return GDK_FILTER_CONTINUE;
1228 
1229 	state = kev->state & (ShiftMask | ControlMask | AltMask | MetaMask |
1230 			      HyperMask | SuperMask);
1231 
1232 	for (next = icon_shortcuts; next; next = next->next)
1233 	{
1234 		Icon  *icon = (Icon *) next->data;
1235 
1236 		if (icon->shortcut_key.keycode == kev->keycode &&
1237 		    icon->shortcut_key.modifier == state)
1238 		{
1239 			icon_wink(icon);
1240 			icon_run(icon);
1241 		}
1242 	}
1243 
1244 	return GDK_FILTER_CONTINUE;
1245 }
1246 
1247 #define GRAB(key, mods) XGrabKey(dpy, key->keycode, key->modifier | mods, \
1248 				 root, False, GrabModeAsync, GrabModeAsync)
1249 #define UNGRAB(key, mods) XUngrabKey(dpy, key->keycode, key->modifier | mods, \
1250 				 root)
1251 
mykey_hash(gconstpointer key)1252 static guint mykey_hash(gconstpointer key)
1253 {
1254 	MyKey *k = (MyKey *) key;
1255 
1256 	return (k->keycode << 8) + k->modifier;
1257 }
1258 
mykey_cmp(gconstpointer a,gconstpointer b)1259 static gboolean mykey_cmp(gconstpointer a, gconstpointer b)
1260 {
1261 	MyKey *ka = (MyKey *) a;
1262 	MyKey *kb = (MyKey *) b;
1263 
1264 	return ka->keycode == kb->keycode && kb->modifier == kb->modifier;
1265 }
1266 
1267 /* Stolen from xfwm4 and modified.
1268  * FALSE on error. Call initModifiers before this.
1269  */
grabKey(MyKey * key)1270 static gboolean grabKey(MyKey *key)
1271 {
1272 	Window root;
1273 	Display *dpy = GDK_DISPLAY();
1274 	static gboolean need_init = TRUE;
1275 
1276 	if (need_init)
1277 	{
1278 		need_init = FALSE;
1279 		gdk_window_add_filter(gdk_get_default_root_window(),
1280 				filter_keys, NULL);
1281 	}
1282 
1283 	gdk_error_trap_push();
1284 
1285 	root = GDK_ROOT_WINDOW();
1286 
1287 	GRAB(key, 0);
1288 
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);
1297 
1298 	gdk_flush();
1299 	return gdk_error_trap_pop() == Success;
1300 }
1301 
ungrabKey(MyKey * key)1302 static gboolean ungrabKey(MyKey *key)
1303 {
1304 	Window root = GDK_ROOT_WINDOW();
1305 	Display *dpy = GDK_DISPLAY();
1306 
1307 	gdk_error_trap_push();
1308 
1309 	UNGRAB(key, 0);
1310 
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);
1318 
1319 	gdk_flush();
1320 	return gdk_error_trap_pop() == Success;
1321 }
1322 
ungrab_key(Icon * icon)1323 static void ungrab_key(Icon *icon)
1324 {
1325 	int *count;
1326 
1327 	g_return_if_fail(icon != NULL);
1328 
1329 	if (!icon->shortcut_key.keycode)
1330 		return;
1331 
1332 	icon_shortcuts = g_list_remove(icon_shortcuts, icon);
1333 
1334 	count = g_hash_table_lookup(grab_counter, &icon->shortcut_key);
1335 	g_return_if_fail(count != NULL);
1336 
1337 	(*count)--;
1338 
1339 	if (*count > 0)
1340 		return;
1341 
1342 	g_hash_table_remove(grab_counter, &icon->shortcut_key);
1343 
1344 	if (!ungrabKey(&icon->shortcut_key))
1345 		g_warning("Failed to ungrab shortcut '%s' for '%s' icon.",
1346 			   icon->shortcut, icon->item->leafname);
1347 }
1348 
grab_key(Icon * icon)1349 static void grab_key(Icon *icon)
1350 {
1351 	MyKey *hash_key;
1352 	int   *count;
1353 
1354 	g_return_if_fail(icon != NULL);
1355 
1356 	g_return_if_fail(g_list_find(icon_shortcuts, icon) == NULL);
1357 
1358 	if (!icon->shortcut_key.keycode)
1359 		return;
1360 
1361 	icon_shortcuts = g_list_prepend(icon_shortcuts, icon);
1362 
1363 	if (!grab_counter)
1364 		grab_counter = g_hash_table_new_full(mykey_hash, mykey_cmp,
1365 						     g_free, NULL);
1366 
1367 	count = g_hash_table_lookup(grab_counter, &icon->shortcut_key);
1368 	if (count)
1369 	{
1370 		(*count)++;
1371 		return;	/* Already grabbed */
1372 	}
1373 
1374 	hash_key = g_new(MyKey, 1);
1375 	*hash_key = icon->shortcut_key;
1376 	count = g_new(int, 1);
1377 	*count = 1;
1378 	g_hash_table_insert(grab_counter, hash_key, count);
1379 
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);
1384 
1385 }
1386 
icon_wink(Icon * icon)1387 static void icon_wink(Icon *icon)
1388 {
1389 	IconClass	*iclass;
1390 
1391 	iclass = (IconClass *) G_OBJECT_GET_CLASS(icon);
1392 	g_return_if_fail(iclass->wink != NULL);
1393 
1394 	iclass->wink(icon);
1395 }
1396 
1397 /* Sets icon_menu */
create_menu(void)1398 static void create_menu(void)
1399 {
1400 	GList		*items;
1401 	guchar		*tmp;
1402 	GtkItemFactory	*item_factory;
1403 
1404 	g_return_if_fail(icon_menu == NULL);
1405 
1406 	item_factory = menu_create(menu_def,
1407 				sizeof(menu_def) / sizeof(*menu_def),
1408 				 "<icon>", NULL);
1409 
1410 	tmp = g_strconcat("<icon>/", _("File"), NULL);
1411 	icon_menu = gtk_item_factory_get_widget(item_factory, "<icon>");
1412 	icon_file_menu = gtk_item_factory_get_widget(item_factory, tmp);
1413 	g_free(tmp);
1414 
1415 	/* File '' label... */
1416 	items = gtk_container_get_children(GTK_CONTAINER(icon_menu));
1417 	icon_file_item = GTK_BIN(g_list_nth(items, 1)->data)->child;
1418 	g_list_free(items);
1419 
1420 	/* Shift Open... label */
1421 	items = gtk_container_get_children(GTK_CONTAINER(icon_file_menu));
1422 	file_shift_item = GTK_BIN(items->data)->child;
1423 	g_list_free(items);
1424 
1425 	g_signal_connect(icon_menu, "unmap_event",
1426 			G_CALLBACK(menu_closed), NULL);
1427 }
1428 
1429