4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2002, 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)
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
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 - shared code for the pinboard and panels */
28 #include <gtk/gtkinvisible.h>
35 #include "gui_support.h"
50 #include "usericons.h"
52 typedef void (*RenameFn
)(Icon
*icon
);
54 static GtkWidget
*icon_menu
; /* The popup icon menu */
55 static GtkWidget
*icon_file_menu
; /* The file submenu */
56 static GtkWidget
*icon_file_item
; /* 'File' label */
57 static GtkWidget
*file_shift_item
; /* 'Shift Open' label */
59 /* Widget which holds the selection when we have it */
60 static GtkWidget
*selection_invisible
= NULL
;
61 static guint losing_selection
= 0; /* > 0 => Don't send events */
62 gboolean tmp_icon_selected
= FALSE
; /* When dragging */
64 /* A list of selected Icons */
65 GList
*icon_selection
= NULL
;
67 /* Each entry is a GList of Icons which have the given pathname.
68 * This allows us to update all necessary icons when something changes.
70 static GHashTable
*icons_hash
= NULL
;
72 static Icon
*menu_icon
= NULL
; /* Item clicked if there is no selection */
74 /* Static prototypes */
75 static void rename_activate(GtkWidget
*dialog
);
76 static void menu_closed(GtkWidget
*widget
);
77 static void panel_position_menu(GtkMenu
*menu
, gint
*x
, gint
*y
,
82 static gint
lose_selection(GtkWidget
*widget
, GdkEventSelection
*event
);
83 static void selection_get(GtkWidget
*widget
,
84 GtkSelectionData
*selection_data
,
88 static void remove_items(gpointer data
, guint action
, GtkWidget
*widget
);
89 static void file_op(gpointer data
, guint action
, GtkWidget
*widget
);
90 static void show_rename_box(GtkWidget
*widget
, Icon
*icon
, RenameFn callback
);
104 static GtkItemFactoryEntry menu_def
[] = {
105 {N_("ROX-Filer"), NULL
, NULL
, 0, "<Branch>"},
106 {">" N_("Help"), NULL
, menu_rox_help
, 0, NULL
},
107 {">" N_("Options..."), NULL
, menu_show_options
, 0, NULL
},
108 {">" N_("Home Directory"), NULL
, open_home
, 0, NULL
},
109 {N_("File"), NULL
, NULL
, 0, "<Branch>"},
110 {">" N_("Shift Open"), NULL
, file_op
, ACTION_SHIFT
, NULL
},
111 {">" N_("Help"), NULL
, file_op
, ACTION_HELP
, NULL
},
112 {">" N_("Info"), NULL
, file_op
, ACTION_INFO
, NULL
},
113 {">" N_("Set Run Action..."), NULL
, file_op
, ACTION_RUN_ACTION
, NULL
},
114 {">" N_("Set Icon..."), NULL
, file_op
, ACTION_SET_ICON
, NULL
},
115 {N_("Edit Item"), NULL
, file_op
, ACTION_EDIT
, NULL
},
116 {N_("Show Location"), NULL
, file_op
, ACTION_LOCATION
, NULL
},
117 {N_("Remove Item(s)"), NULL
, remove_items
, 0, NULL
},
120 /****************************************************************
121 * EXTERNAL INTERFACE *
122 ****************************************************************/
128 GtkItemFactory
*item_factory
;
129 GtkTargetEntry target_table
[] =
131 {"text/uri-list", 0, TARGET_URI_LIST
},
132 {"STRING", 0, TARGET_STRING
},
135 icons_hash
= g_hash_table_new(g_str_hash
, g_str_equal
);
137 item_factory
= menu_create(menu_def
,
138 sizeof(menu_def
) / sizeof(*menu_def
),
141 tmp
= g_strconcat("<icon>/", _("File"), NULL
);
142 icon_menu
= gtk_item_factory_get_widget(item_factory
, "<icon>");
143 icon_file_menu
= gtk_item_factory_get_widget(item_factory
, tmp
);
146 /* File '' label... */
147 items
= gtk_container_children(GTK_CONTAINER(icon_menu
));
148 icon_file_item
= GTK_BIN(g_list_nth(items
, 1)->data
)->child
;
151 /* Shift Open... label */
152 items
= gtk_container_children(GTK_CONTAINER(icon_file_menu
));
153 file_shift_item
= GTK_BIN(items
->data
)->child
;
156 gtk_signal_connect(GTK_OBJECT(icon_menu
), "unmap_event",
157 GTK_SIGNAL_FUNC(menu_closed
), NULL
);
159 selection_invisible
= gtk_invisible_new();
161 gtk_signal_connect(GTK_OBJECT(selection_invisible
),
162 "selection_clear_event",
163 GTK_SIGNAL_FUNC(lose_selection
),
166 gtk_signal_connect(GTK_OBJECT(selection_invisible
),
168 GTK_SIGNAL_FUNC(selection_get
), NULL
);
170 gtk_selection_add_targets(selection_invisible
,
171 GDK_SELECTION_PRIMARY
,
173 sizeof(target_table
) / sizeof(*target_table
));
175 tooltips
= gtk_tooltips_new();
178 /* Removes trailing / chars and converts a leading '~/' (if any) to
179 * the user's home dir. g_free() the result.
181 guchar
*icon_convert_path(guchar
*path
)
186 g_return_val_if_fail(path
!= NULL
, NULL
);
188 path_len
= strlen(path
);
189 while (path_len
> 1 && path
[path_len
- 1] == '/')
192 retval
= g_strndup(path
, path_len
);
194 if (path
[0] == '~' && (path
[1] == '\0' || path
[1] == '/'))
196 guchar
*tmp
= retval
;
198 retval
= g_strconcat(home_dir
, retval
+ 1, NULL
);
205 /* The icons_hash table allows us to convert from a path to a list
206 * of icons that use that path.
207 * Add this icon to the list for its path.
209 void icon_hash_path(Icon
*icon
)
213 g_return_if_fail(icon
!= NULL
);
215 /* g_print("[ hashing '%s' ]\n", icon->path); */
217 list
= g_hash_table_lookup(icons_hash
, icon
->path
);
218 list
= g_list_prepend(list
, icon
);
219 g_hash_table_insert(icons_hash
, icon
->path
, list
);
222 /* Remove this icon from the icons_hash table */
223 void icon_unhash_path(Icon
*icon
)
227 g_return_if_fail(icon
!= NULL
);
229 /* g_print("[ unhashing '%s' ]\n", icon->path); */
231 list
= g_hash_table_lookup(icons_hash
, icon
->path
);
232 g_return_if_fail(list
!= NULL
);
234 list
= g_list_remove(list
, icon
);
236 /* Remove it first; the hash key may have changed address */
237 g_hash_table_remove(icons_hash
, icon
->path
);
239 g_hash_table_insert(icons_hash
,
240 ((Icon
*) list
->data
)->path
, list
);
243 /* Called when the pointer moves over the icon */
244 void icon_may_update(Icon
*icon
)
249 g_return_if_fail(icon
!= NULL
);
251 image
= icon
->item
->image
;
252 flags
= icon
->item
->flags
;
256 diritem_restat(icon
->path
, icon
->item
);
258 if (icon
->item
->image
!= image
|| icon
->item
->flags
!= flags
)
260 /* Appearance changed; need to redraw */
262 gtk_widget_queue_clear(icon
->widget
);
264 pinboard_reshape_icon(icon
);
270 /* If path is on an icon then it may have changed... check! */
271 void icons_may_update(guchar
*path
)
275 affected
= g_hash_table_lookup(icons_hash
, path
);
277 for (; affected
; affected
= affected
->next
)
278 icon_may_update((Icon
*) affected
->data
);
281 typedef struct _CheckData CheckData
;
287 static void check_has(gpointer key
, GList
*icons
, CheckData
*check
)
291 g_return_if_fail(icons
!= NULL
);
295 if (is_sub_dir(icon
->path
, check
->path
))
299 /* Set the tooltip AND hide/show the label for panel icons */
300 void icon_set_tip(Icon
*icon
)
306 g_return_if_fail(icon
!= NULL
);
308 widget
= icon
->panel
? icon
->widget
: icon
->win
;
310 if (icon
->panel
&& icon
->label
)
312 if (panel_want_show_text(icon
))
313 gtk_widget_show(icon
->label
);
315 gtk_widget_hide(icon
->label
);
318 if (icon
->panel
&& icon
->socket
)
321 ai
= appinfo_get(icon
->path
, icon
->item
);
323 if (ai
&& ((node
= appinfo_get_section(ai
, "Summary"))))
326 str
= xmlNodeListGetString(node
->doc
,
327 node
->xmlChildrenNode
, 1);
330 gtk_tooltips_set_tip(tooltips
, widget
, str
, NULL
);
334 else if (icon
->panel
&& (!panel_want_show_text(icon
)) && !icon
->socket
)
336 gtk_tooltips_set_tip(tooltips
, widget
,
337 icon
->item
->leafname
, NULL
);
340 gtk_tooltips_set_tip(tooltips
, widget
, NULL
, NULL
);
346 /* Returns TRUE if any icon links to this item (or any item inside
349 gboolean
icons_require(guchar
*path
)
353 /* g_print("[ icons_require(%s)? ]\n", path); */
357 g_hash_table_foreach(icons_hash
, (GHFunc
) check_has
, &check
);
362 /* Callback to update icons of a certain path */
363 static void update_icons(gpointer key
, GList
*icons
, gpointer data
)
365 icons_may_update((guchar
*) key
);
368 /* Check all icons to see if they have been updated */
369 void update_all_icons(void)
371 g_hash_table_foreach(icons_hash
, (GHFunc
) update_icons
, NULL
);
374 /* Show the menu for this panel (NULL for the pinboard).
375 * icon is the icon that was clicked on, if any.
377 void icon_show_menu(GdkEventButton
*event
, Icon
*icon
, Panel
*panel
)
381 pos
[0] = event
->x_root
;
382 pos
[1] = event
->y_root
;
389 /* Shade Remove Item(s) unless there is a selection */
390 menu_set_items_shaded(icon_menu
,
391 (icon_selection
|| menu_icon
) ? FALSE
: TRUE
,
394 menu_show_shift_action(file_shift_item
, icon
? icon
->item
: NULL
,
397 /* Shade the File/Edit/Show items unless an item was clicked */
402 menu_set_items_shaded(icon_menu
, FALSE
, 1, 3);
403 menu_set_items_shaded(icon_file_menu
, FALSE
, 0, 5);
404 if (!can_set_run_action(icon
->item
))
405 menu_set_items_shaded(icon_file_menu
, TRUE
, 3, 1);
407 tmp
= g_strdup_printf("%s '%s'",
408 basetype_name(icon
->item
),
409 icon
->item
->leafname
);
410 gtk_label_set_text(GTK_LABEL(icon_file_item
), tmp
);
413 /* Check for app-specific menu */
414 appmenu_add(icon
->path
, icon
->item
, icon_menu
);
418 menu_set_items_shaded(icon_menu
, TRUE
, 1, 3);
419 menu_set_items_shaded(icon_file_menu
, TRUE
, 0, 5);
420 gtk_label_set_text(GTK_LABEL(icon_file_item
), _("Nothing"));
425 PanelSide side
= panel
->side
;
427 if (side
== PANEL_LEFT
)
429 else if (side
== PANEL_RIGHT
)
432 if (side
== PANEL_TOP
)
434 else if (side
== PANEL_BOTTOM
)
438 gtk_menu_popup(GTK_MENU(icon_menu
), NULL
, NULL
,
439 panel
? panel_position_menu
: position_menu
,
440 (gpointer
) pos
, event
->button
, event
->time
);
443 void icon_set_selected(Icon
*icon
, gboolean selected
)
445 gboolean clear
= FALSE
;
447 g_return_if_fail(icon
!= NULL
);
449 if (icon
->selected
== selected
)
452 /* When selecting an icon on another panel, we need to unselect
453 * everything else afterwards.
455 if (selected
&& icon_selection
)
457 Icon
*current
= (Icon
*) icon_selection
->data
;
459 if (icon
->panel
!= current
->panel
)
463 icon
->selected
= selected
;
465 gtk_widget_queue_clear(icon
->widget
);
469 icon_selection
= g_list_prepend(icon_selection
, icon
);
470 if (losing_selection
== 0 && !icon_selection
->next
)
473 gtk_selection_owner_set(selection_invisible
,
474 GDK_SELECTION_PRIMARY
,
475 gdk_event_get_time(gtk_get_current_event()));
480 icon_selection
= g_list_remove(icon_selection
, icon
);
481 if (losing_selection
== 0 && !icon_selection
)
483 /* Release selection */
484 gtk_selection_owner_set(NULL
,
485 GDK_SELECTION_PRIMARY
,
486 gdk_event_get_time(gtk_get_current_event()));
491 icon_select_only(icon
);
494 /* Clear everything, except 'select', which is selected.
495 * If select is NULL, unselects everything.
497 void icon_select_only(Icon
*select
)
499 GList
*to_clear
, *next
;
502 icon_set_selected(select
, TRUE
);
504 to_clear
= g_list_copy(icon_selection
);
507 to_clear
= g_list_remove(to_clear
, select
);
509 for (next
= to_clear
; next
; next
= next
->next
)
510 icon_set_selected((Icon
*) next
->data
, FALSE
);
512 g_list_free(to_clear
);
515 /* XXX: Under Gtk+ 2.0, can this get called twice? */
516 void icon_destroyed(Icon
*icon
)
518 g_return_if_fail(icon
!= NULL
);
520 if (icon
== menu_icon
)
526 g_object_unref(G_OBJECT(icon
->layout
));
531 icon_unhash_path(icon
);
533 pinboard_wink_item(NULL
, FALSE
);
535 if (g_list_find(icon_selection
, icon
))
537 icon_selection
= g_list_remove(icon_selection
, icon
);
540 gtk_selection_owner_set(NULL
,
541 GDK_SELECTION_PRIMARY
,
542 gdk_event_get_time(gtk_get_current_event()));
545 if (icon
->panel
== NULL
)
547 gdk_pixmap_unref(icon
->mask
);
548 if (current_pinboard
)
549 current_pinboard
->icons
=
550 g_list_remove(current_pinboard
->icons
, icon
);
553 diritem_free(icon
->item
);
555 g_free(icon
->src_path
);
559 static void set_tip(gpointer key
, GList
*icons
, gpointer data
)
561 for (; icons
; icons
= icons
->next
)
563 Icon
*icon
= (Icon
*) icons
->data
;
569 void icons_update_tip(void)
571 g_hash_table_foreach(icons_hash
, (GHFunc
) set_tip
, NULL
);
574 /****************************************************************
575 * INTERNAL FUNCTIONS *
576 ****************************************************************/
578 static void rename_activate(GtkWidget
*dialog
)
580 GtkWidget
*entry
, *src
;
583 guchar
*new_name
, *new_src
;
585 entry
= gtk_object_get_data(GTK_OBJECT(dialog
), "new_name");
586 icon
= gtk_object_get_data(GTK_OBJECT(dialog
), "callback_icon");
587 callback
= gtk_object_get_data(GTK_OBJECT(dialog
), "callback_fn");
588 src
= gtk_object_get_data(GTK_OBJECT(dialog
), "new_path");
590 g_return_if_fail(callback
!= NULL
&&
595 new_name
= gtk_entry_get_text(GTK_ENTRY(entry
));
596 new_src
= gtk_entry_get_text(GTK_ENTRY(src
));
598 if (*new_name
== '\0')
600 _("The label must contain at least one character!"));
601 else if (*new_src
== '\0')
603 _("The location must contain at least one character!"));
607 GdkFont
*font
= icon
->widget
->style
->font
;
610 g_free(icon
->item
->leafname
);
611 g_free(icon
->src_path
);
613 icon
->src_path
= g_strdup(new_src
);
615 icon_unhash_path(icon
);
617 icon
->path
= icon_convert_path(new_src
);
618 icon_hash_path(icon
);
620 icon
->item
->leafname
= g_strdup(new_name
);
622 icon
->name_width
= gdk_string_measure(font
, new_name
);
624 /* XXX: Set name_width in size_and_shape? */
626 diritem_restat(icon
->path
, icon
->item
);
629 gtk_widget_destroy(dialog
);
633 static void menu_closed(GtkWidget
*widget
)
639 static void panel_position_menu(GtkMenu
*menu
, gint
*x
, gint
*y
,
645 int *pos
= (int *) data
;
646 GtkRequisition requisition
;
648 gtk_widget_size_request(GTK_WIDGET(menu
), &requisition
);
651 *x
= screen_width
- MENU_MARGIN
- requisition
.width
;
652 else if (pos
[0] == -2)
655 *x
= pos
[0] - (requisition
.width
>> 2);
658 *y
= screen_height
- MENU_MARGIN
- requisition
.height
;
659 else if (pos
[1] == -2)
662 *y
= pos
[1] - (requisition
.height
>> 2);
664 *x
= CLAMP(*x
, 0, screen_width
- requisition
.width
);
665 *y
= CLAMP(*y
, 0, screen_height
- requisition
.height
);
672 /* Called when another application takes the selection away from us */
673 static gint
lose_selection(GtkWidget
*widget
, GdkEventSelection
*event
)
675 /* Don't send any events */
678 icon_select_only(NULL
);
684 /* Called when another application wants the contents of our selection */
685 static void selection_get(GtkWidget
*widget
,
686 GtkSelectionData
*selection_data
,
693 guchar
*leader
= NULL
;
695 str
= g_string_new(NULL
);
697 if (info
== TARGET_URI_LIST
)
698 leader
= g_strdup_printf("file://%s", our_host_name_for_dnd());
700 for (next
= icon_selection
; next
; next
= next
->next
)
702 Icon
*icon
= (Icon
*) next
->data
;
705 g_string_append(str
, leader
);
706 g_string_append(str
, icon
->path
);
707 g_string_append_c(str
, ' ');
712 gtk_selection_data_set(selection_data
,
713 gdk_atom_intern("STRING", FALSE
),
716 str
->len
? str
->len
- 1 : 0);
718 g_string_free(str
, TRUE
);
721 static void rename_cb(Icon
*icon
)
725 gtk_widget_queue_clear(icon
->widget
);
726 panel_icon_renamed(icon
);
727 panel_save(icon
->panel
);
731 pinboard_reshape_icon(icon
);
736 static void remove_items(gpointer data
, guint action
, GtkWidget
*widget
)
742 icon_set_selected(menu_icon
, TRUE
);
744 next
= icon_selection
;
749 _("You must first select some items to remove"));
753 panel
= ((Icon
*) next
->data
)->panel
;
759 Icon
*icon
= (Icon
*) next
->data
;
763 gtk_widget_destroy(icon
->widget
);
772 Icon
*icon
= (Icon
*) next
->data
;
776 gtk_widget_destroy(icon
->win
);
783 static void file_op(gpointer data
, guint action
, GtkWidget
*widget
)
787 delayed_error(_("You must open the menu over an item"));
794 run_diritem(menu_icon
->path
, menu_icon
->item
,
798 show_rename_box(menu_icon
->widget
,
799 menu_icon
, rename_cb
);
801 case ACTION_LOCATION
:
802 open_to_show(menu_icon
->path
);
805 show_item_help(menu_icon
->path
, menu_icon
->item
);
808 infobox_new(menu_icon
->path
);
810 case ACTION_RUN_ACTION
:
811 if (can_set_run_action(menu_icon
->item
))
812 type_set_handler_dialog(
813 menu_icon
->item
->mime_type
);
816 _("You can only set the run action for a "
819 case ACTION_SET_ICON
:
820 icon_set_handler_dialog(menu_icon
->item
,
826 /* Opens a box allowing the user to change the name of a pinned icon.
827 * If 'widget' is destroyed then the box will close.
828 * If the user chooses OK then the callback is called once the icon's
829 * name, src_path and path fields have been updated and the item field
832 static void show_rename_box(GtkWidget
*widget
, Icon
*icon
, RenameFn callback
)
834 GtkWidget
*dialog
, *hbox
, *vbox
, *label
, *entry
, *button
;
836 dialog
= gtk_window_new(GTK_WINDOW_DIALOG
);
838 gtk_window_set_type_hint(GTK_WINDOW(dialog
),
839 GDK_WINDOW_TYPE_HINT_DIALOG
);
841 gtk_window_set_title(GTK_WINDOW(dialog
), _("Edit Item"));
842 gtk_container_set_border_width(GTK_CONTAINER(dialog
), 10);
844 vbox
= gtk_vbox_new(FALSE
, 4);
845 gtk_container_add(GTK_CONTAINER(dialog
), vbox
);
847 label
= gtk_label_new(_("Clicking the icon opens:"));
848 gtk_box_pack_start(GTK_BOX(vbox
), label
, TRUE
, TRUE
, 0);
850 entry
= gtk_entry_new();
851 gtk_box_pack_start(GTK_BOX(vbox
), entry
, TRUE
, FALSE
, 2);
852 gtk_entry_set_text(GTK_ENTRY(entry
), icon
->src_path
);
853 gtk_object_set_data(GTK_OBJECT(dialog
), "new_path", entry
);
854 gtk_signal_connect_object(GTK_OBJECT(entry
), "activate",
855 GTK_SIGNAL_FUNC(rename_activate
), GTK_OBJECT(dialog
));
857 gtk_box_pack_start(GTK_BOX(vbox
), gtk_hseparator_new(), TRUE
, TRUE
, 2);
859 label
= gtk_label_new(_("The text displayed under the icon is:"));
860 gtk_box_pack_start(GTK_BOX(vbox
), label
, TRUE
, TRUE
, 0);
861 entry
= gtk_entry_new();
862 gtk_box_pack_start(GTK_BOX(vbox
), entry
, TRUE
, FALSE
, 2);
863 gtk_entry_set_text(GTK_ENTRY(entry
), icon
->item
->leafname
);
864 gtk_editable_select_region(GTK_EDITABLE(entry
), 0, -1);
865 gtk_widget_grab_focus(entry
);
866 gtk_object_set_data(GTK_OBJECT(dialog
), "new_name", entry
);
867 gtk_signal_connect_object(GTK_OBJECT(entry
), "activate",
868 GTK_SIGNAL_FUNC(rename_activate
), GTK_OBJECT(dialog
));
870 gtk_signal_connect_object_while_alive(GTK_OBJECT(widget
),
872 GTK_SIGNAL_FUNC(gtk_widget_destroy
),
875 gtk_object_set_data(GTK_OBJECT(dialog
), "callback_icon", icon
);
876 gtk_object_set_data(GTK_OBJECT(dialog
), "callback_fn", callback
);
878 gtk_box_pack_start(GTK_BOX(vbox
), gtk_hseparator_new(), TRUE
, TRUE
, 2);
880 hbox
= gtk_hbox_new(TRUE
, 4);
881 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, TRUE
, 0);
883 button
= gtk_button_new_with_label(_("OK"));
884 gtk_box_pack_start(GTK_BOX(hbox
), button
, TRUE
, TRUE
, 0);
885 GTK_WIDGET_SET_FLAGS(button
, GTK_CAN_DEFAULT
);
886 gtk_window_set_default(GTK_WINDOW(dialog
), button
);
887 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
888 GTK_SIGNAL_FUNC(rename_activate
), GTK_OBJECT(dialog
));
890 button
= gtk_button_new_with_label(_("Cancel"));
891 GTK_WIDGET_SET_FLAGS(button
, GTK_CAN_DEFAULT
);
892 gtk_box_pack_start(GTK_BOX(hbox
), button
, TRUE
, TRUE
, 0);
893 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
894 GTK_SIGNAL_FUNC(gtk_widget_destroy
),
897 gtk_widget_show_all(dialog
);