4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2001, 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 /* panel.c - code for dealing with panel windows */
31 #include <gtk/gtkinvisible.h>
40 #include "gui_support.h"
54 static Panel
*current_panel
[PANEL_NUMBER_OF_SIDES
];
56 /* Widget which holds the selection when we have it */
57 static GtkWidget
*selection_invisible
= NULL
;
58 static guint losing_selection
= 0; /* > 0 => Don't send events */
63 GtkAdjustment
*adj
; /* Scroll position of the bar */
65 guchar
*name
; /* Leaf name */
67 GtkWidget
*before
; /* Icons at the left/top end */
68 GtkWidget
*after
; /* Icons at the right/bottom end */
70 GtkWidget
*gap
; /* Event box between sides */
73 /* NULL => Not loading a panel */
74 static Panel
*loading_panel
= NULL
;
76 /* Static prototypes */
77 static int panel_delete(GtkWidget
*widget
, GdkEvent
*event
, Panel
*panel
);
78 static void panel_destroyed(GtkWidget
*widget
, Panel
*panel
);
79 static char *pan_from_file(guchar
*line
);
80 static void icon_destroyed(Icon
*icon
);
81 static gint
icon_button_release(GtkWidget
*widget
,
82 GdkEventButton
*event
,
84 static gint
icon_button_press(GtkWidget
*widget
,
85 GdkEventButton
*event
,
87 static void reposition_panel(Panel
*panel
);
88 static void icon_set_selected(Icon
*icon
, gboolean selected
);
89 static gint
expose_icon(GtkWidget
*widget
,
90 GdkEventExpose
*event
,
92 static gint
draw_icon(GtkWidget
*widget
,
93 GdkRectangle
*badarea
,
95 static void popup_panel_menu(GdkEventButton
*event
,
98 static gint
panel_button_release(GtkWidget
*widget
,
99 GdkEventButton
*event
,
101 static gint
panel_button_press(GtkWidget
*widget
,
102 GdkEventButton
*event
,
104 static void size_icon(Icon
*icon
);
105 static void drag_set_panel_dest(Icon
*icon
);
106 static void add_uri_list(GtkWidget
*widget
,
107 GdkDragContext
*context
,
110 GtkSelectionData
*selection_data
,
114 static void panel_add_item(Panel
*panel
,
118 static void menu_closed(GtkWidget
*widget
);
119 static void remove_items(gpointer data
, guint action
, GtkWidget
*widget
);
120 static void edit_icon(gpointer data
, guint action
, GtkWidget
*widget
);
121 static void show_location(gpointer data
, guint action
, GtkWidget
*widget
);
122 static void show_help(gpointer data
, guint action
, GtkWidget
*widget
);
123 static gboolean
drag_motion(GtkWidget
*widget
,
124 GdkDragContext
*context
,
129 static void drag_leave(GtkWidget
*widget
,
130 GdkDragContext
*context
,
133 static void panel_save(Panel
*panel
);
134 static GList
*get_widget_list(Panel
*panel
);
135 static GtkWidget
*make_insert_frame(Panel
*panel
);
136 static gboolean
enter_icon(GtkWidget
*widget
,
137 GdkEventCrossing
*event
,
139 static gint
lose_selection(GtkWidget
*widget
, GdkEventSelection
*event
);
140 static void selection_get(GtkWidget
*widget
,
141 GtkSelectionData
*selection_data
,
145 static gint
icon_motion_event(GtkWidget
*widget
,
146 GdkEventMotion
*event
,
148 static gint
panel_motion_event(GtkWidget
*widget
,
149 GdkEventMotion
*event
,
151 static void reposition_icon(Icon
*icon
, int index
);
152 static void start_drag(Icon
*icon
, GdkEventMotion
*event
);
153 static guchar
*create_uri_list(GList
*list
);
154 static void drag_end(GtkWidget
*widget
,
155 GdkDragContext
*context
,
157 static void perform_action(Panel
*panel
,
159 GdkEventButton
*event
);
162 static GtkItemFactoryEntry menu_def
[] = {
163 {N_("ROX-Filer Help"), NULL
, menu_rox_help
, 0, NULL
},
164 {N_("ROX-Filer Options..."), NULL
, menu_show_options
, 0, NULL
},
165 {N_("Open Home Directory"), NULL
, open_home
, 0, NULL
},
166 {"", NULL
, NULL
, 0, "<Separator>"},
167 {N_("Edit Icon"), NULL
, edit_icon
, 0, NULL
},
168 {N_("Show Location"), NULL
, show_location
, 0, NULL
},
169 {N_("Show Help"), NULL
, show_help
, 0, NULL
},
170 {N_("Remove Item(s)"), NULL
, remove_items
, 0, NULL
},
173 static GtkWidget
*panel_menu
= NULL
;
174 static gboolean tmp_icon_selected
= FALSE
;
176 /* A list of selected Icons */
177 static GList
*panel_selection
= NULL
;
179 static GtkWidget
*dnd_highlight
= NULL
; /* (stops flickering) */
181 /* When sliding the panel, records where the panel was before */
182 static gint slide_from_value
= 0;
185 /****************************************************************
186 * EXTERNAL INTERFACE *
187 ****************************************************************/
189 void panel_init(void)
191 GtkTargetEntry target_table
[] =
193 {"text/uri-list", 0, TARGET_URI_LIST
},
194 {"STRING", 0, TARGET_STRING
},
197 panel_menu
= menu_create(menu_def
,
198 sizeof(menu_def
) / sizeof(*menu_def
),
200 gtk_signal_connect(GTK_OBJECT(panel_menu
), "unmap_event",
201 GTK_SIGNAL_FUNC(menu_closed
), NULL
);
203 selection_invisible
= gtk_invisible_new();
205 gtk_signal_connect(GTK_OBJECT(selection_invisible
),
206 "selection_clear_event",
207 GTK_SIGNAL_FUNC(lose_selection
),
210 gtk_signal_connect(GTK_OBJECT(selection_invisible
),
212 GTK_SIGNAL_FUNC(selection_get
), NULL
);
214 gtk_selection_add_targets(selection_invisible
,
215 GDK_SELECTION_PRIMARY
,
217 sizeof(target_table
) / sizeof(*target_table
));
220 /* 'name' may be NULL or "" to remove the panel */
221 Panel
*panel_new(guchar
*name
, PanelSide side
)
225 GtkWidget
*vp
, *box
, *frame
;
227 g_return_val_if_fail(side
>= 0 && side
< PANEL_NUMBER_OF_SIDES
, NULL
);
228 g_return_val_if_fail(loading_panel
== NULL
, NULL
);
230 if (name
&& *name
== '\0')
233 if (current_panel
[side
])
237 gtk_widget_destroy(current_panel
[side
]->window
);
242 if (name
== NULL
|| *name
== '\0')
245 panel
= g_new(Panel
, 1);
246 panel
->name
= g_strdup(name
);
249 panel
->window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
250 gtk_window_set_wmclass(GTK_WINDOW(panel
->window
), "ROX-Panel", PROJECT
);
251 gtk_widget_set_events(panel
->window
,
252 GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
|
253 GDK_BUTTON1_MOTION_MASK
| GDK_BUTTON2_MOTION_MASK
|
254 GDK_BUTTON2_MOTION_MASK
);
256 gtk_signal_connect(GTK_OBJECT(panel
->window
), "delete-event",
257 GTK_SIGNAL_FUNC(panel_delete
), panel
);
258 gtk_signal_connect(GTK_OBJECT(panel
->window
), "destroy",
259 GTK_SIGNAL_FUNC(panel_destroyed
), panel
);
260 gtk_signal_connect(GTK_OBJECT(panel
->window
), "button_press_event",
261 GTK_SIGNAL_FUNC(panel_button_press
), panel
);
262 gtk_signal_connect(GTK_OBJECT(panel
->window
), "button_release_event",
263 GTK_SIGNAL_FUNC(panel_button_release
), panel
);
264 gtk_signal_connect(GTK_OBJECT(panel
->window
), "motion-notify-event",
265 GTK_SIGNAL_FUNC(panel_motion_event
), panel
);
267 if (strchr(name
, '/'))
268 load_path
= g_strdup(name
);
273 leaf
= g_strconcat("pan_", name
, NULL
);
274 load_path
= choices_find_path_load(leaf
, "ROX-Filer");
278 vp
= gtk_viewport_new(NULL
, NULL
);
279 gtk_viewport_set_shadow_type(GTK_VIEWPORT(vp
), GTK_SHADOW_OUT
);
280 gtk_container_add(GTK_CONTAINER(panel
->window
), vp
);
282 if (side
== PANEL_TOP
|| side
== PANEL_BOTTOM
)
284 panel
->adj
= gtk_viewport_get_hadjustment(GTK_VIEWPORT(vp
));
285 box
= gtk_hbox_new(FALSE
, 0);
286 panel
->before
= gtk_hbox_new(FALSE
, 0);
287 panel
->after
= gtk_hbox_new(FALSE
, 0);
291 panel
->adj
= gtk_viewport_get_vadjustment(GTK_VIEWPORT(vp
));
292 box
= gtk_vbox_new(FALSE
, 0);
293 panel
->before
= gtk_vbox_new(FALSE
, 0);
294 panel
->after
= gtk_vbox_new(FALSE
, 0);
297 gtk_container_add(GTK_CONTAINER(vp
), box
);
298 gtk_box_pack_start(GTK_BOX(box
), panel
->before
, FALSE
, TRUE
, 0);
299 gtk_box_pack_end(GTK_BOX(box
), panel
->after
, FALSE
, TRUE
, 0);
301 frame
= make_insert_frame(panel
);
302 gtk_box_pack_start(GTK_BOX(box
), frame
, TRUE
, TRUE
, 4);
304 /* This is used so that we can find the middle easily! */
305 panel
->gap
= gtk_event_box_new();
306 gtk_box_pack_start(GTK_BOX(box
), panel
->gap
, FALSE
, FALSE
, 0);
308 frame
= make_insert_frame(panel
);
309 gtk_object_set_data(GTK_OBJECT(frame
), "after", "yes");
310 gtk_box_pack_start(GTK_BOX(box
), frame
, TRUE
, TRUE
, 4);
312 gtk_widget_realize(panel
->window
);
313 make_panel_window(panel
->window
->window
);
315 loading_panel
= panel
;
316 if (load_path
&& access(load_path
, F_OK
) == 0)
317 parse_file(load_path
, pan_from_file
);
320 /* Don't scare users with an empty panel... */
323 panel_add_item(panel
, "~", "Home", FALSE
);
325 apps
= pathdup(make_path(app_dir
, "..")->str
);
328 panel_add_item(panel
, apps
, "Apps", FALSE
);
332 loading_panel
= NULL
;
335 current_panel
[side
] = panel
;
337 reposition_panel(panel
);
340 gtk_widget_show_all(panel
->window
);
345 /* See if the file the icon points to has changed. Update the icon
348 void panel_icon_may_update(Icon
*icon
)
350 MaskedPixmap
*image
= icon
->item
.image
;
351 int flags
= icon
->item
.flags
;
355 dir_restat(icon
->path
, &icon
->item
, FALSE
);
357 if (icon
->item
.image
!= image
|| icon
->item
.flags
!= flags
)
360 gtk_widget_queue_clear(icon
->widget
);
361 reposition_panel(icon
->panel
);
369 /****************************************************************
370 * INTERNAL FUNCTIONS *
371 ****************************************************************/
373 /* User has tried to close the panel via the window manager - confirm */
374 static int panel_delete(GtkWidget
*widget
, GdkEvent
*event
, Panel
*panel
)
376 /* TODO: We can open lots of these - very irritating! */
377 return get_choice(_("Close panel?"),
378 _("You have tried to close a panel via the window "
379 "manager - I usually find that this is accidental... "
381 2, _("Remove"), _("Cancel")) != 0;
384 static void panel_destroyed(GtkWidget
*widget
, Panel
*panel
)
386 if (current_panel
[panel
->side
] == panel
)
387 current_panel
[panel
->side
] = NULL
;
389 if (panel
->side
== PANEL_TOP
|| panel
->side
== PANEL_BOTTOM
)
391 if (current_panel
[PANEL_RIGHT
])
392 reposition_panel(current_panel
[PANEL_RIGHT
]);
393 if (current_panel
[PANEL_LEFT
])
394 reposition_panel(current_panel
[PANEL_LEFT
]);
400 if (--number_of_windows
< 1)
404 /* Called for each line in the config file while loading a new panel */
405 static char *pan_from_file(guchar
*line
)
409 g_return_val_if_fail(line
!= NULL
, NULL
);
410 g_return_val_if_fail(loading_panel
!= NULL
, NULL
);
415 sep
= strpbrk(line
, "<>");
417 return _("Missing < or > in panel config file");
420 leaf
= g_strndup(line
, sep
- line
);
424 panel_add_item(loading_panel
,
434 /* Add an icon with this path to the panel. If after is TRUE then the
435 * icon is added to the right/bottom end of the panel.
437 * If name is NULL a suitable name is taken from path.
439 static void panel_add_item(Panel
*panel
,
448 widget
= gtk_event_box_new();
449 gtk_widget_set_events(widget
,
450 GDK_BUTTON1_MOTION_MASK
| GDK_BUTTON2_MOTION_MASK
|
451 GDK_BUTTON3_MOTION_MASK
|
452 GDK_EXPOSURE_MASK
| GDK_BUTTON_PRESS_MASK
|
453 GDK_BUTTON_RELEASE_MASK
);
455 gtk_box_pack_start(GTK_BOX(after
? panel
->after
: panel
->before
),
456 widget
, FALSE
, TRUE
, 4);
458 gtk_box_reorder_child(GTK_BOX(panel
->after
), widget
, 0);
460 gtk_widget_realize(widget
);
461 font
= widget
->style
->font
;
463 icon
= g_new(Icon
, 1);
464 icon
->type
= ICON_PANEL
;
466 icon
->src_path
= g_strdup(path
);
467 icon
->path
= icon_convert_path(path
);
469 gtk_object_set_data(GTK_OBJECT(widget
), "icon", icon
);
471 icon_hash_path(icon
);
473 icon
->widget
= widget
;
474 icon
->selected
= FALSE
;
475 dir_stat(icon
->path
, &icon
->item
, FALSE
);
478 icon
->item
.leafname
= g_strdup(name
);
483 slash
= strrchr(icon
->path
, '/');
484 icon
->item
.leafname
= g_strdup(slash
&& slash
[1] ? slash
+ 1
488 icon
->item
.name_width
= gdk_string_width(font
, icon
->item
.leafname
);
490 gtk_signal_connect_after(GTK_OBJECT(widget
), "enter-notify-event",
491 GTK_SIGNAL_FUNC(enter_icon
), icon
);
492 gtk_signal_connect(GTK_OBJECT(icon
->widget
), "motion-notify-event",
493 GTK_SIGNAL_FUNC(icon_motion_event
), icon
);
494 gtk_signal_connect_after(GTK_OBJECT(widget
), "draw",
495 GTK_SIGNAL_FUNC(draw_icon
), icon
);
496 gtk_signal_connect_after(GTK_OBJECT(widget
), "expose_event",
497 GTK_SIGNAL_FUNC(expose_icon
), icon
);
498 gtk_signal_connect(GTK_OBJECT(widget
), "button_release_event",
499 GTK_SIGNAL_FUNC(icon_button_release
), icon
);
500 gtk_signal_connect(GTK_OBJECT(widget
), "button_press_event",
501 GTK_SIGNAL_FUNC(icon_button_press
), icon
);
502 gtk_signal_connect(GTK_OBJECT(widget
), "drag_data_get",
503 drag_data_get
, NULL
);
505 gtk_signal_connect_object(GTK_OBJECT(widget
), "destroy",
506 GTK_SIGNAL_FUNC(icon_destroyed
), (gpointer
) icon
);
508 drag_set_panel_dest(icon
);
514 reposition_panel(panel
);
518 gtk_widget_show(widget
);
521 /* Set the size of the widget for this icon.
522 * You should call reposition_panel() sometime after doing this...
524 static void size_icon(Icon
*icon
)
526 int im_height
= MIN(icon
->item
.image
->height
, MAX_ICON_HEIGHT
- 4);
527 GdkFont
*font
= icon
->widget
->style
->font
;
530 width
= MAX(icon
->item
.image
->width
, icon
->item
.name_width
) + 4;
531 height
= font
->ascent
+ font
->descent
+ 2 + im_height
;
533 gtk_widget_set_usize(icon
->widget
, width
, height
);
536 static gint
expose_icon(GtkWidget
*widget
,
537 GdkEventExpose
*event
,
540 return draw_icon(widget
, &event
->area
, icon
);
543 static gint
draw_icon(GtkWidget
*widget
, GdkRectangle
*badarea
, Icon
*icon
)
545 GdkFont
*font
= widget
->style
->font
;
550 gdk_window_get_size(widget
->window
, &width
, &height
);
555 area
.height
= height
- font
->ascent
- font
->descent
- 2;
557 draw_large_icon(widget
, &area
, &icon
->item
, icon
->selected
);
559 text_x
= (area
.width
- icon
->item
.name_width
) >> 1;
561 text_y
= height
- font
->descent
;
562 if (icon
->panel
->side
== PANEL_TOP
|| icon
->panel
->side
== PANEL_BOTTOM
)
570 icon
->item
.name_width
,
572 icon
->selected
, TRUE
);
577 static void icon_destroyed(Icon
*icon
)
579 icon_unhash_path(icon
);
581 if (g_list_find(panel_selection
, icon
))
583 panel_selection
= g_list_remove(panel_selection
, icon
);
585 if (!panel_selection
)
586 gtk_selection_owner_set(NULL
,
587 GDK_SELECTION_PRIMARY
,
588 gdk_event_get_time(gtk_get_current_event()));
591 dir_item_clear(&icon
->item
);
593 g_free(icon
->src_path
);
597 /* Clear everything, except 'select', which is selected.
598 * If select is NULL, unselects everything.
600 static void panel_clear_selection(Icon
*select
)
602 GList
*to_clear
, *next
;
605 icon_set_selected(select
, TRUE
);
607 to_clear
= g_list_copy(panel_selection
), select
;
610 to_clear
= g_list_remove(g_list_copy(panel_selection
), select
);
612 for (next
= to_clear
; next
; next
= next
->next
)
613 icon_set_selected((Icon
*) next
->data
, FALSE
);
615 g_list_free(to_clear
);
618 static void icon_set_selected(Icon
*icon
, gboolean selected
)
620 gboolean clear
= FALSE
;
622 g_return_if_fail(icon
!= NULL
);
624 if (icon
->selected
== selected
)
627 /* When selecting an icon on another panel, we need to unselect
628 * everything else afterwards.
630 if (selected
&& panel_selection
)
632 Icon
*current
= (Icon
*) panel_selection
->data
;
634 if (icon
->panel
!= current
->panel
)
638 icon
->selected
= selected
;
639 gtk_widget_queue_clear(icon
->widget
);
643 panel_selection
= g_list_prepend(panel_selection
, icon
);
644 if (losing_selection
== 0 && !panel_selection
->next
)
647 gtk_selection_owner_set(selection_invisible
,
648 GDK_SELECTION_PRIMARY
,
649 gdk_event_get_time(gtk_get_current_event()));
654 panel_selection
= g_list_remove(panel_selection
, icon
);
655 if (losing_selection
== 0 && !panel_selection
)
657 /* Release selection */
658 gtk_selection_owner_set(NULL
,
659 GDK_SELECTION_PRIMARY
,
660 gdk_event_get_time(gtk_get_current_event()));
665 panel_clear_selection(icon
);
668 /* icon may be NULL if the event is on the background */
669 static void perform_action(Panel
*panel
, Icon
*icon
, GdkEventButton
*event
)
671 GtkWidget
*widget
= icon
? icon
->widget
: NULL
;
674 action
= bind_lookup_bev(icon
? BIND_PANEL_ICON
: BIND_PANEL
, event
);
680 wink_widget(icon
->widget
);
681 run_diritem(icon
->path
, &icon
->item
, NULL
, FALSE
);
685 wink_widget(icon
->widget
);
686 run_diritem(icon
->path
, &icon
->item
, NULL
, TRUE
);
690 popup_panel_menu(event
, panel
, icon
);
693 dnd_motion_start(MOTION_REPOSITION
);
695 case ACT_PRIME_AND_SELECT
:
697 panel_clear_selection(icon
);
698 dnd_motion_start(MOTION_READY_FOR_DND
);
700 case ACT_PRIME_AND_TOGGLE
:
701 icon_set_selected(icon
, !icon
->selected
);
702 dnd_motion_start(MOTION_READY_FOR_DND
);
704 case ACT_PRIME_FOR_DND
:
706 dnd_motion_start(MOTION_READY_FOR_DND
);
708 case ACT_TOGGLE_SELECTED
:
709 icon_set_selected(icon
, !icon
->selected
);
711 case ACT_SELECT_EXCL
:
712 icon_set_selected(icon
, TRUE
);
714 case ACT_SLIDE_CLEAR_PANEL
:
715 panel_clear_selection(NULL
);
717 case ACT_SLIDE_PANEL
:
718 dnd_motion_grab_pointer();
719 slide_from_value
= panel
->adj
->value
;
720 dnd_motion_start(MOTION_REPOSITION
);
724 case ACT_CLEAR_SELECTION
:
725 panel_clear_selection(NULL
);
728 g_warning("Unsupported action : %d\n", action
);
733 static gint
panel_button_release(GtkWidget
*widget
,
734 GdkEventButton
*event
,
737 if (dnd_motion_release(event
))
740 perform_action(panel
, NULL
, event
);
745 static gint
panel_button_press(GtkWidget
*widget
,
746 GdkEventButton
*event
,
749 if (dnd_motion_press(panel
->window
, event
))
750 perform_action(panel
, NULL
, event
);
755 static gint
icon_button_release(GtkWidget
*widget
,
756 GdkEventButton
*event
,
759 if (dnd_motion_release(event
))
762 perform_action(icon
->panel
, icon
, event
);
767 static gint
icon_button_press(GtkWidget
*widget
,
768 GdkEventButton
*event
,
771 if (dnd_motion_press(widget
, event
))
772 perform_action(icon
->panel
, icon
, event
);
777 /* Difference between height of an icon and height of panel (or width) */
780 static void reposition_panel(Panel
*panel
)
784 GList
*next
, *children
;
785 PanelSide side
= panel
->side
;
787 children
= get_widget_list(panel
);
789 for (next
= children
; next
; next
= next
->next
)
791 GtkWidget
*widget
= (GtkWidget
*) next
->data
;
794 gtk_widget_get_child_requisition(widget
, &req
);
802 g_list_free(children
);
804 if (side
== PANEL_TOP
|| side
== PANEL_BOTTOM
)
812 if (side
== PANEL_RIGHT
)
813 x
= screen_width
- w
;
817 if (current_panel
[PANEL_TOP
])
821 ph
= current_panel
[PANEL_TOP
]->height
;
826 if (current_panel
[PANEL_BOTTOM
])
827 h
-= current_panel
[PANEL_BOTTOM
]->height
;
830 if (side
== PANEL_BOTTOM
)
831 y
= screen_height
- h
;
833 gtk_widget_set_uposition(panel
->window
, x
, y
);
835 gtk_widget_set_usize(panel
->window
, w
, h
);
836 gdk_window_move_resize(panel
->window
->window
, x
, y
, w
, h
);
838 if (side
== PANEL_BOTTOM
|| side
== PANEL_TOP
)
840 if (current_panel
[PANEL_RIGHT
])
841 reposition_panel(current_panel
[PANEL_RIGHT
]);
842 if (current_panel
[PANEL_LEFT
])
843 reposition_panel(current_panel
[PANEL_LEFT
]);
847 static void panel_position_menu(GtkMenu
*menu
, gint
*x
, gint
*y
, gpointer data
)
849 int *pos
= (int *) data
;
850 GtkRequisition requisition
;
852 gtk_widget_size_request(GTK_WIDGET(menu
), &requisition
);
855 *x
= screen_width
- MENU_MARGIN
- requisition
.width
;
856 else if (pos
[0] == -2)
859 *x
= pos
[0] - (requisition
.width
>> 2);
862 *y
= screen_height
- MENU_MARGIN
- requisition
.height
;
863 else if (pos
[1] == -2)
866 *y
= pos
[1] - (requisition
.height
>> 2);
868 *x
= CLAMP(*x
, 0, screen_width
- requisition
.width
);
869 *y
= CLAMP(*y
, 0, screen_height
- requisition
.height
);
873 static void popup_panel_menu(GdkEventButton
*event
,
878 PanelSide side
= panel
->side
;
884 Icon
*current
= panel_selection
885 ? ((Icon
*) panel_selection
->data
)
888 if (current
&& current
->panel
!= panel
)
889 panel_clear_selection(icon
);
891 if (panel_selection
== NULL
)
893 icon_set_selected(icon
, TRUE
);
895 /* Unselect when panel closes */
896 tmp_icon_selected
= TRUE
;
900 if (side
== PANEL_LEFT
)
902 else if (side
== PANEL_RIGHT
)
905 pos
[0] = event
->x_root
;
907 if (side
== PANEL_TOP
)
909 else if (side
== PANEL_BOTTOM
)
912 pos
[1] = event
->y_root
;
914 /* Shade Remove Item(s) unless there is a selection */
915 menu_set_items_shaded(panel_menu
,
916 panel_selection
? FALSE
: TRUE
,
919 /* Shade the Rename/Location/Help items unless exactly one item is
922 if (panel_selection
== NULL
|| panel_selection
->next
)
923 menu_set_items_shaded(panel_menu
, TRUE
, 4, 3);
926 Icon
*icon
= (Icon
*) panel_selection
->data
;
928 menu_set_items_shaded(panel_menu
, FALSE
, 4, 3);
930 /* Check for app-specific menu */
931 appmenu_add(icon
->path
, &icon
->item
, panel_menu
);
934 gtk_menu_popup(GTK_MENU(panel_menu
), NULL
, NULL
, panel_position_menu
,
935 (gpointer
) pos
, event
->button
, event
->time
);
938 static void rename_cb(Icon
*icon
)
943 gtk_widget_queue_clear(icon
->widget
);
944 reposition_panel(icon
->panel
);
946 panel_save(icon
->panel
);
949 static void edit_icon(gpointer data
, guint action
, GtkWidget
*widget
)
953 if (panel_selection
== NULL
|| panel_selection
->next
)
955 delayed_error(PROJECT
,
956 _("First, select a single item to edit"));
960 icon
= (Icon
*) panel_selection
->data
;
961 show_rename_box(icon
->widget
, icon
, rename_cb
);
964 static void show_location(gpointer data
, guint action
, GtkWidget
*widget
)
968 if (panel_selection
== NULL
|| panel_selection
->next
)
970 delayed_error(PROJECT
,
971 _("Select a single item, then use this to find out "
972 "where it is in the filesystem."));
976 icon
= (Icon
*) panel_selection
->data
;
978 open_to_show(icon
->path
);
981 static void show_help(gpointer data
, guint action
, GtkWidget
*widget
)
985 if (panel_selection
== NULL
|| panel_selection
->next
)
987 delayed_error(PROJECT
,
988 _("You must select a single item to get help on"));
992 icon
= (Icon
*) panel_selection
->data
;
993 show_item_help(icon
->path
, &icon
->item
);
996 static void remove_items(gpointer data
, guint action
, GtkWidget
*widget
)
999 GList
*next
= panel_selection
;
1003 delayed_error(PROJECT
,
1004 _("You must first select some icons to remove"));
1008 panel
= ((Icon
*) next
->data
)->panel
;
1012 Icon
*icon
= (Icon
*) next
->data
;
1016 gtk_widget_destroy(icon
->widget
);
1021 reposition_panel(panel
);
1024 /* Same as drag_set_dest(), but for panel icons */
1025 static void drag_set_panel_dest(Icon
*icon
)
1027 GtkObject
*obj
= GTK_OBJECT(icon
->widget
);
1029 make_drop_target(icon
->widget
, 0);
1031 gtk_signal_connect(obj
, "drag_motion",
1032 GTK_SIGNAL_FUNC(drag_motion
), icon
);
1033 gtk_signal_connect(obj
, "drag_leave",
1034 GTK_SIGNAL_FUNC(drag_leave
), icon
);
1035 gtk_signal_connect(obj
, "drag_end",
1036 GTK_SIGNAL_FUNC(drag_end
), icon
);
1039 static gboolean
drag_motion(GtkWidget
*widget
,
1040 GdkDragContext
*context
,
1046 GdkDragAction action
= context
->suggested_action
;
1048 DirItem
*item
= &icon
->item
;
1051 goto out
; /* Can't drag a selection to itself */
1053 type
= dnd_motion_item(context
, &item
);
1058 /* We actually must pretend to accept the drop, even if the
1059 * directory isn't writeable, so that the spring-opening
1063 /* Don't allow drops to non-writeable directories */
1064 if (option_get_int("dnd_spring_open") == FALSE
&&
1065 type
== drop_dest_dir
&&
1066 access(icon
->path
, W_OK
) != 0)
1071 g_dataset_set_data(context
, "drop_dest_type", type
);
1074 gdk_drag_status(context
, action
, time
);
1075 g_dataset_set_data_full(context
, "drop_dest_path",
1076 g_strdup(icon
->path
), g_free
);
1077 if (type
== drop_dest_dir
)
1078 dnd_spring_load(context
);
1080 if (dnd_highlight
&& dnd_highlight
!= icon
->widget
)
1082 gtk_drag_unhighlight(dnd_highlight
);
1083 dnd_highlight
= NULL
;
1086 if (dnd_highlight
== NULL
)
1088 gtk_drag_highlight(icon
->widget
);
1089 dnd_highlight
= icon
->widget
;
1093 return type
!= NULL
;
1097 static void add_uri_list(GtkWidget
*widget
,
1098 GdkDragContext
*context
,
1101 GtkSelectionData
*selection_data
,
1106 gboolean after
= FALSE
;
1107 GSList
*uris
, *next
;
1109 if (!selection_data
->data
)
1112 g_return_if_fail(selection_data
->data
[selection_data
->length
] == '\0');
1114 if (gtk_object_get_data(GTK_OBJECT(widget
), "after"))
1117 uris
= uri_list_to_gslist(selection_data
->data
);
1119 for (next
= uris
; next
; next
= next
->next
)
1123 path
= get_local_path((guchar
*) next
->data
);
1126 panel_add_item(panel
, path
, NULL
, after
);
1132 static void drag_end(GtkWidget
*widget
,
1133 GdkDragContext
*context
,
1136 if (tmp_icon_selected
)
1138 panel_clear_selection(NULL
);
1139 tmp_icon_selected
= FALSE
;
1143 static void menu_closed(GtkWidget
*widget
)
1145 if (tmp_icon_selected
)
1147 panel_clear_selection(NULL
);
1148 tmp_icon_selected
= FALSE
;
1152 static void drag_leave(GtkWidget
*widget
,
1153 GdkDragContext
*context
,
1157 if (dnd_highlight
&& dnd_highlight
== widget
)
1159 gtk_drag_unhighlight(dnd_highlight
);
1160 dnd_highlight
= NULL
;
1166 /* Writes lines to the file, one for each widget, prefixed by 'side'.
1167 * Returns TRUE on success, or FALSE on error (and sets errno).
1168 * Always frees the widgets list.
1170 static gboolean
write_widgets(FILE *file
, GList
*widgets
, guchar side
)
1175 tmp
= g_string_new(NULL
);
1177 for (next
= widgets
; next
; next
= next
->next
)
1181 icon
= gtk_object_get_data(GTK_OBJECT(next
->data
), "icon");
1185 g_warning("Can't find Icon from widget\n");
1189 g_string_sprintf(tmp
, "%s%c%s\n",
1190 icon
->item
.leafname
,
1191 side
, icon
->src_path
);
1192 if (fwrite(tmp
->str
, 1, tmp
->len
, file
) < tmp
->len
)
1194 g_list_free(widgets
);
1199 g_string_free(tmp
, TRUE
);
1202 g_list_free(widgets
);
1207 static void panel_save(Panel
*panel
)
1209 guchar
*save
= NULL
;
1211 guchar
*save_new
= NULL
;
1213 g_return_if_fail(panel
!= NULL
);
1215 if (strchr(panel
->name
, '/'))
1216 save
= g_strdup(panel
->name
);
1221 leaf
= g_strconcat("pan_", panel
->name
, NULL
);
1222 save
= choices_find_path_save(leaf
, "ROX-Filer", TRUE
);
1229 save_new
= g_strconcat(save
, ".new", NULL
);
1230 file
= fopen(save_new
, "wb");
1234 if (!write_widgets(file
,
1235 gtk_container_children(GTK_CONTAINER(panel
->before
)),
1239 if (!write_widgets(file
,
1240 g_list_reverse(gtk_container_children(
1241 GTK_CONTAINER(panel
->after
))),
1253 if (rename(save_new
, save
))
1258 delayed_error(_("Error saving panel"), g_strerror(errno
));
1266 static GList
*get_widget_list(Panel
*panel
)
1270 list
= gtk_container_children(GTK_CONTAINER(panel
->before
));
1271 list
= g_list_concat(list
,
1272 gtk_container_children(GTK_CONTAINER(panel
->after
)));
1277 /* Create a frame widget which can be used to add icons to the panel */
1278 static GtkWidget
*make_insert_frame(Panel
*panel
)
1281 GtkTargetEntry target_table
[] = {
1282 {"text/uri-list", 0, TARGET_URI_LIST
},
1285 frame
= gtk_frame_new(NULL
);
1286 gtk_frame_set_shadow_type(GTK_FRAME(frame
), GTK_SHADOW_NONE
);
1287 gtk_widget_set_usize(frame
, 16, 16);
1289 gtk_signal_connect(GTK_OBJECT(frame
), "drag-data-received",
1290 GTK_SIGNAL_FUNC(add_uri_list
), panel
);
1291 gtk_drag_dest_set(frame
,
1292 GTK_DEST_DEFAULT_ALL
,
1294 sizeof(target_table
) / sizeof(*target_table
),
1300 static gboolean
enter_icon(GtkWidget
*widget
,
1301 GdkEventCrossing
*event
,
1304 panel_icon_may_update(icon
);
1309 /* Called when another application wants the contents of our selection */
1310 static void selection_get(GtkWidget
*widget
,
1311 GtkSelectionData
*selection_data
,
1318 guchar
*leader
= NULL
;
1320 str
= g_string_new(NULL
);
1322 if (info
== TARGET_URI_LIST
)
1323 leader
= g_strdup_printf("file://%s", our_host_name());
1325 for (next
= panel_selection
; next
; next
= next
->next
)
1327 Icon
*icon
= (Icon
*) next
->data
;
1330 g_string_append(str
, leader
);
1331 g_string_append(str
, icon
->path
);
1332 g_string_append_c(str
, ' ');
1337 gtk_selection_data_set(selection_data
,
1338 gdk_atom_intern("STRING", FALSE
),
1341 str
->len
? str
->len
- 1 : 0);
1343 g_string_free(str
, TRUE
);
1346 /* Called when another application takes the selection away from us */
1347 static gint
lose_selection(GtkWidget
*widget
, GdkEventSelection
*event
)
1349 /* Don't send any events */
1352 panel_clear_selection(NULL
);
1358 static gint
panel_motion_event(GtkWidget
*widget
,
1359 GdkEventMotion
*event
,
1363 gboolean horz
= panel
->side
== PANEL_TOP
|| panel
->side
== PANEL_BOTTOM
;
1365 if (motion_state
!= MOTION_REPOSITION
)
1369 delta
= event
->x_root
- drag_start_x
;
1371 delta
= event
->y_root
- drag_start_y
;
1373 new = slide_from_value
- delta
;
1374 new = CLAMP(new, 0, panel
->adj
->upper
- panel
->adj
->page_size
);
1376 gtk_adjustment_set_value(panel
->adj
, new);
1381 static gint
icon_motion_event(GtkWidget
*widget
,
1382 GdkEventMotion
*event
,
1385 Panel
*panel
= icon
->panel
;
1387 gboolean horz
= panel
->side
== PANEL_TOP
|| panel
->side
== PANEL_BOTTOM
;
1391 if (motion_state
== MOTION_READY_FOR_DND
)
1393 if (dnd_motion_moved(event
))
1394 start_drag(icon
, event
);
1397 else if (motion_state
!= MOTION_REPOSITION
)
1400 list
= gtk_container_children(GTK_CONTAINER(panel
->before
));
1401 list
= g_list_append(list
, NULL
); /* The gap in the middle */
1402 list
= g_list_concat(list
,
1403 gtk_container_children(GTK_CONTAINER(panel
->after
)));
1404 me
= g_list_find(list
, widget
);
1406 g_return_val_if_fail(me
!= NULL
, TRUE
);
1408 val
= horz
? event
->x_root
: event
->y_root
;
1416 prev
= GTK_WIDGET(me
->prev
->data
);
1420 gdk_window_get_deskrelative_origin(prev
->window
, &x
, &y
);
1422 if (val
<= (horz
? x
: y
))
1426 if (dir
== 0 && me
->next
)
1432 next
= GTK_WIDGET(me
->next
->data
);
1436 gdk_window_get_deskrelative_origin(next
->window
, &x
, &y
);
1438 gdk_window_get_size(next
->window
, &w
, &h
);
1443 if (val
>= (horz
? x
: y
))
1445 if (next
== panel
->gap
)
1453 reposition_icon(icon
, g_list_index(list
, widget
) + dir
);
1458 /* Move icon to this index in the complete widget list.
1459 * 0 makes the icon the left-most icon. The gap in the middle has
1460 * an index number, which allows you to specify that the icon should
1461 * go on the left or right side.
1463 static void reposition_icon(Icon
*icon
, int index
)
1465 Panel
*panel
= icon
->panel
;
1466 GtkWidget
*widget
= icon
->widget
;
1470 list
= gtk_container_children(GTK_CONTAINER(panel
->before
));
1471 before_len
= g_list_length(list
);
1473 if (index
<= before_len
)
1475 /* Want to move icon to the 'before' list. Is it there
1479 if (!g_list_find(list
, widget
))
1482 gtk_grab_remove(widget
);
1483 gtk_widget_reparent(widget
, panel
->before
);
1484 dnd_motion_grab_pointer();
1485 gtk_grab_add(widget
);
1488 gtk_box_reorder_child(GTK_BOX(panel
->before
), widget
, index
);
1492 /* Else, we need it in the 'after' list. */
1494 index
-= before_len
+ 1;
1498 list
= gtk_container_children(GTK_CONTAINER(panel
->after
));
1500 if (!g_list_find(list
, widget
))
1502 /* Not already there, reparent */
1503 gtk_grab_remove(widget
);
1504 gtk_widget_reparent(widget
, panel
->after
);
1505 dnd_motion_grab_pointer();
1506 gtk_grab_add(widget
);
1509 gtk_box_reorder_child(GTK_BOX(panel
->after
), widget
, index
);
1517 static void start_drag(Icon
*icon
, GdkEventMotion
*event
)
1519 GtkWidget
*widget
= icon
->widget
;
1521 if (!icon
->selected
)
1523 if (event
->state
& GDK_BUTTON1_MASK
)
1525 /* Select just this one */
1526 panel_clear_selection(icon
);
1527 tmp_icon_selected
= TRUE
;
1530 icon_set_selected(icon
, TRUE
);
1533 g_return_if_fail(panel_selection
!= NULL
);
1535 if (panel_selection
->next
== NULL
)
1536 drag_one_item(widget
, event
, icon
->path
, &icon
->item
);
1541 uri_list
= create_uri_list(panel_selection
);
1542 drag_selection(widget
, event
, uri_list
);
1547 /* Return a text/uri-list of all the icons in the list */
1548 static guchar
*create_uri_list(GList
*list
)
1554 tmp
= g_string_new(NULL
);
1555 leader
= g_strdup_printf("file://%s", our_host_name());
1557 for (; list
; list
= list
->next
)
1559 Icon
*icon
= (Icon
*) list
->data
;
1561 g_string_append(tmp
, leader
);
1562 g_string_append(tmp
, icon
->path
);
1563 g_string_append(tmp
, "\r\n");
1568 g_string_free(tmp
, FALSE
);