r936: If a panel applet quits then it is removed from the panel, instead of being
[rox-filer.git] / ROX-Filer / src / panel.c
blob0a119a0e1b821015fbe19648d3f630582fe84d3f
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2001, the ROX-Filer team.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
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>
32 #include <gtk/gtk.h>
33 #include <gdk/gdkx.h>
35 #include "global.h"
37 #include "panel.h"
38 #include "options.h"
39 #include "choices.h"
40 #include "main.h"
41 #include "type.h"
42 #include "gui_support.h"
43 #include "dir.h"
44 #include "pixmaps.h"
45 #include "display.h"
46 #include "bind.h"
47 #include "dnd.h"
48 #include "support.h"
49 #include "filer.h"
50 #include "icon.h"
51 #include "run.h"
53 Panel *current_panel[PANEL_NUMBER_OF_SIDES];
55 /* NULL => Not loading a panel */
56 static Panel *loading_panel = NULL;
58 /* Static prototypes */
59 static int panel_delete(GtkWidget *widget, GdkEvent *event, Panel *panel);
60 static void panel_destroyed(GtkWidget *widget, Panel *panel);
61 static char *pan_from_file(guchar *line);
62 static gint icon_button_release(GtkWidget *widget,
63 GdkEventButton *event,
64 Icon *icon);
65 static gint icon_button_press(GtkWidget *widget,
66 GdkEventButton *event,
67 Icon *icon);
68 static void reposition_panel(Panel *panel, gboolean force_resize);
69 static gint expose_icon(GtkWidget *widget,
70 GdkEventExpose *event,
71 Icon *icon);
72 static gint draw_icon(GtkWidget *widget,
73 GdkRectangle *badarea,
74 Icon *icon);
75 static gint panel_button_release(GtkWidget *widget,
76 GdkEventButton *event,
77 Panel *panel);
78 static gint panel_button_press(GtkWidget *widget,
79 GdkEventButton *event,
80 Panel *panel);
81 static void panel_post_resize(GtkWidget *box,
82 GtkRequisition *req, Panel *panel);
83 static void box_resized(GtkWidget *box, GtkAllocation *alloc, Panel *panel);
84 static void drag_set_panel_dest(Icon *icon);
85 static void add_uri_list(GtkWidget *widget,
86 GdkDragContext *context,
87 gint x,
88 gint y,
89 GtkSelectionData *selection_data,
90 guint info,
91 guint32 time,
92 Panel *panel);
93 static void panel_add_item(Panel *panel,
94 guchar *path,
95 guchar *name,
96 gboolean after);
97 static gboolean drag_motion(GtkWidget *widget,
98 GdkDragContext *context,
99 gint x,
100 gint y,
101 guint time,
102 Icon *icon);
103 static void drag_leave(GtkWidget *widget,
104 GdkDragContext *context,
105 guint32 time,
106 Icon *icon);
107 static GList *get_widget_list(Panel *panel);
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(guchar *new);
129 static void size_request(GtkWidget *widget, GtkRequisition *req, Icon *icon);
132 static GtkWidget *dnd_highlight = NULL; /* (stops flickering) */
134 /* When sliding the panel, records where the panel was before */
135 static gint slide_from_value = 0;
137 #define SHOW_BOTH 0
138 #define SHOW_APPS_SMALL 1
139 #define SHOW_ICON 2
140 static int panel_style = SHOW_APPS_SMALL;
142 static int closing_panel = 0; /* Don't panel_save; destroying! */
144 /****************************************************************
145 * EXTERNAL INTERFACE *
146 ****************************************************************/
148 void panel_init(void)
150 option_add_int("panel_style", panel_style, panel_set_style);
153 /* 'name' may be NULL or "" to remove the panel */
154 Panel *panel_new(guchar *name, PanelSide side)
156 guchar *load_path;
157 Panel *panel;
158 GtkWidget *vp, *box, *frame;
160 g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, NULL);
161 g_return_val_if_fail(loading_panel == NULL, NULL);
163 if (name && *name == '\0')
164 name = NULL;
166 if (current_panel[side])
168 if (name)
169 number_of_windows++;
170 closing_panel++;
171 gtk_widget_destroy(current_panel[side]->window);
172 closing_panel--;
173 if (name)
174 number_of_windows--;
177 if (name == NULL || *name == '\0')
178 return NULL;
180 panel = g_new(Panel, 1);
181 panel->name = g_strdup(name);
182 panel->side = side;
183 panel->height = 0;
184 panel->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
185 gtk_window_set_wmclass(GTK_WINDOW(panel->window), "ROX-Panel", PROJECT);
186 gtk_widget_set_events(panel->window,
187 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
188 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
189 GDK_BUTTON2_MOTION_MASK);
191 gtk_signal_connect(GTK_OBJECT(panel->window), "delete-event",
192 GTK_SIGNAL_FUNC(panel_delete), panel);
193 gtk_signal_connect(GTK_OBJECT(panel->window), "destroy",
194 GTK_SIGNAL_FUNC(panel_destroyed), panel);
195 gtk_signal_connect(GTK_OBJECT(panel->window), "button_press_event",
196 GTK_SIGNAL_FUNC(panel_button_press), panel);
197 gtk_signal_connect(GTK_OBJECT(panel->window), "button_release_event",
198 GTK_SIGNAL_FUNC(panel_button_release), panel);
199 gtk_signal_connect(GTK_OBJECT(panel->window), "motion-notify-event",
200 GTK_SIGNAL_FUNC(panel_motion_event), panel);
202 if (strchr(name, '/'))
203 load_path = g_strdup(name);
204 else
206 guchar *leaf;
208 leaf = g_strconcat("pan_", name, NULL);
209 load_path = choices_find_path_load(leaf, PROJECT);
210 g_free(leaf);
213 vp = gtk_viewport_new(NULL, NULL);
214 gtk_viewport_set_shadow_type(GTK_VIEWPORT(vp), GTK_SHADOW_OUT);
215 gtk_container_add(GTK_CONTAINER(panel->window), vp);
217 if (side == PANEL_TOP || side == PANEL_BOTTOM)
219 panel->adj = gtk_viewport_get_hadjustment(GTK_VIEWPORT(vp));
220 box = gtk_hbox_new(FALSE, 0);
221 panel->before = gtk_hbox_new(FALSE, 0);
222 panel->after = gtk_hbox_new(FALSE, 0);
224 else
226 panel->adj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(vp));
227 box = gtk_vbox_new(FALSE, 0);
228 panel->before = gtk_vbox_new(FALSE, 0);
229 panel->after = gtk_vbox_new(FALSE, 0);
232 gtk_container_add(GTK_CONTAINER(vp), box);
233 gtk_box_pack_start(GTK_BOX(box), panel->before, FALSE, TRUE, 0);
234 gtk_box_pack_end(GTK_BOX(box), panel->after, FALSE, TRUE, 0);
236 frame = make_insert_frame(panel);
237 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
239 /* This is used so that we can find the middle easily! */
240 panel->gap = gtk_event_box_new();
241 gtk_box_pack_start(GTK_BOX(box), panel->gap, FALSE, FALSE, 0);
243 frame = make_insert_frame(panel);
244 gtk_object_set_data(GTK_OBJECT(frame), "after", "yes");
245 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
247 gtk_widget_realize(panel->window);
248 make_panel_window(panel->window->window);
250 gtk_widget_show_all(vp);
252 loading_panel = panel;
253 if (load_path && access(load_path, F_OK) == 0)
254 parse_file(load_path, pan_from_file);
255 else
257 /* Don't scare users with an empty panel... */
258 guchar *apps;
260 panel_add_item(panel, "~", "Home", FALSE);
262 apps = pathdup(make_path(app_dir, "..")->str);
263 if (apps)
265 panel_add_item(panel, apps, "Apps", FALSE);
266 g_free(apps);
269 loading_panel = NULL;
270 g_free(load_path);
272 current_panel[side] = panel;
274 gtk_widget_queue_resize(box);
275 gtk_signal_connect_after(GTK_OBJECT(panel->window), "size-request",
276 GTK_SIGNAL_FUNC(panel_post_resize), (GtkObject *) panel);
277 gtk_signal_connect_after(GTK_OBJECT(box), "size-allocate",
278 GTK_SIGNAL_FUNC(box_resized), (GtkObject *) panel);
280 number_of_windows++;
281 gtk_widget_show(panel->window);
283 return panel;
286 gboolean panel_want_show_text(Icon *icon)
288 if (panel_style == SHOW_BOTH)
289 return TRUE;
290 if (panel_style == SHOW_ICON)
291 return FALSE;
293 if (icon->item.flags & ITEM_FLAG_APPDIR)
294 return FALSE;
296 return TRUE;
299 void panel_icon_renamed(Icon *icon)
301 GtkLabel *label = GTK_LABEL(icon->label);
303 gtk_label_set_text(label, icon->item.leafname);
307 /****************************************************************
308 * INTERNAL FUNCTIONS *
309 ****************************************************************/
311 /* User has tried to close the panel via the window manager - confirm */
312 static int panel_delete(GtkWidget *widget, GdkEvent *event, Panel *panel)
314 return get_choice(_("Close panel?"),
315 _("You have tried to close a panel via the window "
316 "manager - I usually find that this is accidental... "
317 "really close?"),
318 2, _("Remove"), _("Cancel")) != 0;
321 static void panel_destroyed(GtkWidget *widget, Panel *panel)
323 if (current_panel[panel->side] == panel)
324 current_panel[panel->side] = NULL;
326 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
328 if (current_panel[PANEL_RIGHT])
329 reposition_panel(current_panel[PANEL_RIGHT], FALSE);
330 if (current_panel[PANEL_LEFT])
331 reposition_panel(current_panel[PANEL_LEFT], FALSE);
334 g_free(panel->name);
335 g_free(panel);
337 if (--number_of_windows < 1)
338 gtk_main_quit();
341 /* Called for each line in the config file while loading a new panel */
342 static char *pan_from_file(guchar *line)
344 guchar *sep, *leaf;
346 g_return_val_if_fail(line != NULL, NULL);
347 g_return_val_if_fail(loading_panel != NULL, NULL);
349 if (*line == '\0')
350 return NULL;
352 sep = strpbrk(line, "<>");
353 if (!sep)
354 return _("Missing < or > in panel config file");
356 if (sep != line)
357 leaf = g_strndup(line, sep - line);
358 else
359 leaf = NULL;
361 panel_add_item(loading_panel,
362 sep + 1,
363 leaf,
364 sep[0] == '>');
366 g_free(leaf);
368 return NULL;
371 /* Add an icon with this path to the panel. If after is TRUE then the
372 * icon is added to the right/bottom end of the panel.
374 * If name is NULL a suitable name is taken from path.
376 static void panel_add_item(Panel *panel,
377 guchar *path,
378 guchar *name,
379 gboolean after)
381 GtkWidget *widget;
382 Icon *icon;
384 widget = gtk_event_box_new();
385 gtk_widget_set_events(widget,
386 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
387 GDK_BUTTON3_MOTION_MASK |
388 GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
389 GDK_BUTTON_RELEASE_MASK);
391 gtk_box_pack_start(GTK_BOX(after ? panel->after : panel->before),
392 widget, FALSE, TRUE, 4);
393 if (after)
394 gtk_box_reorder_child(GTK_BOX(panel->after), widget, 0);
396 gtk_widget_realize(widget);
398 icon = g_new(Icon, 1);
399 icon->panel = panel;
400 icon->src_path = g_strdup(path);
401 icon->path = icon_convert_path(path);
402 icon->socket = NULL;
403 icon->label = NULL;
404 #ifdef GTK2
405 icon->layout = NULL;
406 #endif
408 gtk_object_set_data(GTK_OBJECT(widget), "icon", icon);
410 icon_hash_path(icon);
412 icon->widget = widget;
413 gtk_widget_set_name(icon->widget, "panel-icon");
414 icon->selected = FALSE;
415 diritem_stat(icon->path, &icon->item, FALSE);
417 if (name)
418 icon->item.leafname = g_strdup(name);
419 else
421 guchar *slash;
423 slash = strrchr(icon->path, '/');
424 icon->item.leafname = g_strdup(slash && slash[1] ? slash + 1
425 : path);
428 gtk_signal_connect_object(GTK_OBJECT(widget), "destroy",
429 GTK_SIGNAL_FUNC(icon_destroyed), (gpointer) icon);
431 if (icon->item.base_type == TYPE_DIRECTORY)
432 run_applet(icon);
434 gtk_signal_connect(GTK_OBJECT(widget), "button_release_event",
435 GTK_SIGNAL_FUNC(icon_button_release), icon);
436 gtk_signal_connect(GTK_OBJECT(widget), "button_press_event",
437 GTK_SIGNAL_FUNC(icon_button_press), icon);
438 gtk_signal_connect(GTK_OBJECT(icon->widget), "motion-notify-event",
439 GTK_SIGNAL_FUNC(icon_motion_event), icon);
441 if (!icon->socket)
443 gtk_signal_connect_after(GTK_OBJECT(widget),
444 "enter-notify-event",
445 GTK_SIGNAL_FUNC(enter_icon), icon);
446 #ifndef GTK2
447 gtk_signal_connect_after(GTK_OBJECT(widget), "draw",
448 GTK_SIGNAL_FUNC(draw_icon), icon);
449 #endif
450 gtk_signal_connect_after(GTK_OBJECT(widget), "expose_event",
451 GTK_SIGNAL_FUNC(expose_icon), icon);
452 gtk_signal_connect(GTK_OBJECT(widget), "drag_data_get",
453 GTK_SIGNAL_FUNC(drag_data_get), NULL);
455 gtk_signal_connect(GTK_OBJECT(widget), "size_request",
456 GTK_SIGNAL_FUNC(size_request), icon);
458 drag_set_panel_dest(icon);
460 icon->label = gtk_label_new(icon->item.leafname);
461 gtk_container_add(GTK_CONTAINER(icon->widget), icon->label);
462 gtk_misc_set_alignment(GTK_MISC(icon->label), 0.5, 1);
463 gtk_misc_set_padding(GTK_MISC(icon->label), 1, 2);
466 if (!loading_panel)
467 panel_save(panel);
469 icon_set_tip(icon);
470 gtk_widget_show(widget);
473 /* Called when Gtk+ wants to know how much space an icon needs.
474 * 'req' is already big enough for the label, if shown.
476 static void size_request(GtkWidget *widget, GtkRequisition *req, Icon *icon)
478 int im_width, im_height;
480 im_width = icon->item.image->width;
481 im_height = MIN(icon->item.image->height, ICON_HEIGHT);
483 req->height += im_height;
484 req->width = MAX(req->width, im_width);
487 static gint expose_icon(GtkWidget *widget,
488 GdkEventExpose *event,
489 Icon *icon)
491 return draw_icon(widget, &event->area, icon);
494 static gint draw_icon(GtkWidget *widget, GdkRectangle *badarea, Icon *icon)
496 GdkRectangle area;
497 int width, height;
499 gdk_window_get_size(widget->window, &width, &height);
501 area.x = 0;
502 area.width = width;
503 area.height = icon->item.image->height;
505 if (panel_want_show_text(icon))
507 int text_height = icon->label->requisition.height;
509 area.y = height - text_height - area.height;
511 draw_large_icon(widget, &area, &icon->item, icon->selected);
513 else
515 area.y = (height - area.height) >> 1;
516 draw_large_icon(widget, &area, &icon->item, icon->selected);
519 return FALSE;
522 /* icon may be NULL if the event is on the background */
523 static void perform_action(Panel *panel, Icon *icon, GdkEventButton *event)
525 BindAction action;
527 action = bind_lookup_bev(icon ? BIND_PANEL_ICON : BIND_PANEL, event);
529 if (icon && icon->socket)
530 if (action != ACT_POPUP_MENU && action != ACT_MOVE_ICON)
531 return;
533 switch (action)
535 case ACT_OPEN_ITEM:
536 dnd_motion_ungrab();
537 wink_widget(icon->widget);
538 run_diritem(icon->path, &icon->item, NULL, NULL, FALSE);
539 break;
540 case ACT_EDIT_ITEM:
541 dnd_motion_ungrab();
542 wink_widget(icon->widget);
543 run_diritem(icon->path, &icon->item, NULL, NULL, TRUE);
544 break;
545 case ACT_POPUP_MENU:
546 dnd_motion_ungrab();
547 icon_show_menu(event, icon, panel);
548 break;
549 case ACT_MOVE_ICON:
550 dnd_motion_start(MOTION_REPOSITION);
551 break;
552 case ACT_PRIME_AND_SELECT:
553 if (!icon->selected)
554 icon_select_only(icon);
555 dnd_motion_start(MOTION_READY_FOR_DND);
556 break;
557 case ACT_PRIME_AND_TOGGLE:
558 icon_set_selected(icon, !icon->selected);
559 dnd_motion_start(MOTION_READY_FOR_DND);
560 break;
561 case ACT_PRIME_FOR_DND:
562 dnd_motion_start(MOTION_READY_FOR_DND);
563 break;
564 case ACT_TOGGLE_SELECTED:
565 icon_set_selected(icon, !icon->selected);
566 break;
567 case ACT_SELECT_EXCL:
568 icon_set_selected(icon, TRUE);
569 break;
570 case ACT_SLIDE_CLEAR_PANEL:
571 icon_select_only(NULL);
572 /* (no break) */
573 case ACT_SLIDE_PANEL:
574 dnd_motion_grab_pointer();
575 slide_from_value = panel->adj->value;
576 dnd_motion_start(MOTION_REPOSITION);
577 break;
578 case ACT_IGNORE:
579 break;
580 case ACT_CLEAR_SELECTION:
581 icon_select_only(NULL);
582 break;
583 default:
584 g_warning("Unsupported action : %d\n", action);
585 break;
589 static gint panel_button_release(GtkWidget *widget,
590 GdkEventButton *event,
591 Panel *panel)
593 if (dnd_motion_release(event))
594 return TRUE;
596 perform_action(panel, NULL, event);
598 return TRUE;
601 static gint panel_button_press(GtkWidget *widget,
602 GdkEventButton *event,
603 Panel *panel)
605 if (dnd_motion_press(panel->window, event))
606 perform_action(panel, NULL, event);
608 return TRUE;
611 static gint icon_button_release(GtkWidget *widget,
612 GdkEventButton *event,
613 Icon *icon)
615 if (icon->socket && event->button == 1)
616 return FALSE; /* Restart button */
618 if (dnd_motion_release(event))
619 return TRUE;
621 perform_action(icon->panel, icon, event);
623 return TRUE;
626 static gint icon_button_press(GtkWidget *widget,
627 GdkEventButton *event,
628 Icon *icon)
630 if (icon->socket && event->button == 1)
631 return FALSE; /* Restart button */
633 if (dnd_motion_press(widget, event))
634 perform_action(icon->panel, icon, event);
636 return TRUE;
639 /* Difference between height of an icon and height of panel (or width) */
640 #define MARGIN 8
642 static void reposition_panel(Panel *panel, gboolean force_resize)
644 int x = 0, y = 0;
645 int w = 32, h = 32;
646 GList *next, *children;
647 PanelSide side = panel->side;
649 children = get_widget_list(panel);
651 for (next = children; next; next = next->next)
653 GtkWidget *widget = (GtkWidget *) next->data;
654 GtkRequisition req;
656 gtk_widget_get_child_requisition(widget, &req);
658 if (req.width > w)
659 w = req.width;
660 if (req.height > h)
661 h = req.height;
664 g_list_free(children);
666 if (side == PANEL_TOP || side == PANEL_BOTTOM)
668 w = screen_width;
669 h += MARGIN;
671 else
673 w += MARGIN;
674 if (side == PANEL_RIGHT)
675 x = screen_width - w;
677 h = screen_height;
679 if (current_panel[PANEL_TOP])
681 int ph;
683 ph = current_panel[PANEL_TOP]->height;
684 y += ph;
685 h -= ph;
688 if (current_panel[PANEL_BOTTOM])
689 h -= current_panel[PANEL_BOTTOM]->height;
692 if (side == PANEL_BOTTOM)
693 y = screen_height - h;
695 gtk_widget_set_uposition(panel->window, x, y);
696 panel->height = h;
697 gdk_window_move_resize(panel->window->window, x, y, w, h);
699 if (side == PANEL_BOTTOM || side == PANEL_TOP)
701 if (current_panel[PANEL_RIGHT])
702 reposition_panel(current_panel[PANEL_RIGHT], FALSE);
703 if (current_panel[PANEL_LEFT])
704 reposition_panel(current_panel[PANEL_LEFT], FALSE);
708 /* Same as drag_set_dest(), but for panel icons */
709 static void drag_set_panel_dest(Icon *icon)
711 GtkObject *obj = GTK_OBJECT(icon->widget);
713 make_drop_target(icon->widget, 0);
715 gtk_signal_connect(obj, "drag_motion",
716 GTK_SIGNAL_FUNC(drag_motion), icon);
717 gtk_signal_connect(obj, "drag_leave",
718 GTK_SIGNAL_FUNC(drag_leave), icon);
719 gtk_signal_connect(obj, "drag_end",
720 GTK_SIGNAL_FUNC(drag_end), icon);
723 static gboolean drag_motion(GtkWidget *widget,
724 GdkDragContext *context,
725 gint x,
726 gint y,
727 guint time,
728 Icon *icon)
730 GdkDragAction action = context->suggested_action;
731 char *type = NULL;
732 DirItem *item = &icon->item;
734 if (icon->selected)
735 goto out; /* Can't drag a selection to itself */
737 type = dnd_motion_item(context, &item);
739 if (!item)
740 type = NULL;
741 out:
742 /* We actually must pretend to accept the drop, even if the
743 * directory isn't writeable, so that the spring-opening
744 * thing works.
747 /* Don't allow drops to non-writeable directories */
748 if (option_get_int("dnd_spring_open") == FALSE &&
749 type == drop_dest_dir &&
750 access(icon->path, W_OK) != 0)
752 type = NULL;
755 g_dataset_set_data(context, "drop_dest_type", type);
756 if (type)
758 gdk_drag_status(context, action, time);
759 g_dataset_set_data_full(context, "drop_dest_path",
760 g_strdup(icon->path), g_free);
761 if (type == drop_dest_dir)
762 dnd_spring_load(context, NULL);
764 if (dnd_highlight && dnd_highlight != icon->widget)
766 gtk_drag_unhighlight(dnd_highlight);
767 dnd_highlight = NULL;
770 if (dnd_highlight == NULL)
772 gtk_drag_highlight(icon->widget);
773 dnd_highlight = icon->widget;
777 return type != NULL;
781 static void add_uri_list(GtkWidget *widget,
782 GdkDragContext *context,
783 gint x,
784 gint y,
785 GtkSelectionData *selection_data,
786 guint info,
787 guint32 time,
788 Panel *panel)
790 gboolean after = FALSE;
791 GList *uris, *next;
793 if (!selection_data->data)
794 return;
796 g_return_if_fail(selection_data->data[selection_data->length] == '\0');
798 if (gtk_object_get_data(GTK_OBJECT(widget), "after"))
799 after = TRUE;
801 uris = uri_list_to_glist(selection_data->data);
803 for (next = uris; next; next = next->next)
805 guchar *path;
807 path = get_local_path((guchar *) next->data);
809 if (path)
810 panel_add_item(panel, path, NULL, after);
813 g_list_free(uris);
816 static void drag_end(GtkWidget *widget,
817 GdkDragContext *context,
818 Icon *icon)
820 if (tmp_icon_selected)
822 icon_select_only(NULL);
823 tmp_icon_selected = FALSE;
827 static void drag_leave(GtkWidget *widget,
828 GdkDragContext *context,
829 guint32 time,
830 Icon *icon)
832 if (dnd_highlight && dnd_highlight == widget)
834 gtk_drag_unhighlight(dnd_highlight);
835 dnd_highlight = NULL;
838 dnd_spring_abort();
841 /* Writes lines to the file, one for each widget, prefixed by 'side'.
842 * Returns TRUE on success, or FALSE on error (and sets errno).
843 * Always frees the widgets list.
845 static gboolean write_widgets(FILE *file, GList *widgets, guchar side)
847 GList *next;
848 GString *tmp;
850 tmp = g_string_new(NULL);
852 for (next = widgets; next; next = next->next)
854 Icon *icon;
856 icon = gtk_object_get_data(GTK_OBJECT(next->data), "icon");
858 if (!icon)
860 g_warning("Can't find Icon from widget\n");
861 continue;
864 g_string_sprintf(tmp, "%s%c%s\n",
865 icon->item.leafname,
866 side, icon->src_path);
867 if (fwrite(tmp->str, 1, tmp->len, file) < tmp->len)
869 g_list_free(widgets);
870 return FALSE;
874 g_string_free(tmp, TRUE);
876 if (widgets)
877 g_list_free(widgets);
879 return TRUE;
882 void panel_save(Panel *panel)
884 guchar *save = NULL;
885 FILE *file = NULL;
886 guchar *save_new = NULL;
888 g_return_if_fail(panel != NULL);
890 if (strchr(panel->name, '/'))
891 save = g_strdup(panel->name);
892 else
894 guchar *leaf;
896 leaf = g_strconcat("pan_", panel->name, NULL);
897 save = choices_find_path_save(leaf, PROJECT, TRUE);
898 g_free(leaf);
901 if (!save)
902 return;
904 save_new = g_strconcat(save, ".new", NULL);
905 file = fopen(save_new, "wb");
906 if (!file)
907 goto err;
909 if (!write_widgets(file,
910 gtk_container_children(GTK_CONTAINER(panel->before)),
911 '<'))
912 goto err;
914 if (!write_widgets(file,
915 g_list_reverse(gtk_container_children(
916 GTK_CONTAINER(panel->after))),
917 '>'))
918 goto err;
920 if (fclose(file))
922 file = NULL;
923 goto err;
926 file = NULL;
928 if (rename(save_new, save))
929 goto err;
931 goto out;
932 err:
933 delayed_rox_error(_("Could not save panel: %s"), g_strerror(errno));
934 out:
935 if (file)
936 fclose(file);
937 g_free(save);
938 g_free(save_new);
941 static GList *get_widget_list(Panel *panel)
943 GList *list;
945 list = gtk_container_children(GTK_CONTAINER(panel->before));
946 list = g_list_concat(list,
947 gtk_container_children(GTK_CONTAINER(panel->after)));
949 return list;
952 /* Create a frame widget which can be used to add icons to the panel */
953 static GtkWidget *make_insert_frame(Panel *panel)
955 GtkWidget *frame;
956 GtkTargetEntry target_table[] = {
957 {"text/uri-list", 0, TARGET_URI_LIST},
960 frame = gtk_frame_new(NULL);
961 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
962 gtk_widget_set_usize(frame, 16, 16);
964 gtk_signal_connect(GTK_OBJECT(frame), "drag-data-received",
965 GTK_SIGNAL_FUNC(add_uri_list), panel);
966 gtk_drag_dest_set(frame,
967 GTK_DEST_DEFAULT_ALL,
968 target_table,
969 sizeof(target_table) / sizeof(*target_table),
970 GDK_ACTION_COPY);
972 return frame;
975 static gboolean enter_icon(GtkWidget *widget,
976 GdkEventCrossing *event,
977 Icon *icon)
979 icon_may_update(icon);
981 return FALSE;
984 static gint panel_motion_event(GtkWidget *widget,
985 GdkEventMotion *event,
986 Panel *panel)
988 gint delta, new;
989 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
991 if (motion_state != MOTION_REPOSITION)
992 return FALSE;
994 if (horz)
995 delta = event->x_root - drag_start_x;
996 else
997 delta = event->y_root - drag_start_y;
999 new = slide_from_value - delta;
1000 new = CLAMP(new, 0, panel->adj->upper - panel->adj->page_size);
1002 gtk_adjustment_set_value(panel->adj, new);
1004 return TRUE;
1007 static gint icon_motion_event(GtkWidget *widget,
1008 GdkEventMotion *event,
1009 Icon *icon)
1011 Panel *panel = icon->panel;
1012 GList *list, *me;
1013 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1014 int val;
1015 int dir = 0;
1017 if (motion_state == MOTION_READY_FOR_DND)
1019 if (dnd_motion_moved(event))
1020 start_drag(icon, event);
1021 return TRUE;
1023 else if (motion_state != MOTION_REPOSITION)
1024 return FALSE;
1026 list = gtk_container_children(GTK_CONTAINER(panel->before));
1027 list = g_list_append(list, NULL); /* The gap in the middle */
1028 list = g_list_concat(list,
1029 gtk_container_children(GTK_CONTAINER(panel->after)));
1030 me = g_list_find(list, widget);
1032 g_return_val_if_fail(me != NULL, TRUE);
1034 val = horz ? event->x_root : event->y_root;
1036 if (me->prev)
1038 GtkWidget *prev;
1039 int x, y;
1041 if (me->prev->data)
1042 prev = GTK_WIDGET(me->prev->data);
1043 else
1044 prev = panel->gap;
1046 gdk_window_get_deskrelative_origin(prev->window, &x, &y);
1048 if (val <= (horz ? x : y))
1049 dir = -1;
1052 if (dir == 0 && me->next)
1054 GtkWidget *next;
1055 int x, y, w, h;
1057 if (me->next->data)
1058 next = GTK_WIDGET(me->next->data);
1059 else
1060 next = panel->gap;
1062 gdk_window_get_deskrelative_origin(next->window, &x, &y);
1064 gdk_window_get_size(next->window, &w, &h);
1066 x += w;
1067 y += h;
1069 if (val >= (horz ? x : y))
1071 if (next == panel->gap)
1072 dir = +2;
1073 else
1074 dir = +1;
1078 if (dir)
1079 reposition_icon(icon, g_list_index(list, widget) + dir);
1081 return TRUE;
1084 /* Move icon to this index in the complete widget list.
1085 * 0 makes the icon the left-most icon. The gap in the middle has
1086 * an index number, which allows you to specify that the icon should
1087 * go on the left or right side.
1089 static void reposition_icon(Icon *icon, int index)
1091 Panel *panel = icon->panel;
1092 GtkWidget *widget = icon->widget;
1093 GList *list;
1094 int before_len;
1096 list = gtk_container_children(GTK_CONTAINER(panel->before));
1097 before_len = g_list_length(list);
1099 if (index <= before_len)
1101 /* Want to move icon to the 'before' list. Is it there
1102 * already?
1105 if (!g_list_find(list, widget))
1107 /* No, reparent */
1108 gtk_grab_remove(widget);
1109 gtk_widget_reparent(widget, panel->before);
1110 dnd_motion_grab_pointer();
1111 gtk_grab_add(widget);
1114 gtk_box_reorder_child(GTK_BOX(panel->before), widget, index);
1116 else
1118 /* Else, we need it in the 'after' list. */
1120 index -= before_len + 1;
1122 g_list_free(list);
1124 list = gtk_container_children(GTK_CONTAINER(panel->after));
1126 if (!g_list_find(list, widget))
1128 /* Not already there, reparent */
1129 gtk_grab_remove(widget);
1130 gtk_widget_reparent(widget, panel->after);
1131 dnd_motion_grab_pointer();
1132 gtk_grab_add(widget);
1135 gtk_box_reorder_child(GTK_BOX(panel->after), widget, index);
1138 g_list_free(list);
1140 panel_save(panel);
1143 static void start_drag(Icon *icon, GdkEventMotion *event)
1145 GtkWidget *widget = icon->widget;
1147 if (!icon->selected)
1149 if (event->state & GDK_BUTTON1_MASK)
1151 /* Select just this one */
1152 icon_select_only(icon);
1153 tmp_icon_selected = TRUE;
1155 else
1156 icon_set_selected(icon, TRUE);
1159 g_return_if_fail(icon_selection != NULL);
1161 if (icon_selection->next == NULL)
1162 drag_one_item(widget, event, icon->path, &icon->item);
1163 else
1165 guchar *uri_list;
1167 uri_list = create_uri_list(icon_selection);
1168 drag_selection(widget, event, uri_list);
1169 g_free(uri_list);
1173 /* Return a text/uri-list of all the icons in the list */
1174 static guchar *create_uri_list(GList *list)
1176 GString *tmp;
1177 guchar *retval;
1178 guchar *leader;
1180 tmp = g_string_new(NULL);
1181 leader = g_strdup_printf("file://%s", our_host_name_for_dnd());
1183 for (; list; list = list->next)
1185 Icon *icon = (Icon *) list->data;
1187 g_string_append(tmp, leader);
1188 g_string_append(tmp, icon->path);
1189 g_string_append(tmp, "\r\n");
1192 g_free(leader);
1193 retval = tmp->str;
1194 g_string_free(tmp, FALSE);
1196 return retval;
1199 static void applet_died(GtkWidget *socket)
1201 gboolean never_plugged;
1203 never_plugged = (!gtk_object_get_data(GTK_OBJECT(socket), "lost_plug"))
1204 && !GTK_SOCKET(socket)->plug_window;
1206 if (never_plugged)
1208 report_rox_error(
1209 _("Applet quit without ever creating a widget!"));
1210 gtk_widget_destroy(socket);
1213 gtk_widget_unref(socket);
1216 static void socket_destroyed(GtkWidget *socket, GtkWidget *widget)
1218 gtk_object_set_data(GTK_OBJECT(socket), "lost_plug", "yes");
1220 gtk_widget_unref(socket);
1222 gtk_widget_destroy(widget); /* Remove from panel */
1224 if (!closing_panel)
1225 panel_save(gtk_object_get_data(GTK_OBJECT(socket), "panel"));
1228 /* Try to run this applet.
1229 * Cases:
1231 * - No executable AppletRun:
1232 * icon->socket == NULL (unchanged) on return.
1234 * Otherwise, create socket (setting icon->socket) and ref it twice.
1236 * - AppletRun quits without connecting a plug:
1237 * On child death lost_plug is unset and socket is empty.
1238 * Unref socket.
1239 * Report error and destroy widget (to 'socket destroyed').
1241 * - AppletRun quits while plug is in socket:
1242 * Unref socket once. Socket will be destroyed later.
1244 * - Socket is destroyed.
1245 * Set lost_plug = "yes" and remove widget from panel.
1246 * Unref socket.
1248 static void run_applet(Icon *icon)
1250 char *argv[3];
1251 pid_t pid;
1253 argv[0] = make_path(icon->path, "AppletRun")->str;
1255 if (access(argv[0], X_OK) != 0)
1256 return;
1258 icon->socket = gtk_socket_new();
1259 /* Two refs held: one for child death, one for socket destroyed */
1260 gtk_widget_ref(icon->socket);
1261 gtk_widget_ref(icon->socket);
1263 gtk_container_add(GTK_CONTAINER(icon->widget), icon->socket);
1264 gtk_widget_show_all(icon->socket);
1265 gtk_widget_realize(icon->socket);
1267 gtk_object_set_data(GTK_OBJECT(icon->widget), "icon", icon);
1268 gtk_object_set_data(GTK_OBJECT(icon->socket), "panel", icon->panel);
1270 gtk_signal_connect(GTK_OBJECT(icon->socket), "destroy",
1271 GTK_SIGNAL_FUNC(socket_destroyed), icon->widget);
1273 argv[1] = g_strdup_printf("%ld",
1274 GDK_WINDOW_XWINDOW(icon->socket->window));
1275 argv[2] = NULL;
1277 pid = spawn(argv);
1279 on_child_death(pid, (CallbackFn) applet_died, icon->socket);
1281 g_free(argv[1]);
1284 /* When one of the panel icons resizes it will cause it's container box
1285 * to resize. This will cause the packing box inside the viewport to resize -
1286 * we get here right after that.
1288 static void box_resized(GtkWidget *box, GtkAllocation *alloc, Panel *panel)
1290 reposition_panel(panel, FALSE);
1293 static void panel_post_resize(GtkWidget *win, GtkRequisition *req, Panel *panel)
1295 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
1297 if (req->width < screen_width)
1298 req->width = screen_width;
1300 else
1302 int h = screen_height;
1304 if (current_panel[PANEL_TOP])
1305 h -= current_panel[PANEL_TOP]->height;
1307 if (current_panel[PANEL_BOTTOM])
1308 h -= current_panel[PANEL_BOTTOM]->height;
1310 if (req->height < h)
1311 req->height = h;
1315 /* The style setting has been changed -- update all panels */
1316 static void panel_set_style(guchar *new)
1318 int os = panel_style;
1320 panel_style = option_get_int("panel_style");
1322 if (os != panel_style)
1324 int i;
1326 icons_update_tip();
1328 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
1330 Panel *panel = current_panel[i];
1332 if (!panel)
1333 continue;
1335 gtk_widget_queue_resize(panel->window);