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 /* panel.c - code for dealing with panel windows */
31 #include <libxml/parser.h>
43 #include "gui_support.h"
54 /* The width of the separator at the inner edge of the panel */
57 /* The gap between panel icons */
58 #define PANEL_ICON_SPACING 8
60 Panel
*current_panel
[PANEL_NUMBER_OF_SIDES
];
62 /* NULL => Not loading a panel */
63 static Panel
*loading_panel
= NULL
;
65 /* Static prototypes */
66 static int panel_delete(GtkWidget
*widget
, GdkEvent
*event
, Panel
*panel
);
67 static void panel_destroyed(GtkWidget
*widget
, Panel
*panel
);
68 static const char *pan_from_file(gchar
*line
);
69 static gint
icon_button_release(GtkWidget
*widget
,
70 GdkEventButton
*event
,
72 static gint
icon_button_press(GtkWidget
*widget
,
73 GdkEventButton
*event
,
75 static void reposition_panel(GtkWidget
*window
,
76 GtkAllocation
*alloc
, Panel
*panel
);
77 static gint
expose_icon(GtkWidget
*widget
,
78 GdkEventExpose
*event
,
80 static gint
draw_icon(GtkWidget
*widget
,
81 GdkRectangle
*badarea
,
83 static gint
panel_button_release(GtkWidget
*widget
,
84 GdkEventButton
*event
,
86 static gint
panel_button_press(GtkWidget
*widget
,
87 GdkEventButton
*event
,
89 static void panel_post_resize(GtkWidget
*box
,
90 GtkRequisition
*req
, Panel
*panel
);
91 static void drag_set_panel_dest(Icon
*icon
);
92 static void add_uri_list(GtkWidget
*widget
,
93 GdkDragContext
*context
,
96 GtkSelectionData
*selection_data
,
100 static void panel_add_item(Panel
*panel
,
104 static gboolean
drag_motion(GtkWidget
*widget
,
105 GdkDragContext
*context
,
110 static void drag_leave(GtkWidget
*widget
,
111 GdkDragContext
*context
,
114 static GtkWidget
*make_insert_frame(Panel
*panel
);
115 static gboolean
enter_icon(GtkWidget
*widget
,
116 GdkEventCrossing
*event
,
118 static gint
icon_motion_event(GtkWidget
*widget
,
119 GdkEventMotion
*event
,
121 static gint
panel_motion_event(GtkWidget
*widget
,
122 GdkEventMotion
*event
,
124 static void reposition_icon(Icon
*icon
, int index
);
125 static void start_drag(Icon
*icon
, GdkEventMotion
*event
);
126 static guchar
*create_uri_list(GList
*list
);
127 static void drag_end(GtkWidget
*widget
,
128 GdkDragContext
*context
,
130 static void perform_action(Panel
*panel
,
132 GdkEventButton
*event
);
133 static void run_applet(Icon
*icon
);
134 static void panel_set_style(void);
135 static void size_request(GtkWidget
*widget
, GtkRequisition
*req
, Icon
*icon
);
136 static void panel_load_from_xml(Panel
*panel
, xmlDocPtr doc
);
137 static gboolean
draw_panel_edge(GtkWidget
*widget
, GdkEventExpose
*event
,
141 static GtkWidget
*dnd_highlight
= NULL
; /* (stops flickering) */
143 /* When sliding the panel, records where the panel was before */
144 static gint slide_from_value
= 0;
147 #define SHOW_APPS_SMALL 1
149 static Option o_panel_style
;
151 static int closing_panel
= 0; /* Don't panel_save; destroying! */
153 /****************************************************************
154 * EXTERNAL INTERFACE *
155 ****************************************************************/
157 void panel_init(void)
159 option_add_int(&o_panel_style
, "panel_style", SHOW_APPS_SMALL
);
161 option_add_notify(panel_set_style
);
164 /* 'name' may be NULL or "" to remove the panel */
165 Panel
*panel_new(const gchar
*name
, PanelSide side
)
169 GtkWidget
*vp
, *box
, *frame
, *align
;
171 g_return_val_if_fail(side
>= 0 && side
< PANEL_NUMBER_OF_SIDES
, NULL
);
172 g_return_val_if_fail(loading_panel
== NULL
, NULL
);
174 if (name
&& *name
== '\0')
177 if (current_panel
[side
])
182 gtk_widget_destroy(current_panel
[side
]->window
);
188 if (name
== NULL
|| *name
== '\0')
191 panel
= g_new(Panel
, 1);
192 panel
->name
= g_strdup(name
);
194 panel
->window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
195 gtk_window_set_resizable(GTK_WINDOW(panel
->window
), FALSE
);
196 gtk_window_set_wmclass(GTK_WINDOW(panel
->window
), "ROX-Panel", PROJECT
);
197 gtk_widget_set_events(panel
->window
,
198 GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
|
199 GDK_BUTTON1_MOTION_MASK
| GDK_BUTTON2_MOTION_MASK
|
200 GDK_BUTTON2_MOTION_MASK
);
202 g_signal_connect(panel
->window
, "delete-event",
203 G_CALLBACK(panel_delete
), panel
);
204 g_signal_connect(panel
->window
, "destroy",
205 G_CALLBACK(panel_destroyed
), panel
);
206 g_signal_connect(panel
->window
, "button_press_event",
207 G_CALLBACK(panel_button_press
), panel
);
208 g_signal_connect(panel
->window
, "button_release_event",
209 G_CALLBACK(panel_button_release
), panel
);
210 g_signal_connect(panel
->window
, "motion-notify-event",
211 G_CALLBACK(panel_motion_event
), panel
);
213 if (strchr(name
, '/'))
214 load_path
= g_strdup(name
);
219 leaf
= g_strconcat("pan_", name
, NULL
);
220 load_path
= choices_find_path_load(leaf
, PROJECT
);
224 if (panel
->side
== PANEL_RIGHT
)
225 align
= gtk_alignment_new(1.0, 0.0, 0.0, 1.0);
226 else if (panel
->side
== PANEL_BOTTOM
)
227 align
= gtk_alignment_new(0.0, 1.0, 1.0, 0.0);
228 else if (panel
->side
== PANEL_TOP
)
229 align
= gtk_alignment_new(0.0, 0.0, 1.0, 0.0);
231 align
= gtk_alignment_new(0.0, 0.0, 0.0, 1.0);
233 gtk_container_add(GTK_CONTAINER(panel
->window
), align
);
235 vp
= gtk_viewport_new(NULL
, NULL
);
236 gtk_container_set_resize_mode(GTK_CONTAINER(vp
), GTK_RESIZE_PARENT
);
237 gtk_viewport_set_shadow_type(GTK_VIEWPORT(vp
), GTK_SHADOW_NONE
);
238 gtk_container_add(GTK_CONTAINER(align
), vp
);
240 g_signal_connect(align
, "expose-event",
241 G_CALLBACK(draw_panel_edge
), panel
);
243 if (side
== PANEL_TOP
|| side
== PANEL_BOTTOM
)
245 panel
->adj
= gtk_viewport_get_hadjustment(GTK_VIEWPORT(vp
));
246 box
= gtk_hbox_new(FALSE
, 0);
247 panel
->before
= gtk_hbox_new(FALSE
, 0);
248 panel
->after
= gtk_hbox_new(FALSE
, 0);
252 panel
->adj
= gtk_viewport_get_vadjustment(GTK_VIEWPORT(vp
));
253 box
= gtk_vbox_new(FALSE
, 0);
254 panel
->before
= gtk_vbox_new(FALSE
, 0);
255 panel
->after
= gtk_vbox_new(FALSE
, 0);
258 gtk_container_add(GTK_CONTAINER(vp
), box
);
259 gtk_box_pack_start(GTK_BOX(box
), panel
->before
, FALSE
, TRUE
, 0);
260 gtk_box_pack_end(GTK_BOX(box
), panel
->after
, FALSE
, TRUE
, 0);
262 frame
= make_insert_frame(panel
);
263 gtk_box_pack_start(GTK_BOX(box
), frame
, TRUE
, TRUE
, 4);
265 /* This is used so that we can find the middle easily! */
266 panel
->gap
= gtk_event_box_new();
267 gtk_box_pack_start(GTK_BOX(box
), panel
->gap
, FALSE
, FALSE
, 0);
269 frame
= make_insert_frame(panel
);
270 g_object_set_data(G_OBJECT(frame
), "after", "yes");
271 gtk_box_pack_start(GTK_BOX(box
), frame
, TRUE
, TRUE
, 4);
273 gtk_widget_realize(panel
->window
);
274 make_panel_window(panel
->window
);
276 gtk_widget_show_all(align
);
278 loading_panel
= panel
;
279 if (load_path
&& access(load_path
, F_OK
) == 0)
282 doc
= xmlParseFile(load_path
);
285 panel_load_from_xml(panel
, doc
);
290 parse_file(load_path
, pan_from_file
);
291 delayed_error(_("Your old panel file has been "
292 "converted to the new XML format."));
298 /* Don't scare users with an empty panel... */
301 panel_add_item(panel
, "~", "Home", FALSE
);
303 apps
= pathdup(make_path(app_dir
, "..")->str
);
306 panel_add_item(panel
, apps
, "Apps", FALSE
);
310 loading_panel
= NULL
;
313 current_panel
[side
] = panel
;
315 gtk_widget_queue_resize(box
);
316 g_signal_connect(panel
->window
, "size-request",
317 G_CALLBACK(panel_post_resize
), panel
);
318 g_signal_connect(panel
->window
, "size-allocate",
319 G_CALLBACK(reposition_panel
), panel
);
322 gtk_widget_show(panel
->window
);
327 gboolean
panel_want_show_text(Icon
*icon
)
329 if (o_panel_style
.int_value
== SHOW_BOTH
)
331 if (o_panel_style
.int_value
== SHOW_ICON
)
334 if (icon
->item
->flags
& ITEM_FLAG_APPDIR
)
340 void panel_icon_renamed(Icon
*icon
)
342 GtkLabel
*label
= GTK_LABEL(icon
->label
);
344 gtk_label_set_text(label
, icon
->item
->leafname
);
347 /* Externally visible function to add an item to a panel */
348 gboolean
panel_add(PanelSide side
,
349 const gchar
*path
, const gchar
*label
, gboolean after
)
351 g_return_val_if_fail(side
>= 0 && side
< PANEL_NUMBER_OF_SIDES
, FALSE
);
353 g_return_val_if_fail(current_panel
[side
] != NULL
, FALSE
);
355 panel_add_item(current_panel
[side
], path
, label
, after
);
360 /****************************************************************
361 * INTERNAL FUNCTIONS *
362 ****************************************************************/
364 /* User has tried to close the panel via the window manager - confirm */
365 static int panel_delete(GtkWidget
*widget
, GdkEvent
*event
, Panel
*panel
)
367 return get_choice(_("Close panel?"),
368 _("You have tried to close a panel via the window "
369 "manager - I usually find that this is accidental... "
371 2, _("Cancel"), _("Remove")) != 1;
374 static void panel_destroyed(GtkWidget
*widget
, Panel
*panel
)
376 if (current_panel
[panel
->side
] == panel
)
377 current_panel
[panel
->side
] = NULL
;
379 if (panel
->side
== PANEL_TOP
|| panel
->side
== PANEL_BOTTOM
)
381 if (current_panel
[PANEL_RIGHT
])
382 gtk_widget_queue_resize(
383 current_panel
[PANEL_RIGHT
]->window
);
384 if (current_panel
[PANEL_LEFT
])
385 gtk_widget_queue_resize(
386 current_panel
[PANEL_LEFT
]->window
);
392 if (--number_of_windows
< 1)
396 static void panel_load_side(Panel
*panel
, xmlNodePtr side
, gboolean after
)
401 for (node
= side
->xmlChildrenNode
; node
; node
= node
->next
)
403 if (node
->type
!= XML_ELEMENT_NODE
)
405 if (strcmp(node
->name
, "icon") != 0)
408 label
= xmlGetProp(node
, "label");
410 label
= g_strdup("<missing label>");
411 path
= xmlNodeGetContent(node
);
413 path
= g_strdup("<missing path>");
415 panel_add_item(panel
, path
, label
, after
);
422 /* Create one panel icon for each icon in the doc */
423 static void panel_load_from_xml(Panel
*panel
, xmlDocPtr doc
)
427 root
= xmlDocGetRootElement(doc
);
428 panel_load_side(panel
, get_subnode(root
, NULL
, "start"), FALSE
);
429 panel_load_side(panel
, get_subnode(root
, NULL
, "end"), TRUE
);
432 /* Called for each line in the config file while loading a new panel */
433 static const char *pan_from_file(gchar
*line
)
437 g_return_val_if_fail(line
!= NULL
, NULL
);
438 g_return_val_if_fail(loading_panel
!= NULL
, NULL
);
443 sep
= strpbrk(line
, "<>");
445 return _("Missing < or > in panel config file");
448 leaf
= g_strndup(line
, sep
- line
);
452 panel_add_item(loading_panel
, sep
+ 1, leaf
, sep
[0] == '>');
459 static gboolean
icon_pointer_in(GtkWidget
*widget
,
460 GdkEventCrossing
*event
,
463 gtk_widget_set_state(widget
, GTK_STATE_PRELIGHT
);
468 static gboolean
icon_pointer_out(GtkWidget
*widget
,
469 GdkEventCrossing
*event
,
472 gtk_widget_set_state(widget
, GTK_STATE_NORMAL
);
477 /* Add an icon with this path to the panel. If after is TRUE then the
478 * icon is added to the right/bottom end of the panel.
480 * If name is NULL a suitable name is taken from path.
482 static void panel_add_item(Panel
*panel
,
490 g_return_if_fail(panel
!= NULL
);
491 g_return_if_fail(path
!= NULL
);
493 widget
= gtk_event_box_new();
494 gtk_widget_set_events(widget
,
495 GDK_BUTTON1_MOTION_MASK
| GDK_BUTTON2_MOTION_MASK
|
496 GDK_BUTTON3_MOTION_MASK
|
497 GDK_EXPOSURE_MASK
| GDK_BUTTON_PRESS_MASK
|
498 GDK_BUTTON_RELEASE_MASK
);
500 gtk_box_pack_start(GTK_BOX(after
? panel
->after
: panel
->before
),
501 widget
, FALSE
, TRUE
, 0);
503 gtk_box_reorder_child(GTK_BOX(panel
->after
), widget
, 0);
505 gtk_widget_realize(widget
);
507 icon
= g_new(Icon
, 1);
509 icon
->src_path
= g_strdup(path
);
510 icon
->path
= icon_convert_path(path
);
515 g_object_set_data(G_OBJECT(widget
), "icon", icon
);
517 icon_hash_path(icon
);
519 icon
->widget
= widget
;
520 gtk_widget_set_name(icon
->widget
, "panel-icon");
521 icon
->selected
= FALSE
;
524 icon
->item
= diritem_new(name
);
529 slash
= strrchr(icon
->path
, '/');
530 icon
->item
= diritem_new(slash
&& slash
[1] ? slash
+ 1
533 diritem_restat(icon
->path
, icon
->item
);
535 g_signal_connect_swapped(widget
, "destroy",
536 G_CALLBACK(icon_destroyed
), icon
);
538 if (icon
->item
->base_type
== TYPE_DIRECTORY
)
541 g_signal_connect(widget
, "button_release_event",
542 G_CALLBACK(icon_button_release
), icon
);
543 g_signal_connect(widget
, "button_press_event",
544 G_CALLBACK(icon_button_press
), icon
);
545 g_signal_connect(icon
->widget
, "motion-notify-event",
546 G_CALLBACK(icon_motion_event
), icon
);
547 g_signal_connect(icon
->widget
, "enter-notify-event",
548 G_CALLBACK(icon_pointer_in
), icon
);
549 g_signal_connect(icon
->widget
, "leave-notify-event",
550 G_CALLBACK(icon_pointer_out
), icon
);
554 g_signal_connect(widget
, "enter-notify-event",
555 G_CALLBACK(enter_icon
), icon
);
556 g_signal_connect_after(widget
, "expose_event",
557 G_CALLBACK(expose_icon
), icon
);
558 g_signal_connect(widget
, "drag_data_get",
559 G_CALLBACK(drag_data_get
), NULL
);
561 g_signal_connect(widget
, "size_request",
562 G_CALLBACK(size_request
), icon
);
564 drag_set_panel_dest(icon
);
566 icon
->label
= gtk_label_new(icon
->item
->leafname
);
567 gtk_container_add(GTK_CONTAINER(icon
->widget
), icon
->label
);
568 gtk_misc_set_alignment(GTK_MISC(icon
->label
), 0.5, 1);
569 gtk_misc_set_padding(GTK_MISC(icon
->label
), 1, 2);
576 gtk_widget_show(widget
);
579 /* Called when Gtk+ wants to know how much space an icon needs.
580 * 'req' is already big enough for the label, if shown.
582 static void size_request(GtkWidget
*widget
, GtkRequisition
*req
, Icon
*icon
)
584 int im_width
, im_height
;
586 im_width
= icon
->item
->image
->width
;
587 im_height
= MIN(icon
->item
->image
->height
, ICON_HEIGHT
);
589 req
->height
+= im_height
;
590 req
->width
= MAX(req
->width
, im_width
);
592 if (icon
->panel
->side
== PANEL_LEFT
|| icon
->panel
->side
== PANEL_RIGHT
)
593 req
->height
+= PANEL_ICON_SPACING
;
595 req
->width
+= PANEL_ICON_SPACING
;
598 static gint
expose_icon(GtkWidget
*widget
,
599 GdkEventExpose
*event
,
602 return draw_icon(widget
, &event
->area
, icon
);
605 static gint
draw_icon(GtkWidget
*widget
, GdkRectangle
*badarea
, Icon
*icon
)
610 gdk_window_get_size(widget
->window
, &width
, &height
);
614 area
.height
= icon
->item
->image
->height
;
616 if (panel_want_show_text(icon
))
618 int text_height
= icon
->label
->requisition
.height
;
620 area
.y
= height
- text_height
- area
.height
;
622 draw_large_icon(widget
, &area
, icon
->item
,
623 icon
->item
->image
, icon
->selected
);
627 area
.y
= (height
- area
.height
) >> 1;
628 draw_large_icon(widget
, &area
, icon
->item
,
629 icon
->item
->image
, icon
->selected
);
635 /* icon may be NULL if the event is on the background */
636 static void perform_action(Panel
*panel
, Icon
*icon
, GdkEventButton
*event
)
640 action
= bind_lookup_bev(icon
? BIND_PANEL_ICON
: BIND_PANEL
, event
);
642 if (icon
&& icon
->socket
)
643 if (action
!= ACT_POPUP_MENU
&& action
!= ACT_MOVE_ICON
)
650 wink_widget(icon
->widget
);
651 run_diritem(icon
->path
, icon
->item
, NULL
, NULL
, FALSE
);
655 wink_widget(icon
->widget
);
656 run_diritem(icon
->path
, icon
->item
, NULL
, NULL
, TRUE
);
660 icon_show_menu(event
, icon
, panel
);
663 dnd_motion_start(MOTION_REPOSITION
);
665 case ACT_PRIME_AND_SELECT
:
667 icon_select_only(icon
);
668 dnd_motion_start(MOTION_READY_FOR_DND
);
670 case ACT_PRIME_AND_TOGGLE
:
671 icon_set_selected(icon
, !icon
->selected
);
672 dnd_motion_start(MOTION_READY_FOR_DND
);
674 case ACT_PRIME_FOR_DND
:
675 dnd_motion_start(MOTION_READY_FOR_DND
);
677 case ACT_TOGGLE_SELECTED
:
678 icon_set_selected(icon
, !icon
->selected
);
680 case ACT_SELECT_EXCL
:
681 icon_set_selected(icon
, TRUE
);
683 case ACT_SLIDE_CLEAR_PANEL
:
684 icon_select_only(NULL
);
686 case ACT_SLIDE_PANEL
:
687 dnd_motion_grab_pointer();
688 slide_from_value
= panel
->adj
->value
;
689 dnd_motion_start(MOTION_REPOSITION
);
693 case ACT_CLEAR_SELECTION
:
694 icon_select_only(NULL
);
697 g_warning("Unsupported action : %d\n", action
);
702 static gint
panel_button_release(GtkWidget
*widget
,
703 GdkEventButton
*event
,
706 if (dnd_motion_release(event
))
709 perform_action(panel
, NULL
, event
);
714 static gint
panel_button_press(GtkWidget
*widget
,
715 GdkEventButton
*event
,
718 if (dnd_motion_press(panel
->window
, event
))
719 perform_action(panel
, NULL
, event
);
724 static gint
icon_button_release(GtkWidget
*widget
,
725 GdkEventButton
*event
,
728 if (icon
->socket
&& event
->button
== 1)
729 return FALSE
; /* Restart button */
731 if (dnd_motion_release(event
))
734 perform_action(icon
->panel
, icon
, event
);
739 static gint
icon_button_press(GtkWidget
*widget
,
740 GdkEventButton
*event
,
743 if (icon
->socket
&& event
->button
== 1)
744 return FALSE
; /* Restart button */
746 if (dnd_motion_press(widget
, event
))
747 perform_action(icon
->panel
, icon
, event
);
752 static void reposition_panel(GtkWidget
*window
,
753 GtkAllocation
*alloc
, Panel
*panel
)
756 PanelSide side
= panel
->side
;
758 if (side
== PANEL_LEFT
|| side
== PANEL_RIGHT
)
760 if (side
== PANEL_RIGHT
)
761 x
= screen_width
- alloc
->width
;
763 if (current_panel
[PANEL_TOP
])
765 GtkWidget
*win
= current_panel
[PANEL_TOP
]->window
;
766 y
+= win
->allocation
.height
;
770 if (side
== PANEL_BOTTOM
)
771 y
= screen_height
- alloc
->height
;
773 gtk_window_move(GTK_WINDOW(panel
->window
), x
, y
);
774 gdk_window_move(panel
->window
->window
, x
, y
);
776 if (side
== PANEL_BOTTOM
|| side
== PANEL_TOP
)
778 if (current_panel
[PANEL_RIGHT
])
779 gtk_widget_queue_resize(
780 current_panel
[PANEL_RIGHT
]->window
);
781 if (current_panel
[PANEL_LEFT
])
782 gtk_widget_queue_resize(
783 current_panel
[PANEL_LEFT
]->window
);
787 /* Same as drag_set_dest(), but for panel icons */
788 static void drag_set_panel_dest(Icon
*icon
)
790 GtkWidget
*obj
= icon
->widget
;
792 make_drop_target(icon
->widget
, 0);
794 g_signal_connect(obj
, "drag_motion", G_CALLBACK(drag_motion
), icon
);
795 g_signal_connect(obj
, "drag_leave", G_CALLBACK(drag_leave
), icon
);
796 g_signal_connect(obj
, "drag_end", G_CALLBACK(drag_end
), icon
);
799 static gboolean
drag_motion(GtkWidget
*widget
,
800 GdkDragContext
*context
,
806 GdkDragAction action
= context
->suggested_action
;
808 DirItem
*item
= icon
->item
;
811 goto out
; /* Can't drag a selection to itself */
813 type
= dnd_motion_item(context
, &item
);
818 /* We actually must pretend to accept the drop, even if the
819 * directory isn't writeable, so that the spring-opening
823 /* Don't allow drops to non-writeable directories */
824 if (o_dnd_spring_open
.int_value
== FALSE
&&
825 type
== drop_dest_dir
&&
826 access(icon
->path
, W_OK
) != 0)
831 g_dataset_set_data(context
, "drop_dest_type", type
);
834 gdk_drag_status(context
, action
, time
);
835 g_dataset_set_data_full(context
, "drop_dest_path",
836 g_strdup(icon
->path
), g_free
);
837 if (type
== drop_dest_dir
)
838 dnd_spring_load(context
, NULL
);
840 if (dnd_highlight
&& dnd_highlight
!= icon
->widget
)
842 gtk_drag_unhighlight(dnd_highlight
);
843 dnd_highlight
= NULL
;
846 if (dnd_highlight
== NULL
)
848 gtk_drag_highlight(icon
->widget
);
849 dnd_highlight
= icon
->widget
;
857 static void add_uri_list(GtkWidget
*widget
,
858 GdkDragContext
*context
,
861 GtkSelectionData
*selection_data
,
866 gboolean after
= FALSE
;
869 if (!selection_data
->data
)
872 g_return_if_fail(selection_data
->data
[selection_data
->length
] == '\0');
874 if (g_object_get_data(G_OBJECT(widget
), "after"))
877 uris
= uri_list_to_glist(selection_data
->data
);
879 for (next
= uris
; next
; next
= next
->next
)
883 path
= get_local_path((guchar
*) next
->data
);
886 panel_add_item(panel
, path
, NULL
, after
);
892 static void drag_end(GtkWidget
*widget
,
893 GdkDragContext
*context
,
896 if (tmp_icon_selected
)
898 icon_select_only(NULL
);
899 tmp_icon_selected
= FALSE
;
903 static void drag_leave(GtkWidget
*widget
,
904 GdkDragContext
*context
,
908 if (dnd_highlight
&& dnd_highlight
== widget
)
910 gtk_drag_unhighlight(dnd_highlight
);
911 dnd_highlight
= NULL
;
917 /* Create XML icon nodes for these widgets.
918 * Always frees the widgets list.
920 static void make_widgets(xmlNodePtr side
, GList
*widgets
)
924 for (next
= widgets
; next
; next
= next
->next
)
929 icon
= g_object_get_data(G_OBJECT(next
->data
), "icon");
933 g_warning("Can't find Icon from widget\n");
937 tree
= xmlNewTextChild(side
, NULL
, "icon", icon
->src_path
);
939 xmlSetProp(tree
, "label", icon
->item
->leafname
);
943 g_list_free(widgets
);
946 void panel_save(Panel
*panel
)
951 guchar
*save_new
= NULL
;
953 g_return_if_fail(panel
!= NULL
);
955 if (strchr(panel
->name
, '/'))
956 save
= g_strdup(panel
->name
);
961 leaf
= g_strconcat("pan_", panel
->name
, NULL
);
962 save
= choices_find_path_save(leaf
, PROJECT
, TRUE
);
969 doc
= xmlNewDoc("1.0");
970 xmlDocSetRootElement(doc
, xmlNewDocNode(doc
, NULL
, "pinboard", NULL
));
972 root
= xmlDocGetRootElement(doc
);
973 make_widgets(xmlNewChild(root
, NULL
, "start", NULL
),
974 gtk_container_get_children(GTK_CONTAINER(panel
->before
)));
976 make_widgets(xmlNewChild(root
, NULL
, "end", NULL
),
977 g_list_reverse(gtk_container_get_children(
978 GTK_CONTAINER(panel
->after
))));
980 save_new
= g_strconcat(save
, ".new", NULL
);
981 if (save_xml_file(doc
, save_new
) || rename(save_new
, save
))
982 delayed_error(_("Error saving panel %s: %s"),
983 save
, g_strerror(errno
));
991 /* Create a frame widget which can be used to add icons to the panel */
992 static GtkWidget
*make_insert_frame(Panel
*panel
)
995 GtkTargetEntry target_table
[] = {
996 {"text/uri-list", 0, TARGET_URI_LIST
},
999 frame
= gtk_frame_new(NULL
);
1000 gtk_frame_set_shadow_type(GTK_FRAME(frame
), GTK_SHADOW_NONE
);
1001 gtk_widget_set_size_request(frame
, 16, 16);
1003 g_signal_connect(frame
, "drag-data-received",
1004 G_CALLBACK(add_uri_list
), panel
);
1005 gtk_drag_dest_set(frame
,
1006 GTK_DEST_DEFAULT_ALL
,
1008 sizeof(target_table
) / sizeof(*target_table
),
1014 static gboolean
enter_icon(GtkWidget
*widget
,
1015 GdkEventCrossing
*event
,
1018 icon_may_update(icon
);
1023 static gint
panel_motion_event(GtkWidget
*widget
,
1024 GdkEventMotion
*event
,
1028 gboolean horz
= panel
->side
== PANEL_TOP
|| panel
->side
== PANEL_BOTTOM
;
1030 if (motion_state
!= MOTION_REPOSITION
)
1034 delta
= event
->x_root
- drag_start_x
;
1036 delta
= event
->y_root
- drag_start_y
;
1038 new = slide_from_value
- delta
;
1039 new = CLAMP(new, 0, panel
->adj
->upper
- panel
->adj
->page_size
);
1041 gtk_adjustment_set_value(panel
->adj
, new);
1046 static gint
icon_motion_event(GtkWidget
*widget
,
1047 GdkEventMotion
*event
,
1050 Panel
*panel
= icon
->panel
;
1052 gboolean horz
= panel
->side
== PANEL_TOP
|| panel
->side
== PANEL_BOTTOM
;
1056 if (motion_state
== MOTION_READY_FOR_DND
)
1058 if (dnd_motion_moved(event
))
1059 start_drag(icon
, event
);
1062 else if (motion_state
!= MOTION_REPOSITION
)
1065 list
= gtk_container_get_children(GTK_CONTAINER(panel
->before
));
1066 list
= g_list_append(list
, NULL
); /* The gap in the middle */
1067 list
= g_list_concat(list
,
1068 gtk_container_get_children(GTK_CONTAINER(panel
->after
)));
1069 me
= g_list_find(list
, widget
);
1071 g_return_val_if_fail(me
!= NULL
, TRUE
);
1073 val
= horz
? event
->x_root
: event
->y_root
;
1081 prev
= GTK_WIDGET(me
->prev
->data
);
1085 gdk_window_get_deskrelative_origin(prev
->window
, &x
, &y
);
1087 if (val
<= (horz
? x
: y
))
1091 if (dir
== 0 && me
->next
)
1097 next
= GTK_WIDGET(me
->next
->data
);
1101 gdk_window_get_deskrelative_origin(next
->window
, &x
, &y
);
1103 gdk_window_get_size(next
->window
, &w
, &h
);
1108 if (val
>= (horz
? x
: y
))
1110 if (next
== panel
->gap
)
1118 reposition_icon(icon
, g_list_index(list
, widget
) + dir
);
1123 /* Move icon to this index in the complete widget list.
1124 * 0 makes the icon the left-most icon. The gap in the middle has
1125 * an index number, which allows you to specify that the icon should
1126 * go on the left or right side.
1128 static void reposition_icon(Icon
*icon
, int index
)
1130 Panel
*panel
= icon
->panel
;
1131 GtkWidget
*widget
= icon
->widget
;
1135 list
= gtk_container_get_children(GTK_CONTAINER(panel
->before
));
1136 before_len
= g_list_length(list
);
1138 if (index
<= before_len
)
1140 /* Want to move icon to the 'before' list. Is it there
1144 if (!g_list_find(list
, widget
))
1147 gtk_grab_remove(widget
);
1148 gtk_widget_reparent(widget
, panel
->before
);
1149 dnd_motion_grab_pointer();
1150 gtk_grab_add(widget
);
1153 gtk_box_reorder_child(GTK_BOX(panel
->before
), widget
, index
);
1157 /* Else, we need it in the 'after' list. */
1159 index
-= before_len
+ 1;
1163 list
= gtk_container_get_children(GTK_CONTAINER(panel
->after
));
1165 if (!g_list_find(list
, widget
))
1167 /* Not already there, reparent */
1168 gtk_grab_remove(widget
);
1169 gtk_widget_reparent(widget
, panel
->after
);
1170 dnd_motion_grab_pointer();
1171 gtk_grab_add(widget
);
1174 gtk_box_reorder_child(GTK_BOX(panel
->after
), widget
, index
);
1182 static void start_drag(Icon
*icon
, GdkEventMotion
*event
)
1184 GtkWidget
*widget
= icon
->widget
;
1186 if (!icon
->selected
)
1188 if (event
->state
& GDK_BUTTON1_MASK
)
1190 /* Select just this one */
1191 icon_select_only(icon
);
1192 tmp_icon_selected
= TRUE
;
1195 icon_set_selected(icon
, TRUE
);
1198 g_return_if_fail(icon_selection
!= NULL
);
1200 if (icon_selection
->next
== NULL
)
1201 drag_one_item(widget
, event
, icon
->path
, icon
->item
, NULL
);
1206 uri_list
= create_uri_list(icon_selection
);
1207 drag_selection(widget
, event
, uri_list
);
1212 /* Return a text/uri-list of all the icons in the list */
1213 static guchar
*create_uri_list(GList
*list
)
1219 tmp
= g_string_new(NULL
);
1220 leader
= g_strdup_printf("file://%s", our_host_name_for_dnd());
1222 for (; list
; list
= list
->next
)
1224 Icon
*icon
= (Icon
*) list
->data
;
1226 g_string_append(tmp
, leader
);
1227 g_string_append(tmp
, icon
->path
);
1228 g_string_append(tmp
, "\r\n");
1233 g_string_free(tmp
, FALSE
);
1238 static void applet_died(GtkWidget
*socket
)
1240 gboolean never_plugged
;
1242 never_plugged
= (!g_object_get_data(G_OBJECT(socket
), "lost_plug"))
1243 && !GTK_SOCKET(socket
)->plug_window
;
1248 _("Applet quit without ever creating a widget!"));
1249 gtk_widget_destroy(socket
);
1252 gtk_widget_unref(socket
);
1255 static void socket_destroyed(GtkWidget
*socket
, GtkWidget
*widget
)
1257 g_object_set_data(G_OBJECT(socket
), "lost_plug", "yes");
1259 gtk_widget_unref(socket
);
1261 gtk_widget_destroy(widget
); /* Remove from panel */
1264 panel_save(g_object_get_data(G_OBJECT(socket
), "panel"));
1267 /* Try to run this applet.
1270 * - No executable AppletRun:
1271 * icon->socket == NULL (unchanged) on return.
1273 * Otherwise, create socket (setting icon->socket) and ref it twice.
1275 * - AppletRun quits without connecting a plug:
1276 * On child death lost_plug is unset and socket is empty.
1278 * Report error and destroy widget (to 'socket destroyed').
1280 * - AppletRun quits while plug is in socket:
1281 * Unref socket once. Socket will be destroyed later.
1283 * - Socket is destroyed.
1284 * Set lost_plug = "yes" and remove widget from panel.
1287 static void run_applet(Icon
*icon
)
1292 argv
[0] = make_path(icon
->path
, "AppletRun")->str
;
1294 if (access(argv
[0], X_OK
) != 0)
1297 icon
->socket
= gtk_socket_new();
1298 /* Two refs held: one for child death, one for socket destroyed */
1299 gtk_widget_ref(icon
->socket
);
1300 gtk_widget_ref(icon
->socket
);
1302 gtk_container_add(GTK_CONTAINER(icon
->widget
), icon
->socket
);
1303 gtk_widget_show_all(icon
->socket
);
1304 gtk_widget_realize(icon
->socket
);
1308 PanelSide side
= icon
->panel
->side
;
1310 /* Set a hint to let applets position their menus correctly */
1311 pos
= g_strdup_printf("%s,%d",
1312 side
== PANEL_TOP
? "Top" :
1313 side
== PANEL_BOTTOM
? "Bottom" :
1314 side
== PANEL_LEFT
? "Left" :
1315 "Right", MENU_MARGIN
);
1316 gdk_property_change(icon
->socket
->window
,
1317 gdk_atom_intern("_ROX_PANEL_MENU_POS", FALSE
),
1318 gdk_atom_intern("STRING", FALSE
),
1319 8, GDK_PROP_MODE_REPLACE
,
1324 g_object_set_data(G_OBJECT(icon
->widget
), "icon", icon
);
1325 g_object_set_data(G_OBJECT(icon
->socket
), "panel", icon
->panel
);
1327 g_signal_connect(icon
->socket
, "destroy",
1328 G_CALLBACK(socket_destroyed
), icon
->widget
);
1330 argv
[1] = g_strdup_printf("%ld",
1331 GDK_WINDOW_XWINDOW(icon
->socket
->window
));
1334 pid
= spawn_full((const char **) argv
, NULL
, NULL
);
1336 on_child_death(pid
, (CallbackFn
) applet_died
, icon
->socket
);
1341 static void panel_post_resize(GtkWidget
*win
, GtkRequisition
*req
, Panel
*panel
)
1343 if (panel
->side
== PANEL_TOP
|| panel
->side
== PANEL_BOTTOM
)
1345 req
->width
= screen_width
;
1346 req
->height
+= EDGE_WIDTH
;
1350 int h
= screen_height
;
1352 if (current_panel
[PANEL_TOP
])
1354 GtkWidget
*win
= current_panel
[PANEL_TOP
]->window
;
1355 h
-= win
->allocation
.height
;
1358 if (current_panel
[PANEL_BOTTOM
])
1360 GtkWidget
*win
= current_panel
[PANEL_BOTTOM
]->window
;
1361 h
-= win
->allocation
.height
;
1365 req
->width
+= EDGE_WIDTH
;
1369 /* The style setting has been changed -- update all panels */
1370 static void panel_set_style(void)
1372 if (o_panel_style
.has_changed
)
1378 for (i
= 0; i
< PANEL_NUMBER_OF_SIDES
; i
++)
1380 Panel
*panel
= current_panel
[i
];
1385 gtk_widget_queue_resize(panel
->window
);
1390 static gboolean
draw_panel_edge(GtkWidget
*widget
, GdkEventExpose
*event
,
1393 int x
, y
, width
, height
;
1395 if (panel
->side
== PANEL_TOP
|| panel
->side
== PANEL_BOTTOM
)
1397 width
= screen_width
;
1398 height
= EDGE_WIDTH
;
1401 if (panel
->side
== PANEL_BOTTOM
)
1404 y
= widget
->allocation
.height
- EDGE_WIDTH
;
1409 height
= screen_height
;
1412 if (panel
->side
== PANEL_RIGHT
)
1415 x
= widget
->allocation
.width
- EDGE_WIDTH
;
1418 gdk_draw_rectangle(widget
->window
,
1419 widget
->style
->fg_gc
[GTK_STATE_NORMAL
], TRUE
,
1420 x
, y
, width
, height
);