r1219: Internal changes to the options system. See text at start of options.c.
[rox-filer.git] / ROX-Filer / src / panel.c
blobe92d196484f5eb447e2157e7e067af6490213de8
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 <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 Panel *current_panel[PANEL_NUMBER_OF_SIDES];
56 /* NULL => Not loading a panel */
57 static Panel *loading_panel = NULL;
59 /* Static prototypes */
60 static int panel_delete(GtkWidget *widget, GdkEvent *event, Panel *panel);
61 static void panel_destroyed(GtkWidget *widget, Panel *panel);
62 static char *pan_from_file(guchar *line);
63 static gint icon_button_release(GtkWidget *widget,
64 GdkEventButton *event,
65 Icon *icon);
66 static gint icon_button_press(GtkWidget *widget,
67 GdkEventButton *event,
68 Icon *icon);
69 static void reposition_panel(GtkWidget *window,
70 GtkAllocation *alloc, Panel *panel);
71 static gint expose_icon(GtkWidget *widget,
72 GdkEventExpose *event,
73 Icon *icon);
74 static gint draw_icon(GtkWidget *widget,
75 GdkRectangle *badarea,
76 Icon *icon);
77 static gint panel_button_release(GtkWidget *widget,
78 GdkEventButton *event,
79 Panel *panel);
80 static gint panel_button_press(GtkWidget *widget,
81 GdkEventButton *event,
82 Panel *panel);
83 static void panel_post_resize(GtkWidget *box,
84 GtkRequisition *req, Panel *panel);
85 static void drag_set_panel_dest(Icon *icon);
86 static void add_uri_list(GtkWidget *widget,
87 GdkDragContext *context,
88 gint x,
89 gint y,
90 GtkSelectionData *selection_data,
91 guint info,
92 guint32 time,
93 Panel *panel);
94 static void panel_add_item(Panel *panel,
95 guchar *path,
96 guchar *name,
97 gboolean after);
98 static gboolean drag_motion(GtkWidget *widget,
99 GdkDragContext *context,
100 gint x,
101 gint y,
102 guint time,
103 Icon *icon);
104 static void drag_leave(GtkWidget *widget,
105 GdkDragContext *context,
106 guint32 time,
107 Icon *icon);
108 static GtkWidget *make_insert_frame(Panel *panel);
109 static gboolean enter_icon(GtkWidget *widget,
110 GdkEventCrossing *event,
111 Icon *icon);
112 static gint icon_motion_event(GtkWidget *widget,
113 GdkEventMotion *event,
114 Icon *icon);
115 static gint panel_motion_event(GtkWidget *widget,
116 GdkEventMotion *event,
117 Panel *panel);
118 static void reposition_icon(Icon *icon, int index);
119 static void start_drag(Icon *icon, GdkEventMotion *event);
120 static guchar *create_uri_list(GList *list);
121 static void drag_end(GtkWidget *widget,
122 GdkDragContext *context,
123 Icon *icon);
124 static void perform_action(Panel *panel,
125 Icon *icon,
126 GdkEventButton *event);
127 static void run_applet(Icon *icon);
128 static void panel_set_style(void);
129 static void size_request(GtkWidget *widget, GtkRequisition *req, Icon *icon);
130 static void panel_load_from_xml(Panel *panel, xmlDocPtr doc);
133 static GtkWidget *dnd_highlight = NULL; /* (stops flickering) */
135 /* When sliding the panel, records where the panel was before */
136 static gint slide_from_value = 0;
138 #define SHOW_BOTH 0
139 #define SHOW_APPS_SMALL 1
140 #define SHOW_ICON 2
141 static Option o_panel_style;
143 static int closing_panel = 0; /* Don't panel_save; destroying! */
145 /****************************************************************
146 * EXTERNAL INTERFACE *
147 ****************************************************************/
149 void panel_init(void)
151 option_add_int(&o_panel_style, "panel_style", SHOW_APPS_SMALL);
153 option_add_notify(panel_set_style);
156 /* 'name' may be NULL or "" to remove the panel */
157 Panel *panel_new(guchar *name, PanelSide side)
159 guchar *load_path;
160 Panel *panel;
161 GtkWidget *vp, *box, *frame;
163 g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, NULL);
164 g_return_val_if_fail(loading_panel == NULL, NULL);
166 if (name && *name == '\0')
167 name = NULL;
169 if (current_panel[side])
171 if (name)
172 number_of_windows++;
173 closing_panel++;
174 gtk_widget_destroy(current_panel[side]->window);
175 closing_panel--;
176 if (name)
177 number_of_windows--;
180 if (name == NULL || *name == '\0')
181 return NULL;
183 panel = g_new(Panel, 1);
184 panel->name = g_strdup(name);
185 panel->side = side;
186 panel->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
187 gtk_window_set_policy(GTK_WINDOW(panel->window), FALSE, FALSE, TRUE);
188 gtk_window_set_wmclass(GTK_WINDOW(panel->window), "ROX-Panel", PROJECT);
189 gtk_widget_set_events(panel->window,
190 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
191 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
192 GDK_BUTTON2_MOTION_MASK);
194 gtk_signal_connect(GTK_OBJECT(panel->window), "delete-event",
195 GTK_SIGNAL_FUNC(panel_delete), panel);
196 gtk_signal_connect(GTK_OBJECT(panel->window), "destroy",
197 GTK_SIGNAL_FUNC(panel_destroyed), panel);
198 gtk_signal_connect(GTK_OBJECT(panel->window), "button_press_event",
199 GTK_SIGNAL_FUNC(panel_button_press), panel);
200 gtk_signal_connect(GTK_OBJECT(panel->window), "button_release_event",
201 GTK_SIGNAL_FUNC(panel_button_release), panel);
202 gtk_signal_connect(GTK_OBJECT(panel->window), "motion-notify-event",
203 GTK_SIGNAL_FUNC(panel_motion_event), panel);
205 if (strchr(name, '/'))
206 load_path = g_strdup(name);
207 else
209 guchar *leaf;
211 leaf = g_strconcat("pan_", name, NULL);
212 load_path = choices_find_path_load(leaf, PROJECT);
213 g_free(leaf);
216 vp = gtk_viewport_new(NULL, NULL);
217 gtk_container_set_resize_mode(GTK_CONTAINER(vp), GTK_RESIZE_PARENT);
218 gtk_viewport_set_shadow_type(GTK_VIEWPORT(vp), GTK_SHADOW_OUT);
219 gtk_container_add(GTK_CONTAINER(panel->window), vp);
221 if (side == PANEL_TOP || side == PANEL_BOTTOM)
223 panel->adj = gtk_viewport_get_hadjustment(GTK_VIEWPORT(vp));
224 box = gtk_hbox_new(FALSE, 0);
225 panel->before = gtk_hbox_new(FALSE, 0);
226 panel->after = gtk_hbox_new(FALSE, 0);
228 else
230 panel->adj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(vp));
231 box = gtk_vbox_new(FALSE, 0);
232 panel->before = gtk_vbox_new(FALSE, 0);
233 panel->after = gtk_vbox_new(FALSE, 0);
236 gtk_container_add(GTK_CONTAINER(vp), box);
237 gtk_box_pack_start(GTK_BOX(box), panel->before, FALSE, TRUE, 0);
238 gtk_box_pack_end(GTK_BOX(box), panel->after, FALSE, TRUE, 0);
240 frame = make_insert_frame(panel);
241 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
243 /* This is used so that we can find the middle easily! */
244 panel->gap = gtk_event_box_new();
245 gtk_box_pack_start(GTK_BOX(box), panel->gap, FALSE, FALSE, 0);
247 frame = make_insert_frame(panel);
248 gtk_object_set_data(GTK_OBJECT(frame), "after", "yes");
249 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
251 gtk_widget_realize(panel->window);
252 make_panel_window(panel->window);
254 gtk_widget_show_all(vp);
256 loading_panel = panel;
257 if (load_path && access(load_path, F_OK) == 0)
259 xmlDocPtr doc;
260 doc = xmlParseFile(load_path);
261 if (doc)
263 panel_load_from_xml(panel, doc);
264 xmlFreeDoc(doc);
266 else
268 parse_file(load_path, pan_from_file);
269 delayed_error(_("Your old panel file has been "
270 "converted to the new XML format."));
271 panel_save(panel);
274 else
276 /* Don't scare users with an empty panel... */
277 guchar *apps;
279 panel_add_item(panel, "~", "Home", FALSE);
281 apps = pathdup(make_path(app_dir, "..")->str);
282 if (apps)
284 panel_add_item(panel, apps, "Apps", FALSE);
285 g_free(apps);
288 loading_panel = NULL;
289 g_free(load_path);
291 current_panel[side] = panel;
293 gtk_widget_queue_resize(box);
294 gtk_signal_connect_after(GTK_OBJECT(panel->window), "size-request",
295 GTK_SIGNAL_FUNC(panel_post_resize), (GtkObject *) panel);
296 gtk_signal_connect_after(GTK_OBJECT(panel->window), "size-allocate",
297 GTK_SIGNAL_FUNC(reposition_panel), (GtkObject *) panel);
299 number_of_windows++;
300 gtk_widget_show(panel->window);
302 return panel;
305 gboolean panel_want_show_text(Icon *icon)
307 if (o_panel_style.int_value == SHOW_BOTH)
308 return TRUE;
309 if (o_panel_style.int_value == SHOW_ICON)
310 return FALSE;
312 if (icon->item->flags & ITEM_FLAG_APPDIR)
313 return FALSE;
315 return TRUE;
318 void panel_icon_renamed(Icon *icon)
320 GtkLabel *label = GTK_LABEL(icon->label);
322 gtk_label_set_text(label, icon->item->leafname);
326 /****************************************************************
327 * INTERNAL FUNCTIONS *
328 ****************************************************************/
330 /* User has tried to close the panel via the window manager - confirm */
331 static int panel_delete(GtkWidget *widget, GdkEvent *event, Panel *panel)
333 return get_choice(_("Close panel?"),
334 _("You have tried to close a panel via the window "
335 "manager - I usually find that this is accidental... "
336 "really close?"),
337 2, _("Cancel"), _("Remove")) != 1;
340 static void panel_destroyed(GtkWidget *widget, Panel *panel)
342 if (current_panel[panel->side] == panel)
343 current_panel[panel->side] = NULL;
345 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
347 if (current_panel[PANEL_RIGHT])
348 gtk_widget_queue_resize(
349 current_panel[PANEL_RIGHT]->window);
350 if (current_panel[PANEL_LEFT])
351 gtk_widget_queue_resize(
352 current_panel[PANEL_LEFT]->window);
355 g_free(panel->name);
356 g_free(panel);
358 if (--number_of_windows < 1)
359 gtk_main_quit();
362 static void panel_load_side(Panel *panel, xmlNodePtr side, gboolean after)
364 xmlNodePtr node;
365 char *label, *path;
367 for (node = side->xmlChildrenNode; node; node = node->next)
369 if (node->type != XML_ELEMENT_NODE)
370 continue;
371 if (strcmp(node->name, "icon") != 0)
372 continue;
374 label = xmlGetProp(node, "label");
375 if (!label)
376 label = g_strdup("<missing label>");
377 path = xmlNodeGetContent(node);
378 if (!path)
379 path = g_strdup("<missing path>");
381 #ifdef GTK2
382 panel_add_item(panel, path, label, after);
383 #else
385 gchar *loc_path, *loc_label;
387 loc_path = from_utf8(path);
388 loc_label = from_utf8(label);
389 panel_add_item(panel, loc_path, loc_label, after);
390 g_free(loc_label);
391 g_free(loc_path);
393 #endif
395 g_free(path);
396 g_free(label);
400 /* Create one panel icon for each icon in the doc */
401 static void panel_load_from_xml(Panel *panel, xmlDocPtr doc)
403 xmlNodePtr root;
405 root = xmlDocGetRootElement(doc);
406 panel_load_side(panel, get_subnode(root, NULL, "start"), FALSE);
407 panel_load_side(panel, get_subnode(root, NULL, "end"), TRUE);
410 /* Called for each line in the config file while loading a new panel */
411 static char *pan_from_file(guchar *line)
413 guchar *sep, *leaf;
415 g_return_val_if_fail(line != NULL, NULL);
416 g_return_val_if_fail(loading_panel != NULL, NULL);
418 if (*line == '\0')
419 return NULL;
421 sep = strpbrk(line, "<>");
422 if (!sep)
423 return _("Missing < or > in panel config file");
425 if (sep != line)
426 leaf = g_strndup(line, sep - line);
427 else
428 leaf = NULL;
430 panel_add_item(loading_panel, sep + 1, leaf, sep[0] == '>');
432 g_free(leaf);
434 return NULL;
437 /* Add an icon with this path to the panel. If after is TRUE then the
438 * icon is added to the right/bottom end of the panel.
440 * If name is NULL a suitable name is taken from path.
442 * name and path are in UTF-8 for Gtk+-2.0 only.
444 static void panel_add_item(Panel *panel,
445 guchar *path,
446 guchar *name,
447 gboolean after)
449 GtkWidget *widget;
450 Icon *icon;
452 widget = gtk_event_box_new();
453 gtk_widget_set_events(widget,
454 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
455 GDK_BUTTON3_MOTION_MASK |
456 GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
457 GDK_BUTTON_RELEASE_MASK);
459 gtk_box_pack_start(GTK_BOX(after ? panel->after : panel->before),
460 widget, FALSE, TRUE, 4);
461 if (after)
462 gtk_box_reorder_child(GTK_BOX(panel->after), widget, 0);
464 gtk_widget_realize(widget);
466 icon = g_new(Icon, 1);
467 icon->panel = panel;
468 icon->src_path = g_strdup(path);
469 icon->path = icon_convert_path(path);
470 icon->socket = NULL;
471 icon->label = NULL;
472 #ifdef GTK2
473 icon->layout = NULL;
474 #endif
476 gtk_object_set_data(GTK_OBJECT(widget), "icon", icon);
478 icon_hash_path(icon);
480 icon->widget = widget;
481 gtk_widget_set_name(icon->widget, "panel-icon");
482 icon->selected = FALSE;
484 if (name)
485 icon->item = diritem_new(name);
486 else
488 guchar *slash;
490 slash = strrchr(icon->path, '/');
491 icon->item = diritem_new(slash && slash[1] ? slash + 1
492 : path);
494 diritem_restat(icon->path, icon->item);
496 gtk_signal_connect_object(GTK_OBJECT(widget), "destroy",
497 GTK_SIGNAL_FUNC(icon_destroyed), (gpointer) icon);
499 if (icon->item->base_type == TYPE_DIRECTORY)
500 run_applet(icon);
502 gtk_signal_connect(GTK_OBJECT(widget), "button_release_event",
503 GTK_SIGNAL_FUNC(icon_button_release), icon);
504 gtk_signal_connect(GTK_OBJECT(widget), "button_press_event",
505 GTK_SIGNAL_FUNC(icon_button_press), icon);
506 gtk_signal_connect(GTK_OBJECT(icon->widget), "motion-notify-event",
507 GTK_SIGNAL_FUNC(icon_motion_event), icon);
509 if (!icon->socket)
511 gtk_signal_connect_after(GTK_OBJECT(widget),
512 "enter-notify-event",
513 GTK_SIGNAL_FUNC(enter_icon), icon);
514 #ifndef GTK2
515 gtk_signal_connect_after(GTK_OBJECT(widget), "draw",
516 GTK_SIGNAL_FUNC(draw_icon), icon);
517 #endif
518 gtk_signal_connect_after(GTK_OBJECT(widget), "expose_event",
519 GTK_SIGNAL_FUNC(expose_icon), icon);
520 gtk_signal_connect(GTK_OBJECT(widget), "drag_data_get",
521 GTK_SIGNAL_FUNC(drag_data_get), NULL);
523 gtk_signal_connect(GTK_OBJECT(widget), "size_request",
524 GTK_SIGNAL_FUNC(size_request), icon);
526 drag_set_panel_dest(icon);
528 icon->label = gtk_label_new(icon->item->leafname);
529 gtk_container_add(GTK_CONTAINER(icon->widget), icon->label);
530 gtk_misc_set_alignment(GTK_MISC(icon->label), 0.5, 1);
531 gtk_misc_set_padding(GTK_MISC(icon->label), 1, 2);
534 if (!loading_panel)
535 panel_save(panel);
537 icon_set_tip(icon);
538 gtk_widget_show(widget);
541 /* Called when Gtk+ wants to know how much space an icon needs.
542 * 'req' is already big enough for the label, if shown.
544 static void size_request(GtkWidget *widget, GtkRequisition *req, Icon *icon)
546 int im_width, im_height;
548 im_width = icon->item->image->width;
549 im_height = MIN(icon->item->image->height, ICON_HEIGHT);
551 req->height += im_height;
552 req->width = MAX(req->width, im_width);
555 static gint expose_icon(GtkWidget *widget,
556 GdkEventExpose *event,
557 Icon *icon)
559 return draw_icon(widget, &event->area, icon);
562 static gint draw_icon(GtkWidget *widget, GdkRectangle *badarea, Icon *icon)
564 GdkRectangle area;
565 int width, height;
567 gdk_window_get_size(widget->window, &width, &height);
569 area.x = 0;
570 area.width = width;
571 area.height = icon->item->image->height;
573 if (panel_want_show_text(icon))
575 int text_height = icon->label->requisition.height;
577 area.y = height - text_height - area.height;
579 draw_large_icon(widget, &area, icon->item,
580 icon->item->image, icon->selected);
582 else
584 area.y = (height - area.height) >> 1;
585 draw_large_icon(widget, &area, icon->item,
586 icon->item->image, icon->selected);
589 return FALSE;
592 /* icon may be NULL if the event is on the background */
593 static void perform_action(Panel *panel, Icon *icon, GdkEventButton *event)
595 BindAction action;
597 action = bind_lookup_bev(icon ? BIND_PANEL_ICON : BIND_PANEL, event);
599 if (icon && icon->socket)
600 if (action != ACT_POPUP_MENU && action != ACT_MOVE_ICON)
601 return;
603 switch (action)
605 case ACT_OPEN_ITEM:
606 dnd_motion_ungrab();
607 wink_widget(icon->widget);
608 run_diritem(icon->path, icon->item, NULL, NULL, FALSE);
609 break;
610 case ACT_EDIT_ITEM:
611 dnd_motion_ungrab();
612 wink_widget(icon->widget);
613 run_diritem(icon->path, icon->item, NULL, NULL, TRUE);
614 break;
615 case ACT_POPUP_MENU:
616 dnd_motion_ungrab();
617 icon_show_menu(event, icon, panel);
618 break;
619 case ACT_MOVE_ICON:
620 dnd_motion_start(MOTION_REPOSITION);
621 break;
622 case ACT_PRIME_AND_SELECT:
623 if (!icon->selected)
624 icon_select_only(icon);
625 dnd_motion_start(MOTION_READY_FOR_DND);
626 break;
627 case ACT_PRIME_AND_TOGGLE:
628 icon_set_selected(icon, !icon->selected);
629 dnd_motion_start(MOTION_READY_FOR_DND);
630 break;
631 case ACT_PRIME_FOR_DND:
632 dnd_motion_start(MOTION_READY_FOR_DND);
633 break;
634 case ACT_TOGGLE_SELECTED:
635 icon_set_selected(icon, !icon->selected);
636 break;
637 case ACT_SELECT_EXCL:
638 icon_set_selected(icon, TRUE);
639 break;
640 case ACT_SLIDE_CLEAR_PANEL:
641 icon_select_only(NULL);
642 /* (no break) */
643 case ACT_SLIDE_PANEL:
644 dnd_motion_grab_pointer();
645 slide_from_value = panel->adj->value;
646 dnd_motion_start(MOTION_REPOSITION);
647 break;
648 case ACT_IGNORE:
649 break;
650 case ACT_CLEAR_SELECTION:
651 icon_select_only(NULL);
652 break;
653 default:
654 g_warning("Unsupported action : %d\n", action);
655 break;
659 static gint panel_button_release(GtkWidget *widget,
660 GdkEventButton *event,
661 Panel *panel)
663 if (dnd_motion_release(event))
664 return TRUE;
666 perform_action(panel, NULL, event);
668 return TRUE;
671 static gint panel_button_press(GtkWidget *widget,
672 GdkEventButton *event,
673 Panel *panel)
675 if (dnd_motion_press(panel->window, event))
676 perform_action(panel, NULL, event);
678 return TRUE;
681 static gint icon_button_release(GtkWidget *widget,
682 GdkEventButton *event,
683 Icon *icon)
685 if (icon->socket && event->button == 1)
686 return FALSE; /* Restart button */
688 if (dnd_motion_release(event))
689 return TRUE;
691 perform_action(icon->panel, icon, event);
693 return TRUE;
696 static gint icon_button_press(GtkWidget *widget,
697 GdkEventButton *event,
698 Icon *icon)
700 if (icon->socket && event->button == 1)
701 return FALSE; /* Restart button */
703 if (dnd_motion_press(widget, event))
704 perform_action(icon->panel, icon, event);
706 return TRUE;
709 static void reposition_panel(GtkWidget *window,
710 GtkAllocation *alloc, Panel *panel)
712 int x = 0, y = 0;
713 PanelSide side = panel->side;
715 if (side == PANEL_LEFT || side == PANEL_RIGHT)
717 if (side == PANEL_RIGHT)
718 x = screen_width - alloc->width;
720 if (current_panel[PANEL_TOP])
722 GtkWidget *win = current_panel[PANEL_TOP]->window;
723 y += win->allocation.height;
727 if (side == PANEL_BOTTOM)
728 y = screen_height - alloc->height;
730 gtk_widget_set_uposition(panel->window, x, y);
731 gdk_window_move(panel->window->window, x, y);
733 if (side == PANEL_BOTTOM || side == PANEL_TOP)
735 if (current_panel[PANEL_RIGHT])
736 gtk_widget_queue_resize(
737 current_panel[PANEL_RIGHT]->window);
738 if (current_panel[PANEL_LEFT])
739 gtk_widget_queue_resize(
740 current_panel[PANEL_LEFT]->window);
744 /* Same as drag_set_dest(), but for panel icons */
745 static void drag_set_panel_dest(Icon *icon)
747 GtkObject *obj = GTK_OBJECT(icon->widget);
749 make_drop_target(icon->widget, 0);
751 gtk_signal_connect(obj, "drag_motion",
752 GTK_SIGNAL_FUNC(drag_motion), icon);
753 gtk_signal_connect(obj, "drag_leave",
754 GTK_SIGNAL_FUNC(drag_leave), icon);
755 gtk_signal_connect(obj, "drag_end",
756 GTK_SIGNAL_FUNC(drag_end), icon);
759 static gboolean drag_motion(GtkWidget *widget,
760 GdkDragContext *context,
761 gint x,
762 gint y,
763 guint time,
764 Icon *icon)
766 GdkDragAction action = context->suggested_action;
767 char *type = NULL;
768 DirItem *item = icon->item;
770 if (icon->selected)
771 goto out; /* Can't drag a selection to itself */
773 type = dnd_motion_item(context, &item);
775 if (!item)
776 type = NULL;
777 out:
778 /* We actually must pretend to accept the drop, even if the
779 * directory isn't writeable, so that the spring-opening
780 * thing works.
783 /* Don't allow drops to non-writeable directories */
784 if (o_dnd_spring_open.int_value == FALSE &&
785 type == drop_dest_dir &&
786 access(icon->path, W_OK) != 0)
788 type = NULL;
791 g_dataset_set_data(context, "drop_dest_type", type);
792 if (type)
794 gdk_drag_status(context, action, time);
795 g_dataset_set_data_full(context, "drop_dest_path",
796 g_strdup(icon->path), g_free);
797 if (type == drop_dest_dir)
798 dnd_spring_load(context, NULL);
800 if (dnd_highlight && dnd_highlight != icon->widget)
802 gtk_drag_unhighlight(dnd_highlight);
803 dnd_highlight = NULL;
806 if (dnd_highlight == NULL)
808 gtk_drag_highlight(icon->widget);
809 dnd_highlight = icon->widget;
813 return type != NULL;
817 static void add_uri_list(GtkWidget *widget,
818 GdkDragContext *context,
819 gint x,
820 gint y,
821 GtkSelectionData *selection_data,
822 guint info,
823 guint32 time,
824 Panel *panel)
826 gboolean after = FALSE;
827 GList *uris, *next;
829 if (!selection_data->data)
830 return;
832 g_return_if_fail(selection_data->data[selection_data->length] == '\0');
834 if (gtk_object_get_data(GTK_OBJECT(widget), "after"))
835 after = TRUE;
837 uris = uri_list_to_glist(selection_data->data);
839 for (next = uris; next; next = next->next)
841 guchar *path;
843 path = get_local_path((guchar *) next->data);
845 if (path)
846 panel_add_item(panel, path, NULL, after);
849 g_list_free(uris);
852 static void drag_end(GtkWidget *widget,
853 GdkDragContext *context,
854 Icon *icon)
856 if (tmp_icon_selected)
858 icon_select_only(NULL);
859 tmp_icon_selected = FALSE;
863 static void drag_leave(GtkWidget *widget,
864 GdkDragContext *context,
865 guint32 time,
866 Icon *icon)
868 if (dnd_highlight && dnd_highlight == widget)
870 gtk_drag_unhighlight(dnd_highlight);
871 dnd_highlight = NULL;
874 dnd_spring_abort();
877 /* Create XML icon nodes for these widgets.
878 * Always frees the widgets list.
880 static void make_widgets(xmlNodePtr side, GList *widgets)
882 GList *next;
884 for (next = widgets; next; next = next->next)
886 Icon *icon;
887 xmlNodePtr tree;
889 icon = gtk_object_get_data(GTK_OBJECT(next->data), "icon");
891 if (!icon)
893 g_warning("Can't find Icon from widget\n");
894 continue;
897 #ifdef GTK2
898 tree = xmlNewTextChild(side, NULL, "icon", icon->src_path);
899 #else
901 gchar *u8;
902 u8 = to_utf8(icon->src_path);
903 tree = xmlNewTextChild(side, NULL, "icon", u8);
904 g_free(u8);
906 #endif
908 #ifndef GTK2
910 gchar *u8;
911 u8 = to_utf8(icon->item->leafname);
912 xmlSetProp(tree, "label", u8);
913 g_free(u8);
915 #else
916 xmlSetProp(tree, "label", icon->item->leafname);
917 #endif
920 if (widgets)
921 g_list_free(widgets);
924 void panel_save(Panel *panel)
926 xmlDocPtr doc;
927 xmlNodePtr root;
928 guchar *save = NULL;
929 guchar *save_new = NULL;
931 g_return_if_fail(panel != NULL);
933 if (strchr(panel->name, '/'))
934 save = g_strdup(panel->name);
935 else
937 guchar *leaf;
939 leaf = g_strconcat("pan_", panel->name, NULL);
940 save = choices_find_path_save(leaf, PROJECT, TRUE);
941 g_free(leaf);
944 if (!save)
945 return;
947 doc = xmlNewDoc("1.0");
948 xmlDocSetRootElement(doc, xmlNewDocNode(doc, NULL, "pinboard", NULL));
950 root = xmlDocGetRootElement(doc);
951 make_widgets(xmlNewChild(root, NULL, "start", NULL),
952 gtk_container_children(GTK_CONTAINER(panel->before)));
954 make_widgets(xmlNewChild(root, NULL, "end", NULL),
955 g_list_reverse(gtk_container_children(
956 GTK_CONTAINER(panel->after))));
958 save_new = g_strconcat(save, ".new", NULL);
959 if (save_xml_file(doc, save_new) || rename(save_new, save))
960 delayed_error(_("Error saving panel %s: %s"),
961 save, g_strerror(errno));
962 g_free(save_new);
964 g_free(save);
965 if (doc)
966 xmlFreeDoc(doc);
969 /* Create a frame widget which can be used to add icons to the panel */
970 static GtkWidget *make_insert_frame(Panel *panel)
972 GtkWidget *frame;
973 GtkTargetEntry target_table[] = {
974 {"text/uri-list", 0, TARGET_URI_LIST},
977 frame = gtk_frame_new(NULL);
978 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
979 gtk_widget_set_usize(frame, 16, 16);
981 gtk_signal_connect(GTK_OBJECT(frame), "drag-data-received",
982 GTK_SIGNAL_FUNC(add_uri_list), panel);
983 gtk_drag_dest_set(frame,
984 GTK_DEST_DEFAULT_ALL,
985 target_table,
986 sizeof(target_table) / sizeof(*target_table),
987 GDK_ACTION_COPY);
989 return frame;
992 static gboolean enter_icon(GtkWidget *widget,
993 GdkEventCrossing *event,
994 Icon *icon)
996 icon_may_update(icon);
998 return FALSE;
1001 static gint panel_motion_event(GtkWidget *widget,
1002 GdkEventMotion *event,
1003 Panel *panel)
1005 gint delta, new;
1006 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1008 if (motion_state != MOTION_REPOSITION)
1009 return FALSE;
1011 if (horz)
1012 delta = event->x_root - drag_start_x;
1013 else
1014 delta = event->y_root - drag_start_y;
1016 new = slide_from_value - delta;
1017 new = CLAMP(new, 0, panel->adj->upper - panel->adj->page_size);
1019 gtk_adjustment_set_value(panel->adj, new);
1021 return TRUE;
1024 static gint icon_motion_event(GtkWidget *widget,
1025 GdkEventMotion *event,
1026 Icon *icon)
1028 Panel *panel = icon->panel;
1029 GList *list, *me;
1030 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1031 int val;
1032 int dir = 0;
1034 if (motion_state == MOTION_READY_FOR_DND)
1036 if (dnd_motion_moved(event))
1037 start_drag(icon, event);
1038 return TRUE;
1040 else if (motion_state != MOTION_REPOSITION)
1041 return FALSE;
1043 list = gtk_container_children(GTK_CONTAINER(panel->before));
1044 list = g_list_append(list, NULL); /* The gap in the middle */
1045 list = g_list_concat(list,
1046 gtk_container_children(GTK_CONTAINER(panel->after)));
1047 me = g_list_find(list, widget);
1049 g_return_val_if_fail(me != NULL, TRUE);
1051 val = horz ? event->x_root : event->y_root;
1053 if (me->prev)
1055 GtkWidget *prev;
1056 int x, y;
1058 if (me->prev->data)
1059 prev = GTK_WIDGET(me->prev->data);
1060 else
1061 prev = panel->gap;
1063 gdk_window_get_deskrelative_origin(prev->window, &x, &y);
1065 if (val <= (horz ? x : y))
1066 dir = -1;
1069 if (dir == 0 && me->next)
1071 GtkWidget *next;
1072 int x, y, w, h;
1074 if (me->next->data)
1075 next = GTK_WIDGET(me->next->data);
1076 else
1077 next = panel->gap;
1079 gdk_window_get_deskrelative_origin(next->window, &x, &y);
1081 gdk_window_get_size(next->window, &w, &h);
1083 x += w;
1084 y += h;
1086 if (val >= (horz ? x : y))
1088 if (next == panel->gap)
1089 dir = +2;
1090 else
1091 dir = +1;
1095 if (dir)
1096 reposition_icon(icon, g_list_index(list, widget) + dir);
1098 return TRUE;
1101 /* Move icon to this index in the complete widget list.
1102 * 0 makes the icon the left-most icon. The gap in the middle has
1103 * an index number, which allows you to specify that the icon should
1104 * go on the left or right side.
1106 static void reposition_icon(Icon *icon, int index)
1108 Panel *panel = icon->panel;
1109 GtkWidget *widget = icon->widget;
1110 GList *list;
1111 int before_len;
1113 list = gtk_container_children(GTK_CONTAINER(panel->before));
1114 before_len = g_list_length(list);
1116 if (index <= before_len)
1118 /* Want to move icon to the 'before' list. Is it there
1119 * already?
1122 if (!g_list_find(list, widget))
1124 /* No, reparent */
1125 gtk_grab_remove(widget);
1126 gtk_widget_reparent(widget, panel->before);
1127 dnd_motion_grab_pointer();
1128 gtk_grab_add(widget);
1131 gtk_box_reorder_child(GTK_BOX(panel->before), widget, index);
1133 else
1135 /* Else, we need it in the 'after' list. */
1137 index -= before_len + 1;
1139 g_list_free(list);
1141 list = gtk_container_children(GTK_CONTAINER(panel->after));
1143 if (!g_list_find(list, widget))
1145 /* Not already there, reparent */
1146 gtk_grab_remove(widget);
1147 gtk_widget_reparent(widget, panel->after);
1148 dnd_motion_grab_pointer();
1149 gtk_grab_add(widget);
1152 gtk_box_reorder_child(GTK_BOX(panel->after), widget, index);
1155 g_list_free(list);
1157 panel_save(panel);
1160 static void start_drag(Icon *icon, GdkEventMotion *event)
1162 GtkWidget *widget = icon->widget;
1164 if (!icon->selected)
1166 if (event->state & GDK_BUTTON1_MASK)
1168 /* Select just this one */
1169 icon_select_only(icon);
1170 tmp_icon_selected = TRUE;
1172 else
1173 icon_set_selected(icon, TRUE);
1176 g_return_if_fail(icon_selection != NULL);
1178 if (icon_selection->next == NULL)
1179 drag_one_item(widget, event, icon->path, icon->item, NULL);
1180 else
1182 guchar *uri_list;
1184 uri_list = create_uri_list(icon_selection);
1185 drag_selection(widget, event, uri_list);
1186 g_free(uri_list);
1190 /* Return a text/uri-list of all the icons in the list */
1191 static guchar *create_uri_list(GList *list)
1193 GString *tmp;
1194 guchar *retval;
1195 guchar *leader;
1197 tmp = g_string_new(NULL);
1198 leader = g_strdup_printf("file://%s", our_host_name_for_dnd());
1200 for (; list; list = list->next)
1202 Icon *icon = (Icon *) list->data;
1204 g_string_append(tmp, leader);
1205 g_string_append(tmp, icon->path);
1206 g_string_append(tmp, "\r\n");
1209 g_free(leader);
1210 retval = tmp->str;
1211 g_string_free(tmp, FALSE);
1213 return retval;
1216 static void applet_died(GtkWidget *socket)
1218 gboolean never_plugged;
1220 never_plugged = (!gtk_object_get_data(GTK_OBJECT(socket), "lost_plug"))
1221 && !GTK_SOCKET(socket)->plug_window;
1223 if (never_plugged)
1225 report_error(
1226 _("Applet quit without ever creating a widget!"));
1227 gtk_widget_destroy(socket);
1230 gtk_widget_unref(socket);
1233 static void socket_destroyed(GtkWidget *socket, GtkWidget *widget)
1235 gtk_object_set_data(GTK_OBJECT(socket), "lost_plug", "yes");
1237 gtk_widget_unref(socket);
1239 gtk_widget_destroy(widget); /* Remove from panel */
1241 if (!closing_panel)
1242 panel_save(gtk_object_get_data(GTK_OBJECT(socket), "panel"));
1245 /* Try to run this applet.
1246 * Cases:
1248 * - No executable AppletRun:
1249 * icon->socket == NULL (unchanged) on return.
1251 * Otherwise, create socket (setting icon->socket) and ref it twice.
1253 * - AppletRun quits without connecting a plug:
1254 * On child death lost_plug is unset and socket is empty.
1255 * Unref socket.
1256 * Report error and destroy widget (to 'socket destroyed').
1258 * - AppletRun quits while plug is in socket:
1259 * Unref socket once. Socket will be destroyed later.
1261 * - Socket is destroyed.
1262 * Set lost_plug = "yes" and remove widget from panel.
1263 * Unref socket.
1265 static void run_applet(Icon *icon)
1267 char *argv[3];
1268 pid_t pid;
1270 argv[0] = make_path(icon->path, "AppletRun")->str;
1272 if (access(argv[0], X_OK) != 0)
1273 return;
1275 icon->socket = gtk_socket_new();
1276 /* Two refs held: one for child death, one for socket destroyed */
1277 gtk_widget_ref(icon->socket);
1278 gtk_widget_ref(icon->socket);
1280 gtk_container_add(GTK_CONTAINER(icon->widget), icon->socket);
1281 gtk_widget_show_all(icon->socket);
1282 gtk_widget_realize(icon->socket);
1285 gchar *pos;
1286 PanelSide side = icon->panel->side;
1288 /* Set a hint to let applets position their menus correctly */
1289 pos = g_strdup_printf("%s,%d",
1290 side == PANEL_TOP ? "Top" :
1291 side == PANEL_BOTTOM ? "Bottom" :
1292 side == PANEL_LEFT ? "Left" :
1293 "Right", MENU_MARGIN);
1294 gdk_property_change(icon->socket->window,
1295 gdk_atom_intern("_ROX_PANEL_MENU_POS", FALSE),
1296 gdk_atom_intern("STRING", FALSE),
1297 8, GDK_PROP_MODE_REPLACE,
1298 pos, strlen(pos));
1299 g_free(pos);
1302 gtk_object_set_data(GTK_OBJECT(icon->widget), "icon", icon);
1303 gtk_object_set_data(GTK_OBJECT(icon->socket), "panel", icon->panel);
1305 gtk_signal_connect(GTK_OBJECT(icon->socket), "destroy",
1306 GTK_SIGNAL_FUNC(socket_destroyed), icon->widget);
1308 argv[1] = g_strdup_printf("%ld",
1309 GDK_WINDOW_XWINDOW(icon->socket->window));
1310 argv[2] = NULL;
1312 pid = spawn_full(argv, NULL, NULL);
1314 on_child_death(pid, (CallbackFn) applet_died, icon->socket);
1316 g_free(argv[1]);
1319 static void panel_post_resize(GtkWidget *win, GtkRequisition *req, Panel *panel)
1321 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
1322 req->width = screen_width;
1323 else
1325 int h = screen_height;
1327 if (current_panel[PANEL_TOP])
1329 GtkWidget *win = current_panel[PANEL_TOP]->window;
1330 h -= win->allocation.height;
1333 if (current_panel[PANEL_BOTTOM])
1335 GtkWidget *win = current_panel[PANEL_BOTTOM]->window;
1336 h -= win->allocation.height;
1339 req->height = h;
1343 /* The style setting has been changed -- update all panels */
1344 static void panel_set_style(void)
1346 if (o_panel_style.has_changed)
1348 int i;
1350 icons_update_tip();
1352 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
1354 Panel *panel = current_panel[i];
1356 if (!panel)
1357 continue;
1359 gtk_widget_queue_resize(panel->window);