r898: Applied Bernard Jungen's latest patch:
[rox-filer.git] / ROX-Filer / src / panel.c
blobff5e3f5ed11d807c190df769fda313739e00e9a6
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 /****************************************************************
143 * EXTERNAL INTERFACE *
144 ****************************************************************/
146 void panel_init(void)
148 option_add_int("panel_style", panel_style, panel_set_style);
151 /* 'name' may be NULL or "" to remove the panel */
152 Panel *panel_new(guchar *name, PanelSide side)
154 guchar *load_path;
155 Panel *panel;
156 GtkWidget *vp, *box, *frame;
158 g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, NULL);
159 g_return_val_if_fail(loading_panel == NULL, NULL);
161 if (name && *name == '\0')
162 name = NULL;
164 if (current_panel[side])
166 if (name)
167 number_of_windows++;
168 gtk_widget_destroy(current_panel[side]->window);
169 if (name)
170 number_of_windows--;
173 if (name == NULL || *name == '\0')
174 return NULL;
176 panel = g_new(Panel, 1);
177 panel->name = g_strdup(name);
178 panel->side = side;
179 panel->height = 0;
180 panel->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
181 gtk_window_set_wmclass(GTK_WINDOW(panel->window), "ROX-Panel", PROJECT);
182 gtk_widget_set_events(panel->window,
183 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
184 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
185 GDK_BUTTON2_MOTION_MASK);
187 gtk_signal_connect(GTK_OBJECT(panel->window), "delete-event",
188 GTK_SIGNAL_FUNC(panel_delete), panel);
189 gtk_signal_connect(GTK_OBJECT(panel->window), "destroy",
190 GTK_SIGNAL_FUNC(panel_destroyed), panel);
191 gtk_signal_connect(GTK_OBJECT(panel->window), "button_press_event",
192 GTK_SIGNAL_FUNC(panel_button_press), panel);
193 gtk_signal_connect(GTK_OBJECT(panel->window), "button_release_event",
194 GTK_SIGNAL_FUNC(panel_button_release), panel);
195 gtk_signal_connect(GTK_OBJECT(panel->window), "motion-notify-event",
196 GTK_SIGNAL_FUNC(panel_motion_event), panel);
198 if (strchr(name, '/'))
199 load_path = g_strdup(name);
200 else
202 guchar *leaf;
204 leaf = g_strconcat("pan_", name, NULL);
205 load_path = choices_find_path_load(leaf, PROJECT);
206 g_free(leaf);
209 vp = gtk_viewport_new(NULL, NULL);
210 gtk_viewport_set_shadow_type(GTK_VIEWPORT(vp), GTK_SHADOW_OUT);
211 gtk_container_add(GTK_CONTAINER(panel->window), vp);
213 if (side == PANEL_TOP || side == PANEL_BOTTOM)
215 panel->adj = gtk_viewport_get_hadjustment(GTK_VIEWPORT(vp));
216 box = gtk_hbox_new(FALSE, 0);
217 panel->before = gtk_hbox_new(FALSE, 0);
218 panel->after = gtk_hbox_new(FALSE, 0);
220 else
222 panel->adj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(vp));
223 box = gtk_vbox_new(FALSE, 0);
224 panel->before = gtk_vbox_new(FALSE, 0);
225 panel->after = gtk_vbox_new(FALSE, 0);
228 gtk_container_add(GTK_CONTAINER(vp), box);
229 gtk_box_pack_start(GTK_BOX(box), panel->before, FALSE, TRUE, 0);
230 gtk_box_pack_end(GTK_BOX(box), panel->after, FALSE, TRUE, 0);
232 frame = make_insert_frame(panel);
233 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
235 /* This is used so that we can find the middle easily! */
236 panel->gap = gtk_event_box_new();
237 gtk_box_pack_start(GTK_BOX(box), panel->gap, FALSE, FALSE, 0);
239 frame = make_insert_frame(panel);
240 gtk_object_set_data(GTK_OBJECT(frame), "after", "yes");
241 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
243 gtk_widget_realize(panel->window);
244 make_panel_window(panel->window->window);
246 gtk_widget_show_all(vp);
248 loading_panel = panel;
249 if (load_path && access(load_path, F_OK) == 0)
250 parse_file(load_path, pan_from_file);
251 else
253 /* Don't scare users with an empty panel... */
254 guchar *apps;
256 panel_add_item(panel, "~", "Home", FALSE);
258 apps = pathdup(make_path(app_dir, "..")->str);
259 if (apps)
261 panel_add_item(panel, apps, "Apps", FALSE);
262 g_free(apps);
265 loading_panel = NULL;
266 g_free(load_path);
268 current_panel[side] = panel;
270 gtk_widget_queue_resize(box);
271 gtk_signal_connect_after(GTK_OBJECT(panel->window), "size-request",
272 GTK_SIGNAL_FUNC(panel_post_resize), (GtkObject *) panel);
273 gtk_signal_connect_after(GTK_OBJECT(box), "size-allocate",
274 GTK_SIGNAL_FUNC(box_resized), (GtkObject *) panel);
276 number_of_windows++;
277 gtk_widget_show(panel->window);
279 return panel;
282 gboolean panel_want_show_text(Icon *icon)
284 if (panel_style == SHOW_BOTH)
285 return TRUE;
286 if (panel_style == SHOW_ICON)
287 return FALSE;
289 if (icon->item.flags & ITEM_FLAG_APPDIR)
290 return FALSE;
292 return TRUE;
295 void panel_icon_renamed(Icon *icon)
297 GtkLabel *label = GTK_LABEL(icon->label);
299 gtk_label_set_text(label, icon->item.leafname);
303 /****************************************************************
304 * INTERNAL FUNCTIONS *
305 ****************************************************************/
307 /* User has tried to close the panel via the window manager - confirm */
308 static int panel_delete(GtkWidget *widget, GdkEvent *event, Panel *panel)
310 return get_choice(_("Close panel?"),
311 _("You have tried to close a panel via the window "
312 "manager - I usually find that this is accidental... "
313 "really close?"),
314 2, _("Remove"), _("Cancel")) != 0;
317 static void panel_destroyed(GtkWidget *widget, Panel *panel)
319 if (current_panel[panel->side] == panel)
320 current_panel[panel->side] = NULL;
322 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
324 if (current_panel[PANEL_RIGHT])
325 reposition_panel(current_panel[PANEL_RIGHT], FALSE);
326 if (current_panel[PANEL_LEFT])
327 reposition_panel(current_panel[PANEL_LEFT], FALSE);
330 g_free(panel->name);
331 g_free(panel);
333 if (--number_of_windows < 1)
334 gtk_main_quit();
337 /* Called for each line in the config file while loading a new panel */
338 static char *pan_from_file(guchar *line)
340 guchar *sep, *leaf;
342 g_return_val_if_fail(line != NULL, NULL);
343 g_return_val_if_fail(loading_panel != NULL, NULL);
345 if (*line == '\0')
346 return NULL;
348 sep = strpbrk(line, "<>");
349 if (!sep)
350 return _("Missing < or > in panel config file");
352 if (sep != line)
353 leaf = g_strndup(line, sep - line);
354 else
355 leaf = NULL;
357 panel_add_item(loading_panel,
358 sep + 1,
359 leaf,
360 sep[0] == '>');
362 g_free(leaf);
364 return NULL;
367 /* Add an icon with this path to the panel. If after is TRUE then the
368 * icon is added to the right/bottom end of the panel.
370 * If name is NULL a suitable name is taken from path.
372 static void panel_add_item(Panel *panel,
373 guchar *path,
374 guchar *name,
375 gboolean after)
377 GtkWidget *widget;
378 Icon *icon;
380 widget = gtk_event_box_new();
381 gtk_widget_set_events(widget,
382 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
383 GDK_BUTTON3_MOTION_MASK |
384 GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
385 GDK_BUTTON_RELEASE_MASK);
387 gtk_box_pack_start(GTK_BOX(after ? panel->after : panel->before),
388 widget, FALSE, TRUE, 4);
389 if (after)
390 gtk_box_reorder_child(GTK_BOX(panel->after), widget, 0);
392 gtk_widget_realize(widget);
394 icon = g_new(Icon, 1);
395 icon->panel = panel;
396 icon->src_path = g_strdup(path);
397 icon->path = icon_convert_path(path);
398 icon->socket = NULL;
399 icon->label = NULL;
400 #ifdef GTK2
401 icon->layout = NULL;
402 #endif
404 gtk_object_set_data(GTK_OBJECT(widget), "icon", icon);
406 icon_hash_path(icon);
408 icon->widget = widget;
409 gtk_widget_set_name(icon->widget, "panel-icon");
410 icon->selected = FALSE;
411 diritem_stat(icon->path, &icon->item, FALSE);
413 if (name)
414 icon->item.leafname = g_strdup(name);
415 else
417 guchar *slash;
419 slash = strrchr(icon->path, '/');
420 icon->item.leafname = g_strdup(slash && slash[1] ? slash + 1
421 : path);
424 gtk_signal_connect_object(GTK_OBJECT(widget), "destroy",
425 GTK_SIGNAL_FUNC(icon_destroyed), (gpointer) icon);
427 if (icon->item.base_type == TYPE_DIRECTORY)
428 run_applet(icon);
430 gtk_signal_connect(GTK_OBJECT(widget), "button_release_event",
431 GTK_SIGNAL_FUNC(icon_button_release), icon);
432 gtk_signal_connect(GTK_OBJECT(widget), "button_press_event",
433 GTK_SIGNAL_FUNC(icon_button_press), icon);
434 gtk_signal_connect(GTK_OBJECT(icon->widget), "motion-notify-event",
435 GTK_SIGNAL_FUNC(icon_motion_event), icon);
437 if (!icon->socket)
439 gtk_signal_connect_after(GTK_OBJECT(widget),
440 "enter-notify-event",
441 GTK_SIGNAL_FUNC(enter_icon), icon);
442 #ifndef GTK2
443 gtk_signal_connect_after(GTK_OBJECT(widget), "draw",
444 GTK_SIGNAL_FUNC(draw_icon), icon);
445 #endif
446 gtk_signal_connect_after(GTK_OBJECT(widget), "expose_event",
447 GTK_SIGNAL_FUNC(expose_icon), icon);
448 gtk_signal_connect(GTK_OBJECT(widget), "drag_data_get",
449 GTK_SIGNAL_FUNC(drag_data_get), NULL);
451 gtk_signal_connect(GTK_OBJECT(widget), "size_request",
452 GTK_SIGNAL_FUNC(size_request), icon);
454 drag_set_panel_dest(icon);
456 icon->label = gtk_label_new(icon->item.leafname);
457 gtk_container_add(GTK_CONTAINER(icon->widget), icon->label);
458 gtk_misc_set_alignment(GTK_MISC(icon->label), 0.5, 1);
459 gtk_misc_set_padding(GTK_MISC(icon->label), 1, 2);
462 if (!loading_panel)
463 panel_save(panel);
465 icon_set_tip(icon);
466 gtk_widget_show(widget);
469 /* Called when Gtk+ wants to know how much space an icon needs.
470 * 'req' is already big enough for the label, if shown.
472 static void size_request(GtkWidget *widget, GtkRequisition *req, Icon *icon)
474 int im_width, im_height;
476 im_width = icon->item.image->width;
477 im_height = MIN(icon->item.image->height, ICON_HEIGHT);
479 req->height += im_height;
480 req->width = MAX(req->width, im_width);
483 static gint expose_icon(GtkWidget *widget,
484 GdkEventExpose *event,
485 Icon *icon)
487 return draw_icon(widget, &event->area, icon);
490 static gint draw_icon(GtkWidget *widget, GdkRectangle *badarea, Icon *icon)
492 GdkRectangle area;
493 int width, height;
495 gdk_window_get_size(widget->window, &width, &height);
497 area.x = 0;
498 area.width = width;
499 area.height = icon->item.image->height;
501 if (panel_want_show_text(icon))
503 int text_height = icon->label->requisition.height;
505 area.y = height - text_height - area.height;
507 draw_large_icon(widget, &area, &icon->item, icon->selected);
509 else
511 area.y = (height - area.height) >> 1;
512 draw_large_icon(widget, &area, &icon->item, icon->selected);
515 return FALSE;
518 /* icon may be NULL if the event is on the background */
519 static void perform_action(Panel *panel, Icon *icon, GdkEventButton *event)
521 BindAction action;
523 action = bind_lookup_bev(icon ? BIND_PANEL_ICON : BIND_PANEL, event);
525 if (icon && icon->socket)
526 if (action != ACT_POPUP_MENU && action != ACT_MOVE_ICON)
527 return;
529 switch (action)
531 case ACT_OPEN_ITEM:
532 dnd_motion_ungrab();
533 wink_widget(icon->widget);
534 run_diritem(icon->path, &icon->item, NULL, NULL, FALSE);
535 break;
536 case ACT_EDIT_ITEM:
537 dnd_motion_ungrab();
538 wink_widget(icon->widget);
539 run_diritem(icon->path, &icon->item, NULL, NULL, TRUE);
540 break;
541 case ACT_POPUP_MENU:
542 dnd_motion_ungrab();
543 icon_show_menu(event, icon, panel);
544 break;
545 case ACT_MOVE_ICON:
546 dnd_motion_start(MOTION_REPOSITION);
547 break;
548 case ACT_PRIME_AND_SELECT:
549 if (!icon->selected)
550 icon_select_only(icon);
551 dnd_motion_start(MOTION_READY_FOR_DND);
552 break;
553 case ACT_PRIME_AND_TOGGLE:
554 icon_set_selected(icon, !icon->selected);
555 dnd_motion_start(MOTION_READY_FOR_DND);
556 break;
557 case ACT_PRIME_FOR_DND:
558 dnd_motion_start(MOTION_READY_FOR_DND);
559 break;
560 case ACT_TOGGLE_SELECTED:
561 icon_set_selected(icon, !icon->selected);
562 break;
563 case ACT_SELECT_EXCL:
564 icon_set_selected(icon, TRUE);
565 break;
566 case ACT_SLIDE_CLEAR_PANEL:
567 icon_select_only(NULL);
568 /* (no break) */
569 case ACT_SLIDE_PANEL:
570 dnd_motion_grab_pointer();
571 slide_from_value = panel->adj->value;
572 dnd_motion_start(MOTION_REPOSITION);
573 break;
574 case ACT_IGNORE:
575 break;
576 case ACT_CLEAR_SELECTION:
577 icon_select_only(NULL);
578 break;
579 default:
580 g_warning("Unsupported action : %d\n", action);
581 break;
585 static gint panel_button_release(GtkWidget *widget,
586 GdkEventButton *event,
587 Panel *panel)
589 if (dnd_motion_release(event))
590 return TRUE;
592 perform_action(panel, NULL, event);
594 return TRUE;
597 static gint panel_button_press(GtkWidget *widget,
598 GdkEventButton *event,
599 Panel *panel)
601 if (dnd_motion_press(panel->window, event))
602 perform_action(panel, NULL, event);
604 return TRUE;
607 static gint icon_button_release(GtkWidget *widget,
608 GdkEventButton *event,
609 Icon *icon)
611 if (icon->socket && event->button == 1)
612 return FALSE; /* Restart button */
614 if (dnd_motion_release(event))
615 return TRUE;
617 perform_action(icon->panel, icon, event);
619 return TRUE;
622 static gint icon_button_press(GtkWidget *widget,
623 GdkEventButton *event,
624 Icon *icon)
626 if (icon->socket && event->button == 1)
627 return FALSE; /* Restart button */
629 if (dnd_motion_press(widget, event))
630 perform_action(icon->panel, icon, event);
632 return TRUE;
635 /* Difference between height of an icon and height of panel (or width) */
636 #define MARGIN 8
638 static void reposition_panel(Panel *panel, gboolean force_resize)
640 int x = 0, y = 0;
641 int w = 32, h = 32;
642 GList *next, *children;
643 PanelSide side = panel->side;
645 children = get_widget_list(panel);
647 for (next = children; next; next = next->next)
649 GtkWidget *widget = (GtkWidget *) next->data;
650 GtkRequisition req;
652 gtk_widget_get_child_requisition(widget, &req);
654 if (req.width > w)
655 w = req.width;
656 if (req.height > h)
657 h = req.height;
660 g_list_free(children);
662 if (side == PANEL_TOP || side == PANEL_BOTTOM)
664 w = screen_width;
665 h += MARGIN;
667 else
669 w += MARGIN;
670 if (side == PANEL_RIGHT)
671 x = screen_width - w;
673 h = screen_height;
675 if (current_panel[PANEL_TOP])
677 int ph;
679 ph = current_panel[PANEL_TOP]->height;
680 y += ph;
681 h -= ph;
684 if (current_panel[PANEL_BOTTOM])
685 h -= current_panel[PANEL_BOTTOM]->height;
688 if (side == PANEL_BOTTOM)
689 y = screen_height - h;
691 gtk_widget_set_uposition(panel->window, x, y);
692 panel->height = h;
693 gdk_window_move_resize(panel->window->window, x, y, w, h);
695 if (side == PANEL_BOTTOM || side == PANEL_TOP)
697 if (current_panel[PANEL_RIGHT])
698 reposition_panel(current_panel[PANEL_RIGHT], FALSE);
699 if (current_panel[PANEL_LEFT])
700 reposition_panel(current_panel[PANEL_LEFT], FALSE);
704 /* Same as drag_set_dest(), but for panel icons */
705 static void drag_set_panel_dest(Icon *icon)
707 GtkObject *obj = GTK_OBJECT(icon->widget);
709 make_drop_target(icon->widget, 0);
711 gtk_signal_connect(obj, "drag_motion",
712 GTK_SIGNAL_FUNC(drag_motion), icon);
713 gtk_signal_connect(obj, "drag_leave",
714 GTK_SIGNAL_FUNC(drag_leave), icon);
715 gtk_signal_connect(obj, "drag_end",
716 GTK_SIGNAL_FUNC(drag_end), icon);
719 static gboolean drag_motion(GtkWidget *widget,
720 GdkDragContext *context,
721 gint x,
722 gint y,
723 guint time,
724 Icon *icon)
726 GdkDragAction action = context->suggested_action;
727 char *type = NULL;
728 DirItem *item = &icon->item;
730 if (icon->selected)
731 goto out; /* Can't drag a selection to itself */
733 type = dnd_motion_item(context, &item);
735 if (!item)
736 type = NULL;
737 out:
738 /* We actually must pretend to accept the drop, even if the
739 * directory isn't writeable, so that the spring-opening
740 * thing works.
743 /* Don't allow drops to non-writeable directories */
744 if (option_get_int("dnd_spring_open") == FALSE &&
745 type == drop_dest_dir &&
746 access(icon->path, W_OK) != 0)
748 type = NULL;
751 g_dataset_set_data(context, "drop_dest_type", type);
752 if (type)
754 gdk_drag_status(context, action, time);
755 g_dataset_set_data_full(context, "drop_dest_path",
756 g_strdup(icon->path), g_free);
757 if (type == drop_dest_dir)
758 dnd_spring_load(context, NULL);
760 if (dnd_highlight && dnd_highlight != icon->widget)
762 gtk_drag_unhighlight(dnd_highlight);
763 dnd_highlight = NULL;
766 if (dnd_highlight == NULL)
768 gtk_drag_highlight(icon->widget);
769 dnd_highlight = icon->widget;
773 return type != NULL;
777 static void add_uri_list(GtkWidget *widget,
778 GdkDragContext *context,
779 gint x,
780 gint y,
781 GtkSelectionData *selection_data,
782 guint info,
783 guint32 time,
784 Panel *panel)
786 gboolean after = FALSE;
787 GList *uris, *next;
789 if (!selection_data->data)
790 return;
792 g_return_if_fail(selection_data->data[selection_data->length] == '\0');
794 if (gtk_object_get_data(GTK_OBJECT(widget), "after"))
795 after = TRUE;
797 uris = uri_list_to_glist(selection_data->data);
799 for (next = uris; next; next = next->next)
801 guchar *path;
803 path = get_local_path((guchar *) next->data);
805 if (path)
806 panel_add_item(panel, path, NULL, after);
809 g_list_free(uris);
812 static void drag_end(GtkWidget *widget,
813 GdkDragContext *context,
814 Icon *icon)
816 if (tmp_icon_selected)
818 icon_select_only(NULL);
819 tmp_icon_selected = FALSE;
823 static void drag_leave(GtkWidget *widget,
824 GdkDragContext *context,
825 guint32 time,
826 Icon *icon)
828 if (dnd_highlight && dnd_highlight == widget)
830 gtk_drag_unhighlight(dnd_highlight);
831 dnd_highlight = NULL;
834 dnd_spring_abort();
837 /* Writes lines to the file, one for each widget, prefixed by 'side'.
838 * Returns TRUE on success, or FALSE on error (and sets errno).
839 * Always frees the widgets list.
841 static gboolean write_widgets(FILE *file, GList *widgets, guchar side)
843 GList *next;
844 GString *tmp;
846 tmp = g_string_new(NULL);
848 for (next = widgets; next; next = next->next)
850 Icon *icon;
852 icon = gtk_object_get_data(GTK_OBJECT(next->data), "icon");
854 if (!icon)
856 g_warning("Can't find Icon from widget\n");
857 continue;
860 g_string_sprintf(tmp, "%s%c%s\n",
861 icon->item.leafname,
862 side, icon->src_path);
863 if (fwrite(tmp->str, 1, tmp->len, file) < tmp->len)
865 g_list_free(widgets);
866 return FALSE;
870 g_string_free(tmp, TRUE);
872 if (widgets)
873 g_list_free(widgets);
875 return TRUE;
878 void panel_save(Panel *panel)
880 guchar *save = NULL;
881 FILE *file = NULL;
882 guchar *save_new = NULL;
884 g_return_if_fail(panel != NULL);
886 if (strchr(panel->name, '/'))
887 save = g_strdup(panel->name);
888 else
890 guchar *leaf;
892 leaf = g_strconcat("pan_", panel->name, NULL);
893 save = choices_find_path_save(leaf, PROJECT, TRUE);
894 g_free(leaf);
897 if (!save)
898 return;
900 save_new = g_strconcat(save, ".new", NULL);
901 file = fopen(save_new, "wb");
902 if (!file)
903 goto err;
905 if (!write_widgets(file,
906 gtk_container_children(GTK_CONTAINER(panel->before)),
907 '<'))
908 goto err;
910 if (!write_widgets(file,
911 g_list_reverse(gtk_container_children(
912 GTK_CONTAINER(panel->after))),
913 '>'))
914 goto err;
916 if (fclose(file))
918 file = NULL;
919 goto err;
922 file = NULL;
924 if (rename(save_new, save))
925 goto err;
927 goto out;
928 err:
929 delayed_rox_error(_("Could not save panel: %s"), g_strerror(errno));
930 out:
931 if (file)
932 fclose(file);
933 g_free(save);
934 g_free(save_new);
937 static GList *get_widget_list(Panel *panel)
939 GList *list;
941 list = gtk_container_children(GTK_CONTAINER(panel->before));
942 list = g_list_concat(list,
943 gtk_container_children(GTK_CONTAINER(panel->after)));
945 return list;
948 /* Create a frame widget which can be used to add icons to the panel */
949 static GtkWidget *make_insert_frame(Panel *panel)
951 GtkWidget *frame;
952 GtkTargetEntry target_table[] = {
953 {"text/uri-list", 0, TARGET_URI_LIST},
956 frame = gtk_frame_new(NULL);
957 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
958 gtk_widget_set_usize(frame, 16, 16);
960 gtk_signal_connect(GTK_OBJECT(frame), "drag-data-received",
961 GTK_SIGNAL_FUNC(add_uri_list), panel);
962 gtk_drag_dest_set(frame,
963 GTK_DEST_DEFAULT_ALL,
964 target_table,
965 sizeof(target_table) / sizeof(*target_table),
966 GDK_ACTION_COPY);
968 return frame;
971 static gboolean enter_icon(GtkWidget *widget,
972 GdkEventCrossing *event,
973 Icon *icon)
975 icon_may_update(icon);
977 return FALSE;
980 static gint panel_motion_event(GtkWidget *widget,
981 GdkEventMotion *event,
982 Panel *panel)
984 gint delta, new;
985 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
987 if (motion_state != MOTION_REPOSITION)
988 return FALSE;
990 if (horz)
991 delta = event->x_root - drag_start_x;
992 else
993 delta = event->y_root - drag_start_y;
995 new = slide_from_value - delta;
996 new = CLAMP(new, 0, panel->adj->upper - panel->adj->page_size);
998 gtk_adjustment_set_value(panel->adj, new);
1000 return TRUE;
1003 static gint icon_motion_event(GtkWidget *widget,
1004 GdkEventMotion *event,
1005 Icon *icon)
1007 Panel *panel = icon->panel;
1008 GList *list, *me;
1009 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1010 int val;
1011 int dir = 0;
1013 if (motion_state == MOTION_READY_FOR_DND)
1015 if (dnd_motion_moved(event))
1016 start_drag(icon, event);
1017 return TRUE;
1019 else if (motion_state != MOTION_REPOSITION)
1020 return FALSE;
1022 list = gtk_container_children(GTK_CONTAINER(panel->before));
1023 list = g_list_append(list, NULL); /* The gap in the middle */
1024 list = g_list_concat(list,
1025 gtk_container_children(GTK_CONTAINER(panel->after)));
1026 me = g_list_find(list, widget);
1028 g_return_val_if_fail(me != NULL, TRUE);
1030 val = horz ? event->x_root : event->y_root;
1032 if (me->prev)
1034 GtkWidget *prev;
1035 int x, y;
1037 if (me->prev->data)
1038 prev = GTK_WIDGET(me->prev->data);
1039 else
1040 prev = panel->gap;
1042 gdk_window_get_deskrelative_origin(prev->window, &x, &y);
1044 if (val <= (horz ? x : y))
1045 dir = -1;
1048 if (dir == 0 && me->next)
1050 GtkWidget *next;
1051 int x, y, w, h;
1053 if (me->next->data)
1054 next = GTK_WIDGET(me->next->data);
1055 else
1056 next = panel->gap;
1058 gdk_window_get_deskrelative_origin(next->window, &x, &y);
1060 gdk_window_get_size(next->window, &w, &h);
1062 x += w;
1063 y += h;
1065 if (val >= (horz ? x : y))
1067 if (next == panel->gap)
1068 dir = +2;
1069 else
1070 dir = +1;
1074 if (dir)
1075 reposition_icon(icon, g_list_index(list, widget) + dir);
1077 return TRUE;
1080 /* Move icon to this index in the complete widget list.
1081 * 0 makes the icon the left-most icon. The gap in the middle has
1082 * an index number, which allows you to specify that the icon should
1083 * go on the left or right side.
1085 static void reposition_icon(Icon *icon, int index)
1087 Panel *panel = icon->panel;
1088 GtkWidget *widget = icon->widget;
1089 GList *list;
1090 int before_len;
1092 list = gtk_container_children(GTK_CONTAINER(panel->before));
1093 before_len = g_list_length(list);
1095 if (index <= before_len)
1097 /* Want to move icon to the 'before' list. Is it there
1098 * already?
1101 if (!g_list_find(list, widget))
1103 /* No, reparent */
1104 gtk_grab_remove(widget);
1105 gtk_widget_reparent(widget, panel->before);
1106 dnd_motion_grab_pointer();
1107 gtk_grab_add(widget);
1110 gtk_box_reorder_child(GTK_BOX(panel->before), widget, index);
1112 else
1114 /* Else, we need it in the 'after' list. */
1116 index -= before_len + 1;
1118 g_list_free(list);
1120 list = gtk_container_children(GTK_CONTAINER(panel->after));
1122 if (!g_list_find(list, widget))
1124 /* Not already there, reparent */
1125 gtk_grab_remove(widget);
1126 gtk_widget_reparent(widget, panel->after);
1127 dnd_motion_grab_pointer();
1128 gtk_grab_add(widget);
1131 gtk_box_reorder_child(GTK_BOX(panel->after), widget, index);
1134 g_list_free(list);
1136 panel_save(panel);
1139 static void start_drag(Icon *icon, GdkEventMotion *event)
1141 GtkWidget *widget = icon->widget;
1143 if (!icon->selected)
1145 if (event->state & GDK_BUTTON1_MASK)
1147 /* Select just this one */
1148 icon_select_only(icon);
1149 tmp_icon_selected = TRUE;
1151 else
1152 icon_set_selected(icon, TRUE);
1155 g_return_if_fail(icon_selection != NULL);
1157 if (icon_selection->next == NULL)
1158 drag_one_item(widget, event, icon->path, &icon->item);
1159 else
1161 guchar *uri_list;
1163 uri_list = create_uri_list(icon_selection);
1164 drag_selection(widget, event, uri_list);
1165 g_free(uri_list);
1169 /* Return a text/uri-list of all the icons in the list */
1170 static guchar *create_uri_list(GList *list)
1172 GString *tmp;
1173 guchar *retval;
1174 guchar *leader;
1176 tmp = g_string_new(NULL);
1177 leader = g_strdup_printf("file://%s", our_host_name_for_dnd());
1179 for (; list; list = list->next)
1181 Icon *icon = (Icon *) list->data;
1183 g_string_append(tmp, leader);
1184 g_string_append(tmp, icon->path);
1185 g_string_append(tmp, "\r\n");
1188 g_free(leader);
1189 retval = tmp->str;
1190 g_string_free(tmp, FALSE);
1192 return retval;
1195 static void applet_died(GtkWidget *socket)
1197 if (!GTK_OBJECT_DESTROYED(socket))
1198 gtk_widget_destroy(socket);
1200 gtk_widget_unref(socket);
1203 static void restart_applet(GtkWidget *button, Icon *icon)
1205 gtk_widget_destroy(button);
1206 run_applet(icon);
1209 static void socket_destroyed(GtkWidget *socket, GtkWidget *widget)
1211 Icon *icon;
1212 gboolean lost_widget;
1214 lost_widget = GTK_OBJECT_DESTROYED(widget);
1215 gtk_widget_unref(widget);
1217 if (lost_widget)
1218 return; /* We're removing the icon... */
1220 icon = gtk_object_get_data(GTK_OBJECT(widget), "icon");
1221 icon->socket = gtk_button_new_with_label(_("Restart\nApplet"));
1223 gtk_signal_connect(GTK_OBJECT(icon->socket), "button_release_event",
1224 GTK_SIGNAL_FUNC(icon_button_release), icon);
1225 gtk_signal_connect(GTK_OBJECT(icon->socket), "button_press_event",
1226 GTK_SIGNAL_FUNC(icon_button_press), icon);
1228 gtk_container_add(GTK_CONTAINER(icon->widget), icon->socket);
1229 gtk_container_set_border_width(GTK_CONTAINER(icon->socket), 4);
1230 gtk_misc_set_padding(GTK_MISC(GTK_BIN(icon->socket)->child), 4, 4);
1231 gtk_widget_show(icon->socket);
1233 gtk_signal_connect(GTK_OBJECT(icon->socket), "clicked",
1234 GTK_SIGNAL_FUNC(restart_applet), icon);
1237 /* Try to run this applet. Fills in icon->socket on success. */
1238 static void run_applet(Icon *icon)
1240 char *argv[3];
1241 pid_t pid;
1243 argv[0] = make_path(icon->path, "AppletRun")->str;
1245 if (access(argv[0], X_OK) != 0)
1246 return;
1248 icon->socket = gtk_socket_new();
1249 gtk_container_add(GTK_CONTAINER(icon->widget), icon->socket);
1250 gtk_widget_show_all(icon->socket);
1251 gtk_widget_realize(icon->socket);
1253 gtk_widget_ref(icon->widget);
1254 gtk_object_set_data(GTK_OBJECT(icon->widget), "icon", icon);
1255 gtk_signal_connect(GTK_OBJECT(icon->socket), "destroy",
1256 GTK_SIGNAL_FUNC(socket_destroyed), icon->widget);
1258 argv[1] = g_strdup_printf("%ld",
1259 GDK_WINDOW_XWINDOW(icon->socket->window));
1260 argv[2] = NULL;
1262 pid = spawn(argv);
1263 gtk_widget_ref(icon->socket);
1264 on_child_death(pid, (CallbackFn) applet_died, icon->socket);
1266 g_free(argv[1]);
1269 /* When one of the panel icons resizes it will cause it's container box
1270 * to resize. This will cause the packing box inside the viewport to resize -
1271 * we get here right after that.
1273 static void box_resized(GtkWidget *box, GtkAllocation *alloc, Panel *panel)
1275 reposition_panel(panel, FALSE);
1278 static void panel_post_resize(GtkWidget *win, GtkRequisition *req, Panel *panel)
1280 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
1282 if (req->width < screen_width)
1283 req->width = screen_width;
1285 else
1287 int h = screen_height;
1289 if (current_panel[PANEL_TOP])
1290 h -= current_panel[PANEL_TOP]->height;
1292 if (current_panel[PANEL_BOTTOM])
1293 h -= current_panel[PANEL_BOTTOM]->height;
1295 if (req->height < h)
1296 req->height = h;
1300 /* The style setting has been changed -- update all panels */
1301 static void panel_set_style(guchar *new)
1303 int os = panel_style;
1305 panel_style = option_get_int("panel_style");
1307 if (os != panel_style)
1309 int i;
1311 icons_update_tip();
1313 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
1315 Panel *panel = current_panel[i];
1317 if (!panel)
1318 continue;
1320 gtk_widget_queue_resize(panel->window);