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 /* The icons_hash table allows us to convert from a path to a list
179 * of icons that use that path.
180 * Add this icon to the list for its path.
182 void icon_hash_path(Icon
*icon
)
186 g_return_if_fail(icon
!= NULL
);
188 /* g_print("[ hashing '%s' ]\n", icon->path); */
190 list
= g_hash_table_lookup(icons_hash
, icon
->path
);
191 list
= g_list_prepend(list
, icon
);
192 g_hash_table_insert(icons_hash
, icon
->path
, list
);
195 /* Remove this icon from the icons_hash table */
196 void icon_unhash_path(Icon
*icon
)
200 g_return_if_fail(icon
!= NULL
);
202 /* g_print("[ unhashing '%s' ]\n", icon->path); */
204 list
= g_hash_table_lookup(icons_hash
, icon
->path
);
205 g_return_if_fail(list
!= NULL
);
207 list
= g_list_remove(list
, icon
);
209 /* Remove it first; the hash key may have changed address */
210 g_hash_table_remove(icons_hash
, icon
->path
);
212 g_hash_table_insert(icons_hash
,
213 ((Icon
*) list
->data
)->path
, list
);
216 /* Called when the pointer moves over the icon */
217 void icon_may_update(Icon
*icon
)
222 g_return_if_fail(icon
!= NULL
);
224 image
= icon
->item
->image
;
225 flags
= icon
->item
->flags
;
229 diritem_restat(icon
->path
, icon
->item
);
231 if (icon
->item
->image
!= image
|| icon
->item
->flags
!= flags
)
233 /* Appearance changed; need to redraw */
235 gtk_widget_queue_clear(icon
->widget
);
237 pinboard_reshape_icon(icon
);
243 /* If path is on an icon then it may have changed... check! */
244 void icons_may_update(guchar
*path
)
248 affected
= g_hash_table_lookup(icons_hash
, path
);
250 for (; affected
; affected
= affected
->next
)
251 icon_may_update((Icon
*) affected
->data
);
254 typedef struct _CheckData CheckData
;
260 static void check_has(gpointer key
, GList
*icons
, CheckData
*check
)
264 g_return_if_fail(icons
!= NULL
);
268 if (is_sub_dir(icon
->path
, check
->path
))
272 /* Set the tooltip AND hide/show the label for panel icons */
273 void icon_set_tip(Icon
*icon
)
279 g_return_if_fail(icon
!= NULL
);
281 widget
= icon
->panel
? icon
->widget
: icon
->win
;
283 if (icon
->panel
&& icon
->label
)
285 if (panel_want_show_text(icon
))
286 gtk_widget_show(icon
->label
);
288 gtk_widget_hide(icon
->label
);
291 if (icon
->panel
&& icon
->socket
)
294 ai
= appinfo_get(icon
->path
, icon
->item
);
296 if (ai
&& ((node
= appinfo_get_section(ai
, "Summary"))))
299 str
= xmlNodeListGetString(node
->doc
,
300 node
->xmlChildrenNode
, 1);
303 gtk_tooltips_set_tip(tooltips
, widget
, str
, NULL
);
307 else if (icon
->panel
&& (!panel_want_show_text(icon
)) && !icon
->socket
)
309 gtk_tooltips_set_tip(tooltips
, widget
,
310 icon
->item
->leafname
, NULL
);
313 gtk_tooltips_set_tip(tooltips
, widget
, NULL
, NULL
);
319 /* Returns TRUE if any icon links to this item (or any item inside
322 gboolean
icons_require(guchar
*path
)
326 /* g_print("[ icons_require(%s)? ]\n", path); */
330 g_hash_table_foreach(icons_hash
, (GHFunc
) check_has
, &check
);
335 /* Callback to update icons of a certain path */
336 static void update_icons(gpointer key
, GList
*icons
, gpointer data
)
338 icons_may_update((guchar
*) key
);
341 /* Check all icons to see if they have been updated */
342 void update_all_icons(void)
344 g_hash_table_foreach(icons_hash
, (GHFunc
) update_icons
, NULL
);
347 /* Show the menu for this panel (NULL for the pinboard).
348 * icon is the icon that was clicked on, if any.
350 void icon_show_menu(GdkEventButton
*event
, Icon
*icon
, Panel
*panel
)
354 pos
[0] = event
->x_root
;
355 pos
[1] = event
->y_root
;
362 /* Shade Remove Item(s) unless there is a selection */
363 menu_set_items_shaded(icon_menu
,
364 (icon_selection
|| menu_icon
) ? FALSE
: TRUE
,
367 menu_show_shift_action(file_shift_item
, icon
? icon
->item
: NULL
,
370 /* Shade the File/Edit/Show items unless an item was clicked */
375 menu_set_items_shaded(icon_menu
, FALSE
, 1, 3);
376 menu_set_items_shaded(icon_file_menu
, FALSE
, 0, 5);
377 if (!can_set_run_action(icon
->item
))
378 menu_set_items_shaded(icon_file_menu
, TRUE
, 3, 1);
380 tmp
= g_strdup_printf("%s '%s'",
381 basetype_name(icon
->item
),
382 icon
->item
->leafname
);
383 gtk_label_set_text(GTK_LABEL(icon_file_item
), tmp
);
386 /* Check for app-specific menu */
387 appmenu_add(icon
->path
, icon
->item
, icon_menu
);
391 menu_set_items_shaded(icon_menu
, TRUE
, 1, 3);
392 menu_set_items_shaded(icon_file_menu
, TRUE
, 0, 5);
393 gtk_label_set_text(GTK_LABEL(icon_file_item
), _("Nothing"));
398 PanelSide side
= panel
->side
;
400 if (side
== PANEL_LEFT
)
402 else if (side
== PANEL_RIGHT
)
405 if (side
== PANEL_TOP
)
407 else if (side
== PANEL_BOTTOM
)
411 gtk_menu_popup(GTK_MENU(icon_menu
), NULL
, NULL
,
412 panel
? panel_position_menu
: position_menu
,
413 (gpointer
) pos
, event
->button
, event
->time
);
416 void icon_set_selected(Icon
*icon
, gboolean selected
)
418 gboolean clear
= FALSE
;
420 g_return_if_fail(icon
!= NULL
);
422 if (icon
->selected
== selected
)
425 /* When selecting an icon on another panel, we need to unselect
426 * everything else afterwards.
428 if (selected
&& icon_selection
)
430 Icon
*current
= (Icon
*) icon_selection
->data
;
432 if (icon
->panel
!= current
->panel
)
436 icon
->selected
= selected
;
438 gtk_widget_queue_clear(icon
->widget
);
442 icon_selection
= g_list_prepend(icon_selection
, icon
);
443 if (losing_selection
== 0 && !icon_selection
->next
)
446 gtk_selection_owner_set(selection_invisible
,
447 GDK_SELECTION_PRIMARY
,
448 gdk_event_get_time(gtk_get_current_event()));
453 icon_selection
= g_list_remove(icon_selection
, icon
);
454 if (losing_selection
== 0 && !icon_selection
)
456 /* Release selection */
457 gtk_selection_owner_set(NULL
,
458 GDK_SELECTION_PRIMARY
,
459 gdk_event_get_time(gtk_get_current_event()));
464 icon_select_only(icon
);
467 /* Clear everything, except 'select', which is selected.
468 * If select is NULL, unselects everything.
470 void icon_select_only(Icon
*select
)
472 GList
*to_clear
, *next
;
475 icon_set_selected(select
, TRUE
);
477 to_clear
= g_list_copy(icon_selection
);
480 to_clear
= g_list_remove(to_clear
, select
);
482 for (next
= to_clear
; next
; next
= next
->next
)
483 icon_set_selected((Icon
*) next
->data
, FALSE
);
485 g_list_free(to_clear
);
488 /* XXX: Under Gtk+ 2.0, can this get called twice? */
489 void icon_destroyed(Icon
*icon
)
491 g_return_if_fail(icon
!= NULL
);
493 if (icon
== menu_icon
)
499 g_object_unref(G_OBJECT(icon
->layout
));
504 icon_unhash_path(icon
);
506 pinboard_wink_item(NULL
, FALSE
);
508 if (g_list_find(icon_selection
, icon
))
510 icon_selection
= g_list_remove(icon_selection
, icon
);
513 gtk_selection_owner_set(NULL
,
514 GDK_SELECTION_PRIMARY
,
515 gdk_event_get_time(gtk_get_current_event()));
518 if (icon
->panel
== NULL
)
520 gdk_pixmap_unref(icon
->mask
);
521 if (current_pinboard
)
522 current_pinboard
->icons
=
523 g_list_remove(current_pinboard
->icons
, icon
);
526 diritem_free(icon
->item
);
528 g_free(icon
->src_path
);
532 static void set_tip(gpointer key
, GList
*icons
, gpointer data
)
534 for (; icons
; icons
= icons
->next
)
536 Icon
*icon
= (Icon
*) icons
->data
;
542 void icons_update_tip(void)
544 g_hash_table_foreach(icons_hash
, (GHFunc
) set_tip
, NULL
);
547 /****************************************************************
548 * INTERNAL FUNCTIONS *
549 ****************************************************************/
551 static void rename_activate(GtkWidget
*dialog
)
553 GtkWidget
*entry
, *src
;
556 guchar
*new_name
, *new_src
;
558 entry
= gtk_object_get_data(GTK_OBJECT(dialog
), "new_name");
559 icon
= gtk_object_get_data(GTK_OBJECT(dialog
), "callback_icon");
560 callback
= gtk_object_get_data(GTK_OBJECT(dialog
), "callback_fn");
561 src
= gtk_object_get_data(GTK_OBJECT(dialog
), "new_path");
563 g_return_if_fail(callback
!= NULL
&&
568 new_name
= gtk_entry_get_text(GTK_ENTRY(entry
));
569 new_src
= gtk_entry_get_text(GTK_ENTRY(src
));
571 if (*new_name
== '\0')
573 _("The label must contain at least one character!"));
574 else if (*new_src
== '\0')
576 _("The location must contain at least one character!"));
580 GdkFont
*font
= icon
->widget
->style
->font
;
583 g_free(icon
->item
->leafname
);
584 g_free(icon
->src_path
);
586 icon
->src_path
= g_strdup(new_src
);
588 icon_unhash_path(icon
);
590 icon
->path
= icon_convert_path(new_src
);
591 icon_hash_path(icon
);
593 icon
->item
->leafname
= g_strdup(new_name
);
595 icon
->name_width
= gdk_string_measure(font
, new_name
);
597 /* XXX: Set name_width in size_and_shape? */
599 diritem_restat(icon
->path
, icon
->item
);
602 gtk_widget_destroy(dialog
);
606 static void menu_closed(GtkWidget
*widget
)
612 static void panel_position_menu(GtkMenu
*menu
, gint
*x
, gint
*y
,
618 int *pos
= (int *) data
;
619 GtkRequisition requisition
;
621 gtk_widget_size_request(GTK_WIDGET(menu
), &requisition
);
624 *x
= screen_width
- MENU_MARGIN
- requisition
.width
;
625 else if (pos
[0] == -2)
628 *x
= pos
[0] - (requisition
.width
>> 2);
631 *y
= screen_height
- MENU_MARGIN
- requisition
.height
;
632 else if (pos
[1] == -2)
635 *y
= pos
[1] - (requisition
.height
>> 2);
637 *x
= CLAMP(*x
, 0, screen_width
- requisition
.width
);
638 *y
= CLAMP(*y
, 0, screen_height
- requisition
.height
);
645 /* Called when another application takes the selection away from us */
646 static gint
lose_selection(GtkWidget
*widget
, GdkEventSelection
*event
)
648 /* Don't send any events */
651 icon_select_only(NULL
);
657 /* Called when another application wants the contents of our selection */
658 static void selection_get(GtkWidget
*widget
,
659 GtkSelectionData
*selection_data
,
666 guchar
*leader
= NULL
;
668 str
= g_string_new(NULL
);
670 if (info
== TARGET_URI_LIST
)
671 leader
= g_strdup_printf("file://%s", our_host_name_for_dnd());
673 for (next
= icon_selection
; next
; next
= next
->next
)
675 Icon
*icon
= (Icon
*) next
->data
;
678 g_string_append(str
, leader
);
679 g_string_append(str
, icon
->path
);
680 g_string_append_c(str
, ' ');
685 gtk_selection_data_set(selection_data
,
686 gdk_atom_intern("STRING", FALSE
),
689 str
->len
? str
->len
- 1 : 0);
691 g_string_free(str
, TRUE
);
694 static void rename_cb(Icon
*icon
)
698 gtk_widget_queue_clear(icon
->widget
);
699 panel_icon_renamed(icon
);
700 panel_save(icon
->panel
);
704 pinboard_reshape_icon(icon
);
709 static void remove_items(gpointer data
, guint action
, GtkWidget
*widget
)
715 icon_set_selected(menu_icon
, TRUE
);
717 next
= icon_selection
;
722 _("You must first select some items to remove"));
726 panel
= ((Icon
*) next
->data
)->panel
;
732 Icon
*icon
= (Icon
*) next
->data
;
736 gtk_widget_destroy(icon
->widget
);
745 Icon
*icon
= (Icon
*) next
->data
;
749 gtk_widget_destroy(icon
->win
);
756 static void file_op(gpointer data
, guint action
, GtkWidget
*widget
)
760 delayed_error(_("You must open the menu over an item"));
767 run_diritem(menu_icon
->path
, menu_icon
->item
,
771 show_rename_box(menu_icon
->widget
,
772 menu_icon
, rename_cb
);
774 case ACTION_LOCATION
:
775 open_to_show(menu_icon
->path
);
778 show_item_help(menu_icon
->path
, menu_icon
->item
);
781 infobox_new(menu_icon
->path
);
783 case ACTION_RUN_ACTION
:
784 if (can_set_run_action(menu_icon
->item
))
785 type_set_handler_dialog(
786 menu_icon
->item
->mime_type
);
789 _("You can only set the run action for a "
792 case ACTION_SET_ICON
:
793 icon_set_handler_dialog(menu_icon
->item
,
799 /* Opens a box allowing the user to change the name of a pinned icon.
800 * If 'widget' is destroyed then the box will close.
801 * If the user chooses OK then the callback is called once the icon's
802 * name, src_path and path fields have been updated and the item field
805 static void show_rename_box(GtkWidget
*widget
, Icon
*icon
, RenameFn callback
)
807 GtkWidget
*dialog
, *hbox
, *vbox
, *label
, *entry
, *button
;
809 dialog
= gtk_window_new(GTK_WINDOW_DIALOG
);
811 gtk_window_set_type_hint(GTK_WINDOW(dialog
),
812 GDK_WINDOW_TYPE_HINT_DIALOG
);
814 gtk_window_set_title(GTK_WINDOW(dialog
), _("Edit Item"));
815 gtk_container_set_border_width(GTK_CONTAINER(dialog
), 10);
817 vbox
= gtk_vbox_new(FALSE
, 4);
818 gtk_container_add(GTK_CONTAINER(dialog
), vbox
);
820 label
= gtk_label_new(_("Clicking the icon opens:"));
821 gtk_box_pack_start(GTK_BOX(vbox
), label
, TRUE
, TRUE
, 0);
823 entry
= gtk_entry_new();
824 gtk_box_pack_start(GTK_BOX(vbox
), entry
, TRUE
, FALSE
, 2);
825 gtk_entry_set_text(GTK_ENTRY(entry
), icon
->src_path
);
826 gtk_object_set_data(GTK_OBJECT(dialog
), "new_path", entry
);
827 gtk_signal_connect_object(GTK_OBJECT(entry
), "activate",
828 GTK_SIGNAL_FUNC(rename_activate
), GTK_OBJECT(dialog
));
830 gtk_box_pack_start(GTK_BOX(vbox
), gtk_hseparator_new(), TRUE
, TRUE
, 2);
832 label
= gtk_label_new(_("The text displayed under the icon is:"));
833 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
->item
->leafname
);
837 gtk_editable_select_region(GTK_EDITABLE(entry
), 0, -1);
838 gtk_widget_grab_focus(entry
);
839 gtk_object_set_data(GTK_OBJECT(dialog
), "new_name", entry
);
840 gtk_signal_connect_object(GTK_OBJECT(entry
), "activate",
841 GTK_SIGNAL_FUNC(rename_activate
), GTK_OBJECT(dialog
));
843 gtk_signal_connect_object_while_alive(GTK_OBJECT(widget
),
845 GTK_SIGNAL_FUNC(gtk_widget_destroy
),
848 gtk_object_set_data(GTK_OBJECT(dialog
), "callback_icon", icon
);
849 gtk_object_set_data(GTK_OBJECT(dialog
), "callback_fn", callback
);
851 gtk_box_pack_start(GTK_BOX(vbox
), gtk_hseparator_new(), TRUE
, TRUE
, 2);
853 hbox
= gtk_hbox_new(TRUE
, 4);
854 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, TRUE
, 0);
856 button
= gtk_button_new_with_label(_("OK"));
857 gtk_box_pack_start(GTK_BOX(hbox
), button
, TRUE
, TRUE
, 0);
858 GTK_WIDGET_SET_FLAGS(button
, GTK_CAN_DEFAULT
);
859 gtk_window_set_default(GTK_WINDOW(dialog
), button
);
860 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
861 GTK_SIGNAL_FUNC(rename_activate
), GTK_OBJECT(dialog
));
863 button
= gtk_button_new_with_label(_("Cancel"));
864 GTK_WIDGET_SET_FLAGS(button
, GTK_CAN_DEFAULT
);
865 gtk_box_pack_start(GTK_BOX(hbox
), button
, TRUE
, TRUE
, 0);
866 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
867 GTK_SIGNAL_FUNC(gtk_widget_destroy
),
870 gtk_widget_show_all(dialog
);