r1322: Converted MaskedPixmap to GObject.
[rox-filer.git] / ROX-Filer / src / panel.c
blob2616e1c92b58941d409ebecc9986295dd9e257ad
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 #include <stdio.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <ctype.h>
30 #include <unistd.h>
31 #include <libxml/parser.h>
33 #include <gtk/gtk.h>
34 #include <gdk/gdkx.h>
36 #include "global.h"
38 #include "panel.h"
39 #include "options.h"
40 #include "choices.h"
41 #include "main.h"
42 #include "type.h"
43 #include "gui_support.h"
44 #include "diritem.h"
45 #include "pixmaps.h"
46 #include "display.h"
47 #include "bind.h"
48 #include "dnd.h"
49 #include "support.h"
50 #include "filer.h"
51 #include "icon.h"
52 #include "run.h"
54 /* The width of the separator at the inner edge of the panel */
55 #define EDGE_WIDTH 2
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,
71 Icon *icon);
72 static gint icon_button_press(GtkWidget *widget,
73 GdkEventButton *event,
74 Icon *icon);
75 static void reposition_panel(GtkWidget *window,
76 GtkAllocation *alloc, Panel *panel);
77 static gint expose_icon(GtkWidget *widget,
78 GdkEventExpose *event,
79 Icon *icon);
80 static gint draw_icon(GtkWidget *widget,
81 GdkRectangle *badarea,
82 Icon *icon);
83 static gint panel_button_release(GtkWidget *widget,
84 GdkEventButton *event,
85 Panel *panel);
86 static gint panel_button_press(GtkWidget *widget,
87 GdkEventButton *event,
88 Panel *panel);
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,
94 gint x,
95 gint y,
96 GtkSelectionData *selection_data,
97 guint info,
98 guint32 time,
99 Panel *panel);
100 static void panel_add_item(Panel *panel,
101 const guchar *path,
102 const guchar *name,
103 gboolean after);
104 static gboolean drag_motion(GtkWidget *widget,
105 GdkDragContext *context,
106 gint x,
107 gint y,
108 guint time,
109 Icon *icon);
110 static void drag_leave(GtkWidget *widget,
111 GdkDragContext *context,
112 guint32 time,
113 Icon *icon);
114 static GtkWidget *make_insert_frame(Panel *panel);
115 static gboolean enter_icon(GtkWidget *widget,
116 GdkEventCrossing *event,
117 Icon *icon);
118 static gint icon_motion_event(GtkWidget *widget,
119 GdkEventMotion *event,
120 Icon *icon);
121 static gint panel_motion_event(GtkWidget *widget,
122 GdkEventMotion *event,
123 Panel *panel);
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,
129 Icon *icon);
130 static void perform_action(Panel *panel,
131 Icon *icon,
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,
138 Panel *panel);
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;
146 #define SHOW_BOTH 0
147 #define SHOW_APPS_SMALL 1
148 #define SHOW_ICON 2
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)
167 guchar *load_path;
168 Panel *panel;
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')
175 name = NULL;
177 if (current_panel[side])
179 if (name)
180 number_of_windows++;
181 closing_panel++;
182 gtk_widget_destroy(current_panel[side]->window);
183 closing_panel--;
184 if (name)
185 number_of_windows--;
188 if (name == NULL || *name == '\0')
189 return NULL;
191 panel = g_new(Panel, 1);
192 panel->name = g_strdup(name);
193 panel->side = side;
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);
215 else
217 guchar *leaf;
219 leaf = g_strconcat("pan_", name, NULL);
220 load_path = choices_find_path_load(leaf, PROJECT);
221 g_free(leaf);
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);
230 else
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);
250 else
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)
281 xmlDocPtr doc;
282 doc = xmlParseFile(load_path);
283 if (doc)
285 panel_load_from_xml(panel, doc);
286 xmlFreeDoc(doc);
288 else
290 parse_file(load_path, pan_from_file);
291 delayed_error(_("Your old panel file has been "
292 "converted to the new XML format."));
293 panel_save(panel);
296 else
298 /* Don't scare users with an empty panel... */
299 guchar *apps;
301 panel_add_item(panel, "~", "Home", FALSE);
303 apps = pathdup(make_path(app_dir, "..")->str);
304 if (apps)
306 panel_add_item(panel, apps, "Apps", FALSE);
307 g_free(apps);
310 loading_panel = NULL;
311 g_free(load_path);
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);
321 number_of_windows++;
322 gtk_widget_show(panel->window);
324 return panel;
327 gboolean panel_want_show_text(Icon *icon)
329 if (o_panel_style.int_value == SHOW_BOTH)
330 return TRUE;
331 if (o_panel_style.int_value == SHOW_ICON)
332 return FALSE;
334 if (icon->item->flags & ITEM_FLAG_APPDIR)
335 return FALSE;
337 return TRUE;
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);
357 return TRUE;
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... "
370 "really close?"),
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);
389 g_free(panel->name);
390 g_free(panel);
392 if (--number_of_windows < 1)
393 gtk_main_quit();
396 static void panel_load_side(Panel *panel, xmlNodePtr side, gboolean after)
398 xmlNodePtr node;
399 char *label, *path;
401 for (node = side->xmlChildrenNode; node; node = node->next)
403 if (node->type != XML_ELEMENT_NODE)
404 continue;
405 if (strcmp(node->name, "icon") != 0)
406 continue;
408 label = xmlGetProp(node, "label");
409 if (!label)
410 label = g_strdup("<missing label>");
411 path = xmlNodeGetContent(node);
412 if (!path)
413 path = g_strdup("<missing path>");
415 panel_add_item(panel, path, label, after);
417 g_free(path);
418 g_free(label);
422 /* Create one panel icon for each icon in the doc */
423 static void panel_load_from_xml(Panel *panel, xmlDocPtr doc)
425 xmlNodePtr root;
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)
435 gchar *sep, *leaf;
437 g_return_val_if_fail(line != NULL, NULL);
438 g_return_val_if_fail(loading_panel != NULL, NULL);
440 if (*line == '\0')
441 return NULL;
443 sep = strpbrk(line, "<>");
444 if (!sep)
445 return _("Missing < or > in panel config file");
447 if (sep != line)
448 leaf = g_strndup(line, sep - line);
449 else
450 leaf = NULL;
452 panel_add_item(loading_panel, sep + 1, leaf, sep[0] == '>');
454 g_free(leaf);
456 return NULL;
459 static gboolean icon_pointer_in(GtkWidget *widget,
460 GdkEventCrossing *event,
461 Icon *icon)
463 gtk_widget_set_state(widget, GTK_STATE_PRELIGHT);
465 return 0;
468 static gboolean icon_pointer_out(GtkWidget *widget,
469 GdkEventCrossing *event,
470 Icon *icon)
472 gtk_widget_set_state(widget, GTK_STATE_NORMAL);
474 return 0;
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,
483 const guchar *path,
484 const guchar *name,
485 gboolean after)
487 GtkWidget *widget;
488 Icon *icon;
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);
502 if (after)
503 gtk_box_reorder_child(GTK_BOX(panel->after), widget, 0);
505 gtk_widget_realize(widget);
507 icon = g_new(Icon, 1);
508 icon->panel = panel;
509 icon->src_path = g_strdup(path);
510 icon->path = icon_convert_path(path);
511 icon->socket = NULL;
512 icon->label = NULL;
513 icon->layout = NULL;
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;
523 if (name)
524 icon->item = diritem_new(name);
525 else
527 guchar *slash;
529 slash = strrchr(icon->path, '/');
530 icon->item = diritem_new(slash && slash[1] ? slash + 1
531 : path);
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)
539 run_applet(icon);
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);
552 if (!icon->socket)
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);
572 if (!loading_panel)
573 panel_save(panel);
575 icon_set_tip(icon);
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;
594 else
595 req->width += PANEL_ICON_SPACING;
598 static gint expose_icon(GtkWidget *widget,
599 GdkEventExpose *event,
600 Icon *icon)
602 return draw_icon(widget, &event->area, icon);
605 static gint draw_icon(GtkWidget *widget, GdkRectangle *badarea, Icon *icon)
607 GdkRectangle area;
608 int width, height;
610 gdk_window_get_size(widget->window, &width, &height);
612 area.x = 0;
613 area.width = width;
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);
625 else
627 area.y = (height - area.height) >> 1;
628 draw_large_icon(widget, &area, icon->item,
629 icon->item->image, icon->selected);
632 return FALSE;
635 /* icon may be NULL if the event is on the background */
636 static void perform_action(Panel *panel, Icon *icon, GdkEventButton *event)
638 BindAction action;
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)
644 return;
646 switch (action)
648 case ACT_OPEN_ITEM:
649 dnd_motion_ungrab();
650 wink_widget(icon->widget);
651 run_diritem(icon->path, icon->item, NULL, NULL, FALSE);
652 break;
653 case ACT_EDIT_ITEM:
654 dnd_motion_ungrab();
655 wink_widget(icon->widget);
656 run_diritem(icon->path, icon->item, NULL, NULL, TRUE);
657 break;
658 case ACT_POPUP_MENU:
659 dnd_motion_ungrab();
660 icon_show_menu(event, icon, panel);
661 break;
662 case ACT_MOVE_ICON:
663 dnd_motion_start(MOTION_REPOSITION);
664 break;
665 case ACT_PRIME_AND_SELECT:
666 if (!icon->selected)
667 icon_select_only(icon);
668 dnd_motion_start(MOTION_READY_FOR_DND);
669 break;
670 case ACT_PRIME_AND_TOGGLE:
671 icon_set_selected(icon, !icon->selected);
672 dnd_motion_start(MOTION_READY_FOR_DND);
673 break;
674 case ACT_PRIME_FOR_DND:
675 dnd_motion_start(MOTION_READY_FOR_DND);
676 break;
677 case ACT_TOGGLE_SELECTED:
678 icon_set_selected(icon, !icon->selected);
679 break;
680 case ACT_SELECT_EXCL:
681 icon_set_selected(icon, TRUE);
682 break;
683 case ACT_SLIDE_CLEAR_PANEL:
684 icon_select_only(NULL);
685 /* (no break) */
686 case ACT_SLIDE_PANEL:
687 dnd_motion_grab_pointer();
688 slide_from_value = panel->adj->value;
689 dnd_motion_start(MOTION_REPOSITION);
690 break;
691 case ACT_IGNORE:
692 break;
693 case ACT_CLEAR_SELECTION:
694 icon_select_only(NULL);
695 break;
696 default:
697 g_warning("Unsupported action : %d\n", action);
698 break;
702 static gint panel_button_release(GtkWidget *widget,
703 GdkEventButton *event,
704 Panel *panel)
706 if (dnd_motion_release(event))
707 return TRUE;
709 perform_action(panel, NULL, event);
711 return TRUE;
714 static gint panel_button_press(GtkWidget *widget,
715 GdkEventButton *event,
716 Panel *panel)
718 if (dnd_motion_press(panel->window, event))
719 perform_action(panel, NULL, event);
721 return TRUE;
724 static gint icon_button_release(GtkWidget *widget,
725 GdkEventButton *event,
726 Icon *icon)
728 if (icon->socket && event->button == 1)
729 return FALSE; /* Restart button */
731 if (dnd_motion_release(event))
732 return TRUE;
734 perform_action(icon->panel, icon, event);
736 return TRUE;
739 static gint icon_button_press(GtkWidget *widget,
740 GdkEventButton *event,
741 Icon *icon)
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);
749 return TRUE;
752 static void reposition_panel(GtkWidget *window,
753 GtkAllocation *alloc, Panel *panel)
755 int x = 0, y = 0;
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,
801 gint x,
802 gint y,
803 guint time,
804 Icon *icon)
806 GdkDragAction action = context->suggested_action;
807 char *type = NULL;
808 DirItem *item = icon->item;
810 if (icon->selected)
811 goto out; /* Can't drag a selection to itself */
813 type = dnd_motion_item(context, &item);
815 if (!item)
816 type = NULL;
817 out:
818 /* We actually must pretend to accept the drop, even if the
819 * directory isn't writeable, so that the spring-opening
820 * thing works.
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)
828 type = NULL;
831 g_dataset_set_data(context, "drop_dest_type", type);
832 if (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;
853 return type != NULL;
857 static void add_uri_list(GtkWidget *widget,
858 GdkDragContext *context,
859 gint x,
860 gint y,
861 GtkSelectionData *selection_data,
862 guint info,
863 guint32 time,
864 Panel *panel)
866 gboolean after = FALSE;
867 GList *uris, *next;
869 if (!selection_data->data)
870 return;
872 g_return_if_fail(selection_data->data[selection_data->length] == '\0');
874 if (g_object_get_data(G_OBJECT(widget), "after"))
875 after = TRUE;
877 uris = uri_list_to_glist(selection_data->data);
879 for (next = uris; next; next = next->next)
881 const guchar *path;
883 path = get_local_path((guchar *) next->data);
885 if (path)
886 panel_add_item(panel, path, NULL, after);
889 g_list_free(uris);
892 static void drag_end(GtkWidget *widget,
893 GdkDragContext *context,
894 Icon *icon)
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,
905 guint32 time,
906 Icon *icon)
908 if (dnd_highlight && dnd_highlight == widget)
910 gtk_drag_unhighlight(dnd_highlight);
911 dnd_highlight = NULL;
914 dnd_spring_abort();
917 /* Create XML icon nodes for these widgets.
918 * Always frees the widgets list.
920 static void make_widgets(xmlNodePtr side, GList *widgets)
922 GList *next;
924 for (next = widgets; next; next = next->next)
926 Icon *icon;
927 xmlNodePtr tree;
929 icon = g_object_get_data(G_OBJECT(next->data), "icon");
931 if (!icon)
933 g_warning("Can't find Icon from widget\n");
934 continue;
937 tree = xmlNewTextChild(side, NULL, "icon", icon->src_path);
939 xmlSetProp(tree, "label", icon->item->leafname);
942 if (widgets)
943 g_list_free(widgets);
946 void panel_save(Panel *panel)
948 xmlDocPtr doc;
949 xmlNodePtr root;
950 guchar *save = NULL;
951 guchar *save_new = NULL;
953 g_return_if_fail(panel != NULL);
955 if (strchr(panel->name, '/'))
956 save = g_strdup(panel->name);
957 else
959 guchar *leaf;
961 leaf = g_strconcat("pan_", panel->name, NULL);
962 save = choices_find_path_save(leaf, PROJECT, TRUE);
963 g_free(leaf);
966 if (!save)
967 return;
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));
984 g_free(save_new);
986 g_free(save);
987 if (doc)
988 xmlFreeDoc(doc);
991 /* Create a frame widget which can be used to add icons to the panel */
992 static GtkWidget *make_insert_frame(Panel *panel)
994 GtkWidget *frame;
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,
1007 target_table,
1008 sizeof(target_table) / sizeof(*target_table),
1009 GDK_ACTION_COPY);
1011 return frame;
1014 static gboolean enter_icon(GtkWidget *widget,
1015 GdkEventCrossing *event,
1016 Icon *icon)
1018 icon_may_update(icon);
1020 return FALSE;
1023 static gint panel_motion_event(GtkWidget *widget,
1024 GdkEventMotion *event,
1025 Panel *panel)
1027 gint delta, new;
1028 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1030 if (motion_state != MOTION_REPOSITION)
1031 return FALSE;
1033 if (horz)
1034 delta = event->x_root - drag_start_x;
1035 else
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);
1043 return TRUE;
1046 static gint icon_motion_event(GtkWidget *widget,
1047 GdkEventMotion *event,
1048 Icon *icon)
1050 Panel *panel = icon->panel;
1051 GList *list, *me;
1052 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1053 int val;
1054 int dir = 0;
1056 if (motion_state == MOTION_READY_FOR_DND)
1058 if (dnd_motion_moved(event))
1059 start_drag(icon, event);
1060 return TRUE;
1062 else if (motion_state != MOTION_REPOSITION)
1063 return FALSE;
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;
1075 if (me->prev)
1077 GtkWidget *prev;
1078 int x, y;
1080 if (me->prev->data)
1081 prev = GTK_WIDGET(me->prev->data);
1082 else
1083 prev = panel->gap;
1085 gdk_window_get_deskrelative_origin(prev->window, &x, &y);
1087 if (val <= (horz ? x : y))
1088 dir = -1;
1091 if (dir == 0 && me->next)
1093 GtkWidget *next;
1094 int x, y, w, h;
1096 if (me->next->data)
1097 next = GTK_WIDGET(me->next->data);
1098 else
1099 next = panel->gap;
1101 gdk_window_get_deskrelative_origin(next->window, &x, &y);
1103 gdk_window_get_size(next->window, &w, &h);
1105 x += w;
1106 y += h;
1108 if (val >= (horz ? x : y))
1110 if (next == panel->gap)
1111 dir = +2;
1112 else
1113 dir = +1;
1117 if (dir)
1118 reposition_icon(icon, g_list_index(list, widget) + dir);
1120 return TRUE;
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;
1132 GList *list;
1133 int before_len;
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
1141 * already?
1144 if (!g_list_find(list, widget))
1146 /* No, reparent */
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);
1155 else
1157 /* Else, we need it in the 'after' list. */
1159 index -= before_len + 1;
1161 g_list_free(list);
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);
1177 g_list_free(list);
1179 panel_save(panel);
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;
1194 else
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);
1202 else
1204 guchar *uri_list;
1206 uri_list = create_uri_list(icon_selection);
1207 drag_selection(widget, event, uri_list);
1208 g_free(uri_list);
1212 /* Return a text/uri-list of all the icons in the list */
1213 static guchar *create_uri_list(GList *list)
1215 GString *tmp;
1216 guchar *retval;
1217 guchar *leader;
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");
1231 g_free(leader);
1232 retval = tmp->str;
1233 g_string_free(tmp, FALSE);
1235 return retval;
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;
1245 if (never_plugged)
1247 report_error(
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 */
1263 if (!closing_panel)
1264 panel_save(g_object_get_data(G_OBJECT(socket), "panel"));
1267 /* Try to run this applet.
1268 * Cases:
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.
1277 * Unref socket.
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.
1285 * Unref socket.
1287 static void run_applet(Icon *icon)
1289 char *argv[3];
1290 pid_t pid;
1292 argv[0] = make_path(icon->path, "AppletRun")->str;
1294 if (access(argv[0], X_OK) != 0)
1295 return;
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);
1307 gchar *pos;
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,
1320 pos, strlen(pos));
1321 g_free(pos);
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));
1332 argv[2] = NULL;
1334 pid = spawn_full((const char **) argv, NULL, NULL);
1336 on_child_death(pid, (CallbackFn) applet_died, icon->socket);
1338 g_free(argv[1]);
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;
1348 else
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;
1364 req->height = h;
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)
1374 int i;
1376 icons_update_tip();
1378 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
1380 Panel *panel = current_panel[i];
1382 if (!panel)
1383 continue;
1385 gtk_widget_queue_resize(panel->window);
1390 static gboolean draw_panel_edge(GtkWidget *widget, GdkEventExpose *event,
1391 Panel *panel)
1393 int x, y, width, height;
1395 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
1397 width = screen_width;
1398 height = EDGE_WIDTH;
1400 x = 0;
1401 if (panel->side == PANEL_BOTTOM)
1402 y = 0;
1403 else
1404 y = widget->allocation.height - EDGE_WIDTH;
1406 else
1408 width = EDGE_WIDTH;
1409 height = screen_height;
1411 y = 0;
1412 if (panel->side == PANEL_RIGHT)
1413 x = 0;
1414 else
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);
1422 return FALSE;