r1298: Panel icons highlight when the pointer is over them.
[rox-filer.git] / ROX-Filer / src / panel.c
blob96d41625b21ce9d198d46f2a16db34e21797b6bf
1 /*
2 * $Id$
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)
10 * any later version.
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
15 * more details.
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 */
24 #include "config.h"
26 #undef GTK_DISABLE_DEPRECATED
28 #include <stdio.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <ctype.h>
32 #include <unistd.h>
33 #include <libxml/parser.h>
35 #include <gtk/gtk.h>
36 #include <gdk/gdkx.h>
38 #include "global.h"
40 #include "panel.h"
41 #include "options.h"
42 #include "choices.h"
43 #include "main.h"
44 #include "type.h"
45 #include "gui_support.h"
46 #include "diritem.h"
47 #include "pixmaps.h"
48 #include "display.h"
49 #include "bind.h"
50 #include "dnd.h"
51 #include "support.h"
52 #include "filer.h"
53 #include "icon.h"
54 #include "run.h"
56 /* The width of the separator at the inner edge of the panel */
57 #define EDGE_WIDTH 2
59 /* The gap between panel icons */
60 #define PANEL_ICON_SPACING 8
62 Panel *current_panel[PANEL_NUMBER_OF_SIDES];
64 /* NULL => Not loading a panel */
65 static Panel *loading_panel = NULL;
67 /* Static prototypes */
68 static int panel_delete(GtkWidget *widget, GdkEvent *event, Panel *panel);
69 static void panel_destroyed(GtkWidget *widget, Panel *panel);
70 static const char *pan_from_file(gchar *line);
71 static gint icon_button_release(GtkWidget *widget,
72 GdkEventButton *event,
73 Icon *icon);
74 static gint icon_button_press(GtkWidget *widget,
75 GdkEventButton *event,
76 Icon *icon);
77 static void reposition_panel(GtkWidget *window,
78 GtkAllocation *alloc, Panel *panel);
79 static gint expose_icon(GtkWidget *widget,
80 GdkEventExpose *event,
81 Icon *icon);
82 static gint draw_icon(GtkWidget *widget,
83 GdkRectangle *badarea,
84 Icon *icon);
85 static gint panel_button_release(GtkWidget *widget,
86 GdkEventButton *event,
87 Panel *panel);
88 static gint panel_button_press(GtkWidget *widget,
89 GdkEventButton *event,
90 Panel *panel);
91 static void panel_post_resize(GtkWidget *box,
92 GtkRequisition *req, Panel *panel);
93 static void drag_set_panel_dest(Icon *icon);
94 static void add_uri_list(GtkWidget *widget,
95 GdkDragContext *context,
96 gint x,
97 gint y,
98 GtkSelectionData *selection_data,
99 guint info,
100 guint32 time,
101 Panel *panel);
102 static void panel_add_item(Panel *panel,
103 const guchar *path,
104 const guchar *name,
105 gboolean after);
106 static gboolean drag_motion(GtkWidget *widget,
107 GdkDragContext *context,
108 gint x,
109 gint y,
110 guint time,
111 Icon *icon);
112 static void drag_leave(GtkWidget *widget,
113 GdkDragContext *context,
114 guint32 time,
115 Icon *icon);
116 static GtkWidget *make_insert_frame(Panel *panel);
117 static gboolean enter_icon(GtkWidget *widget,
118 GdkEventCrossing *event,
119 Icon *icon);
120 static gint icon_motion_event(GtkWidget *widget,
121 GdkEventMotion *event,
122 Icon *icon);
123 static gint panel_motion_event(GtkWidget *widget,
124 GdkEventMotion *event,
125 Panel *panel);
126 static void reposition_icon(Icon *icon, int index);
127 static void start_drag(Icon *icon, GdkEventMotion *event);
128 static guchar *create_uri_list(GList *list);
129 static void drag_end(GtkWidget *widget,
130 GdkDragContext *context,
131 Icon *icon);
132 static void perform_action(Panel *panel,
133 Icon *icon,
134 GdkEventButton *event);
135 static void run_applet(Icon *icon);
136 static void panel_set_style(void);
137 static void size_request(GtkWidget *widget, GtkRequisition *req, Icon *icon);
138 static void panel_load_from_xml(Panel *panel, xmlDocPtr doc);
139 static gboolean draw_panel_edge(GtkWidget *widget, GdkEventExpose *event,
140 Panel *panel);
143 static GtkWidget *dnd_highlight = NULL; /* (stops flickering) */
145 /* When sliding the panel, records where the panel was before */
146 static gint slide_from_value = 0;
148 #define SHOW_BOTH 0
149 #define SHOW_APPS_SMALL 1
150 #define SHOW_ICON 2
151 static Option o_panel_style;
153 static int closing_panel = 0; /* Don't panel_save; destroying! */
155 /****************************************************************
156 * EXTERNAL INTERFACE *
157 ****************************************************************/
159 void panel_init(void)
161 option_add_int(&o_panel_style, "panel_style", SHOW_APPS_SMALL);
163 option_add_notify(panel_set_style);
166 /* 'name' may be NULL or "" to remove the panel */
167 Panel *panel_new(const gchar *name, PanelSide side)
169 guchar *load_path;
170 Panel *panel;
171 GtkWidget *vp, *box, *frame, *align;
173 g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, NULL);
174 g_return_val_if_fail(loading_panel == NULL, NULL);
176 if (name && *name == '\0')
177 name = NULL;
179 if (current_panel[side])
181 if (name)
182 number_of_windows++;
183 closing_panel++;
184 gtk_widget_destroy(current_panel[side]->window);
185 closing_panel--;
186 if (name)
187 number_of_windows--;
190 if (name == NULL || *name == '\0')
191 return NULL;
193 panel = g_new(Panel, 1);
194 panel->name = g_strdup(name);
195 panel->side = side;
196 panel->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
197 gtk_window_set_resizable(GTK_WINDOW(panel->window), FALSE);
198 gtk_window_set_wmclass(GTK_WINDOW(panel->window), "ROX-Panel", PROJECT);
199 gtk_widget_set_events(panel->window,
200 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
201 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
202 GDK_BUTTON2_MOTION_MASK);
204 g_signal_connect(panel->window, "delete-event",
205 G_CALLBACK(panel_delete), panel);
206 g_signal_connect(panel->window, "destroy",
207 G_CALLBACK(panel_destroyed), panel);
208 g_signal_connect(panel->window, "button_press_event",
209 G_CALLBACK(panel_button_press), panel);
210 g_signal_connect(panel->window, "button_release_event",
211 G_CALLBACK(panel_button_release), panel);
212 g_signal_connect(panel->window, "motion-notify-event",
213 G_CALLBACK(panel_motion_event), panel);
215 if (strchr(name, '/'))
216 load_path = g_strdup(name);
217 else
219 guchar *leaf;
221 leaf = g_strconcat("pan_", name, NULL);
222 load_path = choices_find_path_load(leaf, PROJECT);
223 g_free(leaf);
226 if (panel->side == PANEL_RIGHT)
227 align = gtk_alignment_new(1.0, 0.0, 0.0, 1.0);
228 else if (panel->side == PANEL_BOTTOM)
229 align = gtk_alignment_new(0.0, 1.0, 1.0, 0.0);
230 else if (panel->side == PANEL_TOP)
231 align = gtk_alignment_new(0.0, 0.0, 1.0, 0.0);
232 else
233 align = gtk_alignment_new(0.0, 0.0, 0.0, 1.0);
235 gtk_container_add(GTK_CONTAINER(panel->window), align);
237 vp = gtk_viewport_new(NULL, NULL);
238 gtk_container_set_resize_mode(GTK_CONTAINER(vp), GTK_RESIZE_PARENT);
239 gtk_viewport_set_shadow_type(GTK_VIEWPORT(vp), GTK_SHADOW_NONE);
240 gtk_container_add(GTK_CONTAINER(align), vp);
242 gtk_signal_connect(GTK_OBJECT(align), "expose-event",
243 GTK_SIGNAL_FUNC(draw_panel_edge), panel);
245 if (side == PANEL_TOP || side == PANEL_BOTTOM)
247 panel->adj = gtk_viewport_get_hadjustment(GTK_VIEWPORT(vp));
248 box = gtk_hbox_new(FALSE, 0);
249 panel->before = gtk_hbox_new(FALSE, 0);
250 panel->after = gtk_hbox_new(FALSE, 0);
252 else
254 panel->adj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(vp));
255 box = gtk_vbox_new(FALSE, 0);
256 panel->before = gtk_vbox_new(FALSE, 0);
257 panel->after = gtk_vbox_new(FALSE, 0);
260 gtk_container_add(GTK_CONTAINER(vp), box);
261 gtk_box_pack_start(GTK_BOX(box), panel->before, FALSE, TRUE, 0);
262 gtk_box_pack_end(GTK_BOX(box), panel->after, FALSE, TRUE, 0);
264 frame = make_insert_frame(panel);
265 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
267 /* This is used so that we can find the middle easily! */
268 panel->gap = gtk_event_box_new();
269 gtk_box_pack_start(GTK_BOX(box), panel->gap, FALSE, FALSE, 0);
271 frame = make_insert_frame(panel);
272 g_object_set_data(G_OBJECT(frame), "after", "yes");
273 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
275 gtk_widget_realize(panel->window);
276 make_panel_window(panel->window);
278 gtk_widget_show_all(align);
280 loading_panel = panel;
281 if (load_path && access(load_path, F_OK) == 0)
283 xmlDocPtr doc;
284 doc = xmlParseFile(load_path);
285 if (doc)
287 panel_load_from_xml(panel, doc);
288 xmlFreeDoc(doc);
290 else
292 parse_file(load_path, pan_from_file);
293 delayed_error(_("Your old panel file has been "
294 "converted to the new XML format."));
295 panel_save(panel);
298 else
300 /* Don't scare users with an empty panel... */
301 guchar *apps;
303 panel_add_item(panel, "~", "Home", FALSE);
305 apps = pathdup(make_path(app_dir, "..")->str);
306 if (apps)
308 panel_add_item(panel, apps, "Apps", FALSE);
309 g_free(apps);
312 loading_panel = NULL;
313 g_free(load_path);
315 current_panel[side] = panel;
317 gtk_widget_queue_resize(box);
318 g_signal_connect(panel->window, "size-request",
319 G_CALLBACK(panel_post_resize), panel);
320 g_signal_connect(panel->window, "size-allocate",
321 G_CALLBACK(reposition_panel), panel);
323 number_of_windows++;
324 gtk_widget_show(panel->window);
326 return panel;
329 gboolean panel_want_show_text(Icon *icon)
331 if (o_panel_style.int_value == SHOW_BOTH)
332 return TRUE;
333 if (o_panel_style.int_value == SHOW_ICON)
334 return FALSE;
336 if (icon->item->flags & ITEM_FLAG_APPDIR)
337 return FALSE;
339 return TRUE;
342 void panel_icon_renamed(Icon *icon)
344 GtkLabel *label = GTK_LABEL(icon->label);
346 gtk_label_set_text(label, icon->item->leafname);
349 /* Externally visible function to add an item to a panel */
350 gboolean panel_add(PanelSide side,
351 const gchar *path, const gchar *label, gboolean after)
353 g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, FALSE);
355 g_return_val_if_fail(current_panel[side] != NULL, FALSE);
357 panel_add_item(current_panel[side], path, label, after);
359 return TRUE;
362 /****************************************************************
363 * INTERNAL FUNCTIONS *
364 ****************************************************************/
366 /* User has tried to close the panel via the window manager - confirm */
367 static int panel_delete(GtkWidget *widget, GdkEvent *event, Panel *panel)
369 return get_choice(_("Close panel?"),
370 _("You have tried to close a panel via the window "
371 "manager - I usually find that this is accidental... "
372 "really close?"),
373 2, _("Cancel"), _("Remove")) != 1;
376 static void panel_destroyed(GtkWidget *widget, Panel *panel)
378 if (current_panel[panel->side] == panel)
379 current_panel[panel->side] = NULL;
381 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
383 if (current_panel[PANEL_RIGHT])
384 gtk_widget_queue_resize(
385 current_panel[PANEL_RIGHT]->window);
386 if (current_panel[PANEL_LEFT])
387 gtk_widget_queue_resize(
388 current_panel[PANEL_LEFT]->window);
391 g_free(panel->name);
392 g_free(panel);
394 if (--number_of_windows < 1)
395 gtk_main_quit();
398 static void panel_load_side(Panel *panel, xmlNodePtr side, gboolean after)
400 xmlNodePtr node;
401 char *label, *path;
403 for (node = side->xmlChildrenNode; node; node = node->next)
405 if (node->type != XML_ELEMENT_NODE)
406 continue;
407 if (strcmp(node->name, "icon") != 0)
408 continue;
410 label = xmlGetProp(node, "label");
411 if (!label)
412 label = g_strdup("<missing label>");
413 path = xmlNodeGetContent(node);
414 if (!path)
415 path = g_strdup("<missing path>");
417 panel_add_item(panel, path, label, after);
419 g_free(path);
420 g_free(label);
424 /* Create one panel icon for each icon in the doc */
425 static void panel_load_from_xml(Panel *panel, xmlDocPtr doc)
427 xmlNodePtr root;
429 root = xmlDocGetRootElement(doc);
430 panel_load_side(panel, get_subnode(root, NULL, "start"), FALSE);
431 panel_load_side(panel, get_subnode(root, NULL, "end"), TRUE);
434 /* Called for each line in the config file while loading a new panel */
435 static const char *pan_from_file(gchar *line)
437 gchar *sep, *leaf;
439 g_return_val_if_fail(line != NULL, NULL);
440 g_return_val_if_fail(loading_panel != NULL, NULL);
442 if (*line == '\0')
443 return NULL;
445 sep = strpbrk(line, "<>");
446 if (!sep)
447 return _("Missing < or > in panel config file");
449 if (sep != line)
450 leaf = g_strndup(line, sep - line);
451 else
452 leaf = NULL;
454 panel_add_item(loading_panel, sep + 1, leaf, sep[0] == '>');
456 g_free(leaf);
458 return NULL;
461 static gboolean icon_pointer_in(GtkWidget *widget,
462 GdkEventCrossing *event,
463 Icon *icon)
465 gtk_widget_set_state(widget, GTK_STATE_PRELIGHT);
467 return 0;
470 static gboolean icon_pointer_out(GtkWidget *widget,
471 GdkEventCrossing *event,
472 Icon *icon)
474 gtk_widget_set_state(widget, GTK_STATE_NORMAL);
476 return 0;
479 /* Add an icon with this path to the panel. If after is TRUE then the
480 * icon is added to the right/bottom end of the panel.
482 * If name is NULL a suitable name is taken from path.
484 static void panel_add_item(Panel *panel,
485 const guchar *path,
486 const guchar *name,
487 gboolean after)
489 GtkWidget *widget;
490 Icon *icon;
492 g_return_if_fail(panel != NULL);
493 g_return_if_fail(path != NULL);
495 widget = gtk_event_box_new();
496 gtk_widget_set_events(widget,
497 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
498 GDK_BUTTON3_MOTION_MASK |
499 GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
500 GDK_BUTTON_RELEASE_MASK);
502 gtk_box_pack_start(GTK_BOX(after ? panel->after : panel->before),
503 widget, FALSE, TRUE, 0);
504 if (after)
505 gtk_box_reorder_child(GTK_BOX(panel->after), widget, 0);
507 gtk_widget_realize(widget);
509 icon = g_new(Icon, 1);
510 icon->panel = panel;
511 icon->src_path = g_strdup(path);
512 icon->path = icon_convert_path(path);
513 icon->socket = NULL;
514 icon->label = NULL;
515 icon->layout = NULL;
517 gtk_object_set_data(GTK_OBJECT(widget), "icon", icon);
519 icon_hash_path(icon);
521 icon->widget = widget;
522 gtk_widget_set_name(icon->widget, "panel-icon");
523 icon->selected = FALSE;
525 if (name)
526 icon->item = diritem_new(name);
527 else
529 guchar *slash;
531 slash = strrchr(icon->path, '/');
532 icon->item = diritem_new(slash && slash[1] ? slash + 1
533 : path);
535 diritem_restat(icon->path, icon->item);
537 g_signal_connect_swapped(widget, "destroy",
538 G_CALLBACK(icon_destroyed), icon);
540 if (icon->item->base_type == TYPE_DIRECTORY)
541 run_applet(icon);
543 g_signal_connect(widget, "button_release_event",
544 G_CALLBACK(icon_button_release), icon);
545 g_signal_connect(widget, "button_press_event",
546 G_CALLBACK(icon_button_press), icon);
547 g_signal_connect(icon->widget, "motion-notify-event",
548 G_CALLBACK(icon_motion_event), icon);
549 g_signal_connect(icon->widget, "enter-notify-event",
550 G_CALLBACK(icon_pointer_in), icon);
551 g_signal_connect(icon->widget, "leave-notify-event",
552 G_CALLBACK(icon_pointer_out), icon);
554 if (!icon->socket)
556 gtk_signal_connect_after(GTK_OBJECT(widget),
557 "enter-notify-event",
558 GTK_SIGNAL_FUNC(enter_icon), icon);
559 gtk_signal_connect_after(GTK_OBJECT(widget), "expose_event",
560 GTK_SIGNAL_FUNC(expose_icon), icon);
561 gtk_signal_connect(GTK_OBJECT(widget), "drag_data_get",
562 GTK_SIGNAL_FUNC(drag_data_get), NULL);
564 gtk_signal_connect(GTK_OBJECT(widget), "size_request",
565 GTK_SIGNAL_FUNC(size_request), icon);
567 drag_set_panel_dest(icon);
569 icon->label = gtk_label_new(icon->item->leafname);
570 gtk_container_add(GTK_CONTAINER(icon->widget), icon->label);
571 gtk_misc_set_alignment(GTK_MISC(icon->label), 0.5, 1);
572 gtk_misc_set_padding(GTK_MISC(icon->label), 1, 2);
575 if (!loading_panel)
576 panel_save(panel);
578 icon_set_tip(icon);
579 gtk_widget_show(widget);
582 /* Called when Gtk+ wants to know how much space an icon needs.
583 * 'req' is already big enough for the label, if shown.
585 static void size_request(GtkWidget *widget, GtkRequisition *req, Icon *icon)
587 int im_width, im_height;
589 im_width = icon->item->image->width;
590 im_height = MIN(icon->item->image->height, ICON_HEIGHT);
592 req->height += im_height;
593 req->width = MAX(req->width, im_width);
595 if (icon->panel->side == PANEL_LEFT || icon->panel->side == PANEL_RIGHT)
596 req->height += PANEL_ICON_SPACING;
597 else
598 req->width += PANEL_ICON_SPACING;
601 static gint expose_icon(GtkWidget *widget,
602 GdkEventExpose *event,
603 Icon *icon)
605 return draw_icon(widget, &event->area, icon);
608 static gint draw_icon(GtkWidget *widget, GdkRectangle *badarea, Icon *icon)
610 GdkRectangle area;
611 int width, height;
613 gdk_window_get_size(widget->window, &width, &height);
615 area.x = 0;
616 area.width = width;
617 area.height = icon->item->image->height;
619 if (panel_want_show_text(icon))
621 int text_height = icon->label->requisition.height;
623 area.y = height - text_height - area.height;
625 draw_large_icon(widget, &area, icon->item,
626 icon->item->image, icon->selected);
628 else
630 area.y = (height - area.height) >> 1;
631 draw_large_icon(widget, &area, icon->item,
632 icon->item->image, icon->selected);
635 return FALSE;
638 /* icon may be NULL if the event is on the background */
639 static void perform_action(Panel *panel, Icon *icon, GdkEventButton *event)
641 BindAction action;
643 action = bind_lookup_bev(icon ? BIND_PANEL_ICON : BIND_PANEL, event);
645 if (icon && icon->socket)
646 if (action != ACT_POPUP_MENU && action != ACT_MOVE_ICON)
647 return;
649 switch (action)
651 case ACT_OPEN_ITEM:
652 dnd_motion_ungrab();
653 wink_widget(icon->widget);
654 run_diritem(icon->path, icon->item, NULL, NULL, FALSE);
655 break;
656 case ACT_EDIT_ITEM:
657 dnd_motion_ungrab();
658 wink_widget(icon->widget);
659 run_diritem(icon->path, icon->item, NULL, NULL, TRUE);
660 break;
661 case ACT_POPUP_MENU:
662 dnd_motion_ungrab();
663 icon_show_menu(event, icon, panel);
664 break;
665 case ACT_MOVE_ICON:
666 dnd_motion_start(MOTION_REPOSITION);
667 break;
668 case ACT_PRIME_AND_SELECT:
669 if (!icon->selected)
670 icon_select_only(icon);
671 dnd_motion_start(MOTION_READY_FOR_DND);
672 break;
673 case ACT_PRIME_AND_TOGGLE:
674 icon_set_selected(icon, !icon->selected);
675 dnd_motion_start(MOTION_READY_FOR_DND);
676 break;
677 case ACT_PRIME_FOR_DND:
678 dnd_motion_start(MOTION_READY_FOR_DND);
679 break;
680 case ACT_TOGGLE_SELECTED:
681 icon_set_selected(icon, !icon->selected);
682 break;
683 case ACT_SELECT_EXCL:
684 icon_set_selected(icon, TRUE);
685 break;
686 case ACT_SLIDE_CLEAR_PANEL:
687 icon_select_only(NULL);
688 /* (no break) */
689 case ACT_SLIDE_PANEL:
690 dnd_motion_grab_pointer();
691 slide_from_value = panel->adj->value;
692 dnd_motion_start(MOTION_REPOSITION);
693 break;
694 case ACT_IGNORE:
695 break;
696 case ACT_CLEAR_SELECTION:
697 icon_select_only(NULL);
698 break;
699 default:
700 g_warning("Unsupported action : %d\n", action);
701 break;
705 static gint panel_button_release(GtkWidget *widget,
706 GdkEventButton *event,
707 Panel *panel)
709 if (dnd_motion_release(event))
710 return TRUE;
712 perform_action(panel, NULL, event);
714 return TRUE;
717 static gint panel_button_press(GtkWidget *widget,
718 GdkEventButton *event,
719 Panel *panel)
721 if (dnd_motion_press(panel->window, event))
722 perform_action(panel, NULL, event);
724 return TRUE;
727 static gint icon_button_release(GtkWidget *widget,
728 GdkEventButton *event,
729 Icon *icon)
731 if (icon->socket && event->button == 1)
732 return FALSE; /* Restart button */
734 if (dnd_motion_release(event))
735 return TRUE;
737 perform_action(icon->panel, icon, event);
739 return TRUE;
742 static gint icon_button_press(GtkWidget *widget,
743 GdkEventButton *event,
744 Icon *icon)
746 if (icon->socket && event->button == 1)
747 return FALSE; /* Restart button */
749 if (dnd_motion_press(widget, event))
750 perform_action(icon->panel, icon, event);
752 return TRUE;
755 static void reposition_panel(GtkWidget *window,
756 GtkAllocation *alloc, Panel *panel)
758 int x = 0, y = 0;
759 PanelSide side = panel->side;
761 if (side == PANEL_LEFT || side == PANEL_RIGHT)
763 if (side == PANEL_RIGHT)
764 x = screen_width - alloc->width;
766 if (current_panel[PANEL_TOP])
768 GtkWidget *win = current_panel[PANEL_TOP]->window;
769 y += win->allocation.height;
773 if (side == PANEL_BOTTOM)
774 y = screen_height - alloc->height;
776 gtk_window_move(GTK_WINDOW(panel->window), x, y);
777 gdk_window_move(panel->window->window, x, y);
779 if (side == PANEL_BOTTOM || side == PANEL_TOP)
781 if (current_panel[PANEL_RIGHT])
782 gtk_widget_queue_resize(
783 current_panel[PANEL_RIGHT]->window);
784 if (current_panel[PANEL_LEFT])
785 gtk_widget_queue_resize(
786 current_panel[PANEL_LEFT]->window);
790 /* Same as drag_set_dest(), but for panel icons */
791 static void drag_set_panel_dest(Icon *icon)
793 GtkObject *obj = GTK_OBJECT(icon->widget);
795 make_drop_target(icon->widget, 0);
797 gtk_signal_connect(obj, "drag_motion",
798 GTK_SIGNAL_FUNC(drag_motion), icon);
799 gtk_signal_connect(obj, "drag_leave",
800 GTK_SIGNAL_FUNC(drag_leave), icon);
801 gtk_signal_connect(obj, "drag_end",
802 GTK_SIGNAL_FUNC(drag_end), icon);
805 static gboolean drag_motion(GtkWidget *widget,
806 GdkDragContext *context,
807 gint x,
808 gint y,
809 guint time,
810 Icon *icon)
812 GdkDragAction action = context->suggested_action;
813 char *type = NULL;
814 DirItem *item = icon->item;
816 if (icon->selected)
817 goto out; /* Can't drag a selection to itself */
819 type = dnd_motion_item(context, &item);
821 if (!item)
822 type = NULL;
823 out:
824 /* We actually must pretend to accept the drop, even if the
825 * directory isn't writeable, so that the spring-opening
826 * thing works.
829 /* Don't allow drops to non-writeable directories */
830 if (o_dnd_spring_open.int_value == FALSE &&
831 type == drop_dest_dir &&
832 access(icon->path, W_OK) != 0)
834 type = NULL;
837 g_dataset_set_data(context, "drop_dest_type", type);
838 if (type)
840 gdk_drag_status(context, action, time);
841 g_dataset_set_data_full(context, "drop_dest_path",
842 g_strdup(icon->path), g_free);
843 if (type == drop_dest_dir)
844 dnd_spring_load(context, NULL);
846 if (dnd_highlight && dnd_highlight != icon->widget)
848 gtk_drag_unhighlight(dnd_highlight);
849 dnd_highlight = NULL;
852 if (dnd_highlight == NULL)
854 gtk_drag_highlight(icon->widget);
855 dnd_highlight = icon->widget;
859 return type != NULL;
863 static void add_uri_list(GtkWidget *widget,
864 GdkDragContext *context,
865 gint x,
866 gint y,
867 GtkSelectionData *selection_data,
868 guint info,
869 guint32 time,
870 Panel *panel)
872 gboolean after = FALSE;
873 GList *uris, *next;
875 if (!selection_data->data)
876 return;
878 g_return_if_fail(selection_data->data[selection_data->length] == '\0');
880 if (g_object_get_data(G_OBJECT(widget), "after"))
881 after = TRUE;
883 uris = uri_list_to_glist(selection_data->data);
885 for (next = uris; next; next = next->next)
887 const guchar *path;
889 path = get_local_path((guchar *) next->data);
891 if (path)
892 panel_add_item(panel, path, NULL, after);
895 g_list_free(uris);
898 static void drag_end(GtkWidget *widget,
899 GdkDragContext *context,
900 Icon *icon)
902 if (tmp_icon_selected)
904 icon_select_only(NULL);
905 tmp_icon_selected = FALSE;
909 static void drag_leave(GtkWidget *widget,
910 GdkDragContext *context,
911 guint32 time,
912 Icon *icon)
914 if (dnd_highlight && dnd_highlight == widget)
916 gtk_drag_unhighlight(dnd_highlight);
917 dnd_highlight = NULL;
920 dnd_spring_abort();
923 /* Create XML icon nodes for these widgets.
924 * Always frees the widgets list.
926 static void make_widgets(xmlNodePtr side, GList *widgets)
928 GList *next;
930 for (next = widgets; next; next = next->next)
932 Icon *icon;
933 xmlNodePtr tree;
935 icon = g_object_get_data(G_OBJECT(next->data), "icon");
937 if (!icon)
939 g_warning("Can't find Icon from widget\n");
940 continue;
943 tree = xmlNewTextChild(side, NULL, "icon", icon->src_path);
945 xmlSetProp(tree, "label", icon->item->leafname);
948 if (widgets)
949 g_list_free(widgets);
952 void panel_save(Panel *panel)
954 xmlDocPtr doc;
955 xmlNodePtr root;
956 guchar *save = NULL;
957 guchar *save_new = NULL;
959 g_return_if_fail(panel != NULL);
961 if (strchr(panel->name, '/'))
962 save = g_strdup(panel->name);
963 else
965 guchar *leaf;
967 leaf = g_strconcat("pan_", panel->name, NULL);
968 save = choices_find_path_save(leaf, PROJECT, TRUE);
969 g_free(leaf);
972 if (!save)
973 return;
975 doc = xmlNewDoc("1.0");
976 xmlDocSetRootElement(doc, xmlNewDocNode(doc, NULL, "pinboard", NULL));
978 root = xmlDocGetRootElement(doc);
979 make_widgets(xmlNewChild(root, NULL, "start", NULL),
980 gtk_container_children(GTK_CONTAINER(panel->before)));
982 make_widgets(xmlNewChild(root, NULL, "end", NULL),
983 g_list_reverse(gtk_container_children(
984 GTK_CONTAINER(panel->after))));
986 save_new = g_strconcat(save, ".new", NULL);
987 if (save_xml_file(doc, save_new) || rename(save_new, save))
988 delayed_error(_("Error saving panel %s: %s"),
989 save, g_strerror(errno));
990 g_free(save_new);
992 g_free(save);
993 if (doc)
994 xmlFreeDoc(doc);
997 /* Create a frame widget which can be used to add icons to the panel */
998 static GtkWidget *make_insert_frame(Panel *panel)
1000 GtkWidget *frame;
1001 GtkTargetEntry target_table[] = {
1002 {"text/uri-list", 0, TARGET_URI_LIST},
1005 frame = gtk_frame_new(NULL);
1006 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
1007 gtk_widget_set_usize(frame, 16, 16);
1009 gtk_signal_connect(GTK_OBJECT(frame), "drag-data-received",
1010 GTK_SIGNAL_FUNC(add_uri_list), panel);
1011 gtk_drag_dest_set(frame,
1012 GTK_DEST_DEFAULT_ALL,
1013 target_table,
1014 sizeof(target_table) / sizeof(*target_table),
1015 GDK_ACTION_COPY);
1017 return frame;
1020 static gboolean enter_icon(GtkWidget *widget,
1021 GdkEventCrossing *event,
1022 Icon *icon)
1024 icon_may_update(icon);
1026 return FALSE;
1029 static gint panel_motion_event(GtkWidget *widget,
1030 GdkEventMotion *event,
1031 Panel *panel)
1033 gint delta, new;
1034 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1036 if (motion_state != MOTION_REPOSITION)
1037 return FALSE;
1039 if (horz)
1040 delta = event->x_root - drag_start_x;
1041 else
1042 delta = event->y_root - drag_start_y;
1044 new = slide_from_value - delta;
1045 new = CLAMP(new, 0, panel->adj->upper - panel->adj->page_size);
1047 gtk_adjustment_set_value(panel->adj, new);
1049 return TRUE;
1052 static gint icon_motion_event(GtkWidget *widget,
1053 GdkEventMotion *event,
1054 Icon *icon)
1056 Panel *panel = icon->panel;
1057 GList *list, *me;
1058 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1059 int val;
1060 int dir = 0;
1062 if (motion_state == MOTION_READY_FOR_DND)
1064 if (dnd_motion_moved(event))
1065 start_drag(icon, event);
1066 return TRUE;
1068 else if (motion_state != MOTION_REPOSITION)
1069 return FALSE;
1071 list = gtk_container_children(GTK_CONTAINER(panel->before));
1072 list = g_list_append(list, NULL); /* The gap in the middle */
1073 list = g_list_concat(list,
1074 gtk_container_children(GTK_CONTAINER(panel->after)));
1075 me = g_list_find(list, widget);
1077 g_return_val_if_fail(me != NULL, TRUE);
1079 val = horz ? event->x_root : event->y_root;
1081 if (me->prev)
1083 GtkWidget *prev;
1084 int x, y;
1086 if (me->prev->data)
1087 prev = GTK_WIDGET(me->prev->data);
1088 else
1089 prev = panel->gap;
1091 gdk_window_get_deskrelative_origin(prev->window, &x, &y);
1093 if (val <= (horz ? x : y))
1094 dir = -1;
1097 if (dir == 0 && me->next)
1099 GtkWidget *next;
1100 int x, y, w, h;
1102 if (me->next->data)
1103 next = GTK_WIDGET(me->next->data);
1104 else
1105 next = panel->gap;
1107 gdk_window_get_deskrelative_origin(next->window, &x, &y);
1109 gdk_window_get_size(next->window, &w, &h);
1111 x += w;
1112 y += h;
1114 if (val >= (horz ? x : y))
1116 if (next == panel->gap)
1117 dir = +2;
1118 else
1119 dir = +1;
1123 if (dir)
1124 reposition_icon(icon, g_list_index(list, widget) + dir);
1126 return TRUE;
1129 /* Move icon to this index in the complete widget list.
1130 * 0 makes the icon the left-most icon. The gap in the middle has
1131 * an index number, which allows you to specify that the icon should
1132 * go on the left or right side.
1134 static void reposition_icon(Icon *icon, int index)
1136 Panel *panel = icon->panel;
1137 GtkWidget *widget = icon->widget;
1138 GList *list;
1139 int before_len;
1141 list = gtk_container_children(GTK_CONTAINER(panel->before));
1142 before_len = g_list_length(list);
1144 if (index <= before_len)
1146 /* Want to move icon to the 'before' list. Is it there
1147 * already?
1150 if (!g_list_find(list, widget))
1152 /* No, reparent */
1153 gtk_grab_remove(widget);
1154 gtk_widget_reparent(widget, panel->before);
1155 dnd_motion_grab_pointer();
1156 gtk_grab_add(widget);
1159 gtk_box_reorder_child(GTK_BOX(panel->before), widget, index);
1161 else
1163 /* Else, we need it in the 'after' list. */
1165 index -= before_len + 1;
1167 g_list_free(list);
1169 list = gtk_container_children(GTK_CONTAINER(panel->after));
1171 if (!g_list_find(list, widget))
1173 /* Not already there, reparent */
1174 gtk_grab_remove(widget);
1175 gtk_widget_reparent(widget, panel->after);
1176 dnd_motion_grab_pointer();
1177 gtk_grab_add(widget);
1180 gtk_box_reorder_child(GTK_BOX(panel->after), widget, index);
1183 g_list_free(list);
1185 panel_save(panel);
1188 static void start_drag(Icon *icon, GdkEventMotion *event)
1190 GtkWidget *widget = icon->widget;
1192 if (!icon->selected)
1194 if (event->state & GDK_BUTTON1_MASK)
1196 /* Select just this one */
1197 icon_select_only(icon);
1198 tmp_icon_selected = TRUE;
1200 else
1201 icon_set_selected(icon, TRUE);
1204 g_return_if_fail(icon_selection != NULL);
1206 if (icon_selection->next == NULL)
1207 drag_one_item(widget, event, icon->path, icon->item, NULL);
1208 else
1210 guchar *uri_list;
1212 uri_list = create_uri_list(icon_selection);
1213 drag_selection(widget, event, uri_list);
1214 g_free(uri_list);
1218 /* Return a text/uri-list of all the icons in the list */
1219 static guchar *create_uri_list(GList *list)
1221 GString *tmp;
1222 guchar *retval;
1223 guchar *leader;
1225 tmp = g_string_new(NULL);
1226 leader = g_strdup_printf("file://%s", our_host_name_for_dnd());
1228 for (; list; list = list->next)
1230 Icon *icon = (Icon *) list->data;
1232 g_string_append(tmp, leader);
1233 g_string_append(tmp, icon->path);
1234 g_string_append(tmp, "\r\n");
1237 g_free(leader);
1238 retval = tmp->str;
1239 g_string_free(tmp, FALSE);
1241 return retval;
1244 static void applet_died(GtkWidget *socket)
1246 gboolean never_plugged;
1248 never_plugged = (!gtk_object_get_data(GTK_OBJECT(socket), "lost_plug"))
1249 && !GTK_SOCKET(socket)->plug_window;
1251 if (never_plugged)
1253 report_error(
1254 _("Applet quit without ever creating a widget!"));
1255 gtk_widget_destroy(socket);
1258 gtk_widget_unref(socket);
1261 static void socket_destroyed(GtkWidget *socket, GtkWidget *widget)
1263 gtk_object_set_data(GTK_OBJECT(socket), "lost_plug", "yes");
1265 gtk_widget_unref(socket);
1267 gtk_widget_destroy(widget); /* Remove from panel */
1269 if (!closing_panel)
1270 panel_save(gtk_object_get_data(GTK_OBJECT(socket), "panel"));
1273 /* Try to run this applet.
1274 * Cases:
1276 * - No executable AppletRun:
1277 * icon->socket == NULL (unchanged) on return.
1279 * Otherwise, create socket (setting icon->socket) and ref it twice.
1281 * - AppletRun quits without connecting a plug:
1282 * On child death lost_plug is unset and socket is empty.
1283 * Unref socket.
1284 * Report error and destroy widget (to 'socket destroyed').
1286 * - AppletRun quits while plug is in socket:
1287 * Unref socket once. Socket will be destroyed later.
1289 * - Socket is destroyed.
1290 * Set lost_plug = "yes" and remove widget from panel.
1291 * Unref socket.
1293 static void run_applet(Icon *icon)
1295 char *argv[3];
1296 pid_t pid;
1298 argv[0] = make_path(icon->path, "AppletRun")->str;
1300 if (access(argv[0], X_OK) != 0)
1301 return;
1303 icon->socket = gtk_socket_new();
1304 /* Two refs held: one for child death, one for socket destroyed */
1305 gtk_widget_ref(icon->socket);
1306 gtk_widget_ref(icon->socket);
1308 gtk_container_add(GTK_CONTAINER(icon->widget), icon->socket);
1309 gtk_widget_show_all(icon->socket);
1310 gtk_widget_realize(icon->socket);
1313 gchar *pos;
1314 PanelSide side = icon->panel->side;
1316 /* Set a hint to let applets position their menus correctly */
1317 pos = g_strdup_printf("%s,%d",
1318 side == PANEL_TOP ? "Top" :
1319 side == PANEL_BOTTOM ? "Bottom" :
1320 side == PANEL_LEFT ? "Left" :
1321 "Right", MENU_MARGIN);
1322 gdk_property_change(icon->socket->window,
1323 gdk_atom_intern("_ROX_PANEL_MENU_POS", FALSE),
1324 gdk_atom_intern("STRING", FALSE),
1325 8, GDK_PROP_MODE_REPLACE,
1326 pos, strlen(pos));
1327 g_free(pos);
1330 gtk_object_set_data(GTK_OBJECT(icon->widget), "icon", icon);
1331 gtk_object_set_data(GTK_OBJECT(icon->socket), "panel", icon->panel);
1333 gtk_signal_connect(GTK_OBJECT(icon->socket), "destroy",
1334 GTK_SIGNAL_FUNC(socket_destroyed), icon->widget);
1336 argv[1] = g_strdup_printf("%ld",
1337 GDK_WINDOW_XWINDOW(icon->socket->window));
1338 argv[2] = NULL;
1340 pid = spawn_full((const char **) argv, NULL, NULL);
1342 on_child_death(pid, (CallbackFn) applet_died, icon->socket);
1344 g_free(argv[1]);
1347 static void panel_post_resize(GtkWidget *win, GtkRequisition *req, Panel *panel)
1349 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
1351 req->width = screen_width;
1352 req->height += EDGE_WIDTH;
1354 else
1356 int h = screen_height;
1358 if (current_panel[PANEL_TOP])
1360 GtkWidget *win = current_panel[PANEL_TOP]->window;
1361 h -= win->allocation.height;
1364 if (current_panel[PANEL_BOTTOM])
1366 GtkWidget *win = current_panel[PANEL_BOTTOM]->window;
1367 h -= win->allocation.height;
1370 req->height = h;
1371 req->width += EDGE_WIDTH;
1375 /* The style setting has been changed -- update all panels */
1376 static void panel_set_style(void)
1378 if (o_panel_style.has_changed)
1380 int i;
1382 icons_update_tip();
1384 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
1386 Panel *panel = current_panel[i];
1388 if (!panel)
1389 continue;
1391 gtk_widget_queue_resize(panel->window);
1396 static gboolean draw_panel_edge(GtkWidget *widget, GdkEventExpose *event,
1397 Panel *panel)
1399 int x, y, width, height;
1401 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
1403 width = screen_width;
1404 height = EDGE_WIDTH;
1406 x = 0;
1407 if (panel->side == PANEL_BOTTOM)
1408 y = 0;
1409 else
1410 y = widget->allocation.height - EDGE_WIDTH;
1412 else
1414 width = EDGE_WIDTH;
1415 height = screen_height;
1417 y = 0;
1418 if (panel->side == PANEL_RIGHT)
1419 x = 0;
1420 else
1421 x = widget->allocation.width - EDGE_WIDTH;
1424 gdk_draw_rectangle(widget->window,
1425 widget->style->fg_gc[GTK_STATE_NORMAL], TRUE,
1426 x, y, width, height);
1428 return FALSE;