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 */
26 #undef GTK_DISABLE_DEPRECATED
30 #include <gtk/gtkinvisible.h>
32 #include <libxml/parser.h>
37 #include "gui_support.h"
52 #include "usericons.h"
54 typedef void (*RenameFn
)(Icon
*icon
);
56 static GtkWidget
*icon_menu
; /* The popup icon menu */
57 static GtkWidget
*icon_file_menu
; /* The file submenu */
58 static GtkWidget
*icon_file_item
; /* 'File' label */
59 static GtkWidget
*file_shift_item
; /* 'Shift Open' label */
61 /* Widget which holds the selection when we have it */
62 static GtkWidget
*selection_invisible
= NULL
;
63 static guint losing_selection
= 0; /* > 0 => Don't send events */
64 gboolean tmp_icon_selected
= FALSE
; /* When dragging */
66 /* A list of selected Icons */
67 GList
*icon_selection
= NULL
;
69 /* Each entry is a GList of Icons which have the given pathname.
70 * This allows us to update all necessary icons when something changes.
72 static GHashTable
*icons_hash
= NULL
;
74 static Icon
*menu_icon
= NULL
; /* Item clicked if there is no selection */
76 /* Static prototypes */
77 static void rename_activate(GtkWidget
*dialog
);
78 static void menu_closed(GtkWidget
*widget
);
79 static void panel_position_menu(GtkMenu
*menu
, gint
*x
, gint
*y
,
80 gboolean
*push_in
, gpointer data
);
81 static gint
lose_selection(GtkWidget
*widget
, GdkEventSelection
*event
);
82 static void selection_get(GtkWidget
*widget
,
83 GtkSelectionData
*selection_data
,
87 static void remove_items(gpointer data
, guint action
, GtkWidget
*widget
);
88 static void file_op(gpointer data
, guint action
, GtkWidget
*widget
);
89 static void show_rename_box(GtkWidget
*widget
, Icon
*icon
, RenameFn callback
);
103 static GtkItemFactoryEntry menu_def
[] = {
104 {N_("ROX-Filer"), NULL
, NULL
, 0, "<Branch>"},
105 {">" N_("Help"), NULL
, menu_rox_help
, 0, NULL
},
106 {">" N_("Options..."), NULL
, menu_show_options
, 0, NULL
},
107 {">" N_("Home Directory"), NULL
, open_home
, 0, NULL
},
108 {N_("File"), NULL
, NULL
, 0, "<Branch>"},
109 {">" N_("Shift Open"), NULL
, file_op
, ACTION_SHIFT
, NULL
},
110 {">" N_("Help"), NULL
, file_op
, ACTION_HELP
, NULL
},
111 {">" N_("Info"), NULL
, file_op
, ACTION_INFO
, NULL
},
112 {">" N_("Set Run Action..."), NULL
, file_op
, ACTION_RUN_ACTION
, NULL
},
113 {">" N_("Set Icon..."), NULL
, file_op
, ACTION_SET_ICON
, NULL
},
114 {N_("Edit Item"), NULL
, file_op
, ACTION_EDIT
, NULL
},
115 {N_("Show Location"), NULL
, file_op
, ACTION_LOCATION
, NULL
},
116 {N_("Remove Item(s)"), NULL
, remove_items
, 0, NULL
},
119 /****************************************************************
120 * EXTERNAL INTERFACE *
121 ****************************************************************/
127 GtkItemFactory
*item_factory
;
128 GtkTargetEntry target_table
[] =
130 {"text/uri-list", 0, TARGET_URI_LIST
},
131 {"STRING", 0, TARGET_STRING
},
134 icons_hash
= g_hash_table_new(g_str_hash
, g_str_equal
);
136 item_factory
= menu_create(menu_def
,
137 sizeof(menu_def
) / sizeof(*menu_def
),
140 tmp
= g_strconcat("<icon>/", _("File"), NULL
);
141 icon_menu
= gtk_item_factory_get_widget(item_factory
, "<icon>");
142 icon_file_menu
= gtk_item_factory_get_widget(item_factory
, tmp
);
145 /* File '' label... */
146 items
= gtk_container_get_children(GTK_CONTAINER(icon_menu
));
147 icon_file_item
= GTK_BIN(g_list_nth(items
, 1)->data
)->child
;
150 /* Shift Open... label */
151 items
= gtk_container_get_children(GTK_CONTAINER(icon_file_menu
));
152 file_shift_item
= GTK_BIN(items
->data
)->child
;
155 g_signal_connect(icon_menu
, "unmap_event",
156 G_CALLBACK(menu_closed
), NULL
);
158 selection_invisible
= gtk_invisible_new();
160 g_signal_connect(selection_invisible
, "selection_clear_event",
161 G_CALLBACK(lose_selection
), NULL
);
163 g_signal_connect(selection_invisible
, "selection_get",
164 G_CALLBACK(selection_get
), NULL
);
166 gtk_selection_add_targets(selection_invisible
,
167 GDK_SELECTION_PRIMARY
,
169 sizeof(target_table
) / sizeof(*target_table
));
171 tooltips
= gtk_tooltips_new();
174 /* The icons_hash table allows us to convert from a path to a list
175 * of icons that use that path.
176 * Add this icon to the list for its path.
178 void icon_hash_path(Icon
*icon
)
182 g_return_if_fail(icon
!= NULL
);
184 /* g_print("[ hashing '%s' ]\n", icon->path); */
186 list
= g_hash_table_lookup(icons_hash
, icon
->path
);
187 list
= g_list_prepend(list
, icon
);
188 g_hash_table_insert(icons_hash
, icon
->path
, list
);
191 /* Remove this icon from the icons_hash table */
192 void icon_unhash_path(Icon
*icon
)
196 g_return_if_fail(icon
!= NULL
);
198 /* g_print("[ unhashing '%s' ]\n", icon->path); */
200 list
= g_hash_table_lookup(icons_hash
, icon
->path
);
201 g_return_if_fail(list
!= NULL
);
203 list
= g_list_remove(list
, icon
);
205 /* Remove it first; the hash key may have changed address */
206 g_hash_table_remove(icons_hash
, icon
->path
);
208 g_hash_table_insert(icons_hash
,
209 ((Icon
*) list
->data
)->path
, list
);
212 /* Called when the pointer moves over the icon */
213 void icon_may_update(Icon
*icon
)
218 g_return_if_fail(icon
!= NULL
);
220 image
= icon
->item
->image
;
221 flags
= icon
->item
->flags
;
226 diritem_restat(icon
->path
, icon
->item
);
228 if (icon
->item
->image
!= image
|| icon
->item
->flags
!= flags
)
230 /* Appearance changed; need to redraw */
232 gtk_widget_queue_draw(icon
->widget
);
234 pinboard_reshape_icon(icon
);
238 g_object_unref(image
);
241 /* If path is on an icon then it may have changed... check! */
242 void icons_may_update(const gchar
*path
)
246 affected
= g_hash_table_lookup(icons_hash
, path
);
248 for (; affected
; affected
= affected
->next
)
249 icon_may_update((Icon
*) affected
->data
);
252 typedef struct _CheckData CheckData
;
258 static void check_has(gpointer key
, GList
*icons
, CheckData
*check
)
262 g_return_if_fail(icons
!= NULL
);
266 if (is_sub_dir(icon
->path
, check
->path
))
270 /* Set the tooltip AND hide/show the label for panel icons */
271 void icon_set_tip(Icon
*icon
)
277 g_return_if_fail(icon
!= NULL
);
279 widget
= icon
->panel
? icon
->widget
: icon
->win
;
281 if (icon
->panel
&& icon
->label
)
283 if (panel_want_show_text(icon
))
284 gtk_widget_show(icon
->label
);
286 gtk_widget_hide(icon
->label
);
289 if (icon
->panel
&& icon
->socket
)
292 ai
= appinfo_get(icon
->path
, icon
->item
);
294 if (ai
&& ((node
= appinfo_get_section(ai
, "Summary"))))
297 str
= xmlNodeListGetString(node
->doc
,
298 node
->xmlChildrenNode
, 1);
301 gtk_tooltips_set_tip(tooltips
, widget
, str
, NULL
);
305 else if (icon
->panel
&& (!panel_want_show_text(icon
)) && !icon
->socket
)
307 gtk_tooltips_set_tip(tooltips
, widget
,
308 icon
->item
->leafname
, NULL
);
311 gtk_tooltips_set_tip(tooltips
, widget
, NULL
, NULL
);
317 /* Returns TRUE if any icon links to this item (or any item inside
320 gboolean
icons_require(const gchar
*path
)
324 /* g_print("[ icons_require(%s)? ]\n", path); */
328 g_hash_table_foreach(icons_hash
, (GHFunc
) check_has
, &check
);
333 /* Callback to update icons of a certain path */
334 static void update_icons(gpointer key
, GList
*icons
, gpointer data
)
336 icons_may_update((guchar
*) key
);
339 /* Check all icons to see if they have been updated */
340 void update_all_icons(void)
342 g_hash_table_foreach(icons_hash
, (GHFunc
) update_icons
, NULL
);
345 /* Show the menu for this panel (NULL for the pinboard).
346 * icon is the icon that was clicked on, if any.
348 void icon_show_menu(GdkEventButton
*event
, Icon
*icon
, Panel
*panel
)
352 pos
[0] = event
->x_root
;
353 pos
[1] = event
->y_root
;
360 /* Shade Remove Item(s) unless there is a selection */
361 menu_set_items_shaded(icon_menu
,
362 (icon_selection
|| menu_icon
) ? FALSE
: TRUE
,
365 menu_show_shift_action(file_shift_item
, icon
? icon
->item
: NULL
,
368 /* Shade the File/Edit/Show items unless an item was clicked */
373 menu_set_items_shaded(icon_menu
, FALSE
, 1, 3);
374 menu_set_items_shaded(icon_file_menu
, FALSE
, 0, 5);
375 if (!can_set_run_action(icon
->item
))
376 menu_set_items_shaded(icon_file_menu
, TRUE
, 3, 1);
378 tmp
= g_strdup_printf("%s '%s'",
379 basetype_name(icon
->item
),
380 icon
->item
->leafname
);
381 gtk_label_set_text(GTK_LABEL(icon_file_item
), tmp
);
384 /* Check for app-specific menu */
385 appmenu_add(icon
->path
, icon
->item
, icon_menu
);
389 menu_set_items_shaded(icon_menu
, TRUE
, 1, 3);
390 menu_set_items_shaded(icon_file_menu
, TRUE
, 0, 5);
391 gtk_label_set_text(GTK_LABEL(icon_file_item
), _("Nothing"));
396 PanelSide side
= panel
->side
;
398 if (side
== PANEL_LEFT
)
400 else if (side
== PANEL_RIGHT
)
403 if (side
== PANEL_TOP
)
405 else if (side
== PANEL_BOTTOM
)
409 gtk_menu_popup(GTK_MENU(icon_menu
), NULL
, NULL
,
410 panel
? panel_position_menu
: position_menu
,
411 (gpointer
) pos
, event
->button
, event
->time
);
414 void icon_set_selected(Icon
*icon
, gboolean selected
)
416 gboolean clear
= FALSE
;
418 g_return_if_fail(icon
!= NULL
);
420 if (icon
->selected
== selected
)
423 /* When selecting an icon on another panel, we need to unselect
424 * everything else afterwards.
426 if (selected
&& icon_selection
)
428 Icon
*current
= (Icon
*) icon_selection
->data
;
430 if (icon
->panel
!= current
->panel
)
434 icon
->selected
= selected
;
436 gtk_widget_queue_draw(icon
->widget
);
440 icon_selection
= g_list_prepend(icon_selection
, icon
);
441 if (losing_selection
== 0 && !icon_selection
->next
)
444 gtk_selection_owner_set(selection_invisible
,
445 GDK_SELECTION_PRIMARY
,
446 gdk_event_get_time(gtk_get_current_event()));
451 icon_selection
= g_list_remove(icon_selection
, icon
);
452 if (losing_selection
== 0 && !icon_selection
)
454 /* Release selection */
455 gtk_selection_owner_set(NULL
,
456 GDK_SELECTION_PRIMARY
,
457 gdk_event_get_time(gtk_get_current_event()));
462 icon_select_only(icon
);
465 /* Clear everything, except 'select', which is selected.
466 * If select is NULL, unselects everything.
468 void icon_select_only(Icon
*select
)
470 GList
*to_clear
, *next
;
473 icon_set_selected(select
, TRUE
);
475 to_clear
= g_list_copy(icon_selection
);
478 to_clear
= g_list_remove(to_clear
, select
);
480 for (next
= to_clear
; next
; next
= next
->next
)
481 icon_set_selected((Icon
*) next
->data
, FALSE
);
483 g_list_free(to_clear
);
486 /* XXX: Under Gtk+ 2.0, can this get called twice? */
487 void icon_destroyed(Icon
*icon
)
489 g_return_if_fail(icon
!= NULL
);
491 if (icon
== menu_icon
)
496 g_object_unref(G_OBJECT(icon
->layout
));
500 icon_unhash_path(icon
);
502 pinboard_wink_item(NULL
, FALSE
);
504 if (g_list_find(icon_selection
, icon
))
506 icon_selection
= g_list_remove(icon_selection
, icon
);
509 gtk_selection_owner_set(NULL
,
510 GDK_SELECTION_PRIMARY
,
511 gdk_event_get_time(gtk_get_current_event()));
514 if (icon
->panel
== NULL
)
516 gdk_pixmap_unref(icon
->mask
);
517 if (current_pinboard
)
518 current_pinboard
->icons
=
519 g_list_remove(current_pinboard
->icons
, icon
);
522 diritem_free(icon
->item
);
524 g_free(icon
->src_path
);
528 static void set_tip(gpointer key
, GList
*icons
, gpointer data
)
530 for (; icons
; icons
= icons
->next
)
532 Icon
*icon
= (Icon
*) icons
->data
;
538 void icons_update_tip(void)
540 g_hash_table_foreach(icons_hash
, (GHFunc
) set_tip
, NULL
);
543 /* Removes trailing / chars and converts a leading '~/' (if any) to
544 * the user's home dir. g_free() the result.
546 gchar
*icon_convert_path(const gchar
*path
)
551 g_return_val_if_fail(path
!= NULL
, NULL
);
553 path_len
= strlen(path
);
554 while (path_len
> 1 && path
[path_len
- 1] == '/')
557 retval
= g_strndup(path
, path_len
);
559 if (path
[0] == '~' && (path
[1] == '\0' || path
[1] == '/'))
561 guchar
*tmp
= retval
;
563 retval
= g_strconcat(home_dir
, retval
+ 1, NULL
);
570 /****************************************************************
571 * INTERNAL FUNCTIONS *
572 ****************************************************************/
574 static void rename_activate(GtkWidget
*dialog
)
576 GtkWidget
*entry
, *src
;
579 const guchar
*new_name
, *new_src
;
581 entry
= g_object_get_data(G_OBJECT(dialog
), "new_name");
582 icon
= g_object_get_data(G_OBJECT(dialog
), "callback_icon");
583 callback
= g_object_get_data(G_OBJECT(dialog
), "callback_fn");
584 src
= g_object_get_data(G_OBJECT(dialog
), "new_path");
586 g_return_if_fail(callback
!= NULL
&&
591 new_name
= gtk_entry_get_text(GTK_ENTRY(entry
));
592 new_src
= gtk_entry_get_text(GTK_ENTRY(src
));
594 if (*new_name
== '\0')
596 _("The label must contain at least one character!"));
597 else if (*new_src
== '\0')
599 _("The location must contain at least one character!"));
602 g_free(icon
->item
->leafname
);
603 g_free(icon
->src_path
);
605 icon
->src_path
= g_strdup(new_src
);
607 icon_unhash_path(icon
);
609 icon
->path
= icon_convert_path(new_src
);
610 icon_hash_path(icon
);
612 icon
->item
->leafname
= g_strdup(new_name
);
613 /* XXX: Set name_width in size_and_shape? */
615 diritem_restat(icon
->path
, icon
->item
);
618 gtk_widget_destroy(dialog
);
622 static void menu_closed(GtkWidget
*widget
)
628 static void panel_position_menu(GtkMenu
*menu
, gint
*x
, gint
*y
,
629 gboolean
*push_in
, gpointer data
)
631 int *pos
= (int *) data
;
632 GtkRequisition requisition
;
634 gtk_widget_size_request(GTK_WIDGET(menu
), &requisition
);
637 *x
= screen_width
- MENU_MARGIN
- requisition
.width
;
638 else if (pos
[0] == -2)
641 *x
= pos
[0] - (requisition
.width
>> 2);
644 *y
= screen_height
- MENU_MARGIN
- requisition
.height
;
645 else if (pos
[1] == -2)
648 *y
= pos
[1] - (requisition
.height
>> 2);
650 *x
= CLAMP(*x
, 0, screen_width
- requisition
.width
);
651 *y
= CLAMP(*y
, 0, screen_height
- requisition
.height
);
656 /* Called when another application takes the selection away from us */
657 static gint
lose_selection(GtkWidget
*widget
, GdkEventSelection
*event
)
659 /* Don't send any events */
662 icon_select_only(NULL
);
668 /* Called when another application wants the contents of our selection */
669 static void selection_get(GtkWidget
*widget
,
670 GtkSelectionData
*selection_data
,
677 guchar
*leader
= NULL
;
679 str
= g_string_new(NULL
);
681 if (info
== TARGET_URI_LIST
)
682 leader
= g_strdup_printf("file://%s", our_host_name_for_dnd());
684 for (next
= icon_selection
; next
; next
= next
->next
)
686 Icon
*icon
= (Icon
*) next
->data
;
689 g_string_append(str
, leader
);
690 g_string_append(str
, icon
->path
);
691 g_string_append_c(str
, ' ');
696 gtk_selection_data_set(selection_data
,
697 gdk_atom_intern("STRING", FALSE
),
700 str
->len
? str
->len
- 1 : 0);
702 g_string_free(str
, TRUE
);
705 static void rename_cb(Icon
*icon
)
709 gtk_widget_queue_clear(icon
->widget
);
710 panel_icon_renamed(icon
);
711 panel_save(icon
->panel
);
715 pinboard_reshape_icon(icon
);
720 static void remove_items(gpointer data
, guint action
, GtkWidget
*widget
)
726 icon_set_selected(menu_icon
, TRUE
);
728 next
= icon_selection
;
733 _("You must first select some items to remove"));
737 panel
= ((Icon
*) next
->data
)->panel
;
743 Icon
*icon
= (Icon
*) next
->data
;
747 gtk_widget_destroy(icon
->widget
);
756 Icon
*icon
= (Icon
*) next
->data
;
760 gtk_widget_destroy(icon
->win
);
767 static void file_op(gpointer data
, guint action
, GtkWidget
*widget
)
771 delayed_error(_("You must open the menu over an item"));
778 run_diritem(menu_icon
->path
, menu_icon
->item
,
782 show_rename_box(menu_icon
->widget
,
783 menu_icon
, rename_cb
);
785 case ACTION_LOCATION
:
786 open_to_show(menu_icon
->path
);
789 show_item_help(menu_icon
->path
, menu_icon
->item
);
792 infobox_new(menu_icon
->path
);
794 case ACTION_RUN_ACTION
:
795 if (can_set_run_action(menu_icon
->item
))
796 type_set_handler_dialog(
797 menu_icon
->item
->mime_type
);
800 _("You can only set the run action for a "
803 case ACTION_SET_ICON
:
804 icon_set_handler_dialog(menu_icon
->item
,
810 /* Opens a box allowing the user to change the name of a pinned icon.
811 * If 'widget' is destroyed then the box will close.
812 * If the user chooses OK then the callback is called once the icon's
813 * name, src_path and path fields have been updated and the item field
816 static void show_rename_box(GtkWidget
*widget
, Icon
*icon
, RenameFn callback
)
818 GtkWidget
*dialog
, *hbox
, *vbox
, *label
, *entry
, *button
;
820 /* XXX: Use a real dialog box! */
821 dialog
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
822 gtk_window_set_type_hint(GTK_WINDOW(dialog
),
823 GDK_WINDOW_TYPE_HINT_DIALOG
);
825 gtk_window_set_title(GTK_WINDOW(dialog
), _("Edit Item"));
826 gtk_container_set_border_width(GTK_CONTAINER(dialog
), 10);
828 vbox
= gtk_vbox_new(FALSE
, 4);
829 gtk_container_add(GTK_CONTAINER(dialog
), vbox
);
831 label
= gtk_label_new(_("Clicking the icon opens:"));
832 gtk_box_pack_start(GTK_BOX(vbox
), label
, TRUE
, TRUE
, 0);
834 entry
= gtk_entry_new();
835 gtk_box_pack_start(GTK_BOX(vbox
), entry
, TRUE
, FALSE
, 2);
836 gtk_entry_set_text(GTK_ENTRY(entry
), icon
->src_path
);
837 g_object_set_data(G_OBJECT(dialog
), "new_path", entry
);
838 g_signal_connect_swapped(entry
, "activate",
839 G_CALLBACK(rename_activate
), dialog
);
841 gtk_box_pack_start(GTK_BOX(vbox
), gtk_hseparator_new(), TRUE
, TRUE
, 2);
843 label
= gtk_label_new(_("The text displayed under the icon is:"));
844 gtk_box_pack_start(GTK_BOX(vbox
), label
, TRUE
, TRUE
, 0);
845 entry
= gtk_entry_new();
846 gtk_box_pack_start(GTK_BOX(vbox
), entry
, TRUE
, FALSE
, 2);
847 gtk_entry_set_text(GTK_ENTRY(entry
), icon
->item
->leafname
);
848 gtk_editable_select_region(GTK_EDITABLE(entry
), 0, -1);
849 gtk_widget_grab_focus(entry
);
850 g_object_set_data(G_OBJECT(dialog
), "new_name", entry
);
851 g_signal_connect_swapped(entry
, "activate",
852 G_CALLBACK(rename_activate
), dialog
);
854 gtk_signal_connect_object_while_alive(GTK_OBJECT(widget
),
856 GTK_SIGNAL_FUNC(gtk_widget_destroy
),
859 gtk_object_set_data(GTK_OBJECT(dialog
), "callback_icon", icon
);
860 gtk_object_set_data(GTK_OBJECT(dialog
), "callback_fn", callback
);
862 gtk_box_pack_start(GTK_BOX(vbox
), gtk_hseparator_new(), TRUE
, TRUE
, 2);
864 hbox
= gtk_hbox_new(TRUE
, 4);
865 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, TRUE
, 0);
867 button
= gtk_button_new_with_label(_("OK"));
868 gtk_box_pack_start(GTK_BOX(hbox
), button
, TRUE
, TRUE
, 0);
869 GTK_WIDGET_SET_FLAGS(button
, GTK_CAN_DEFAULT
);
870 gtk_window_set_default(GTK_WINDOW(dialog
), button
);
871 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
872 GTK_SIGNAL_FUNC(rename_activate
), GTK_OBJECT(dialog
));
874 button
= gtk_button_new_with_label(_("Cancel"));
875 GTK_WIDGET_SET_FLAGS(button
, GTK_CAN_DEFAULT
);
876 gtk_box_pack_start(GTK_BOX(hbox
), button
, TRUE
, TRUE
, 0);
877 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
878 GTK_SIGNAL_FUNC(gtk_widget_destroy
),
881 gtk_widget_show_all(dialog
);