r4209: Updated Japanese translation (Hiroaki Tanaka).
[rox-filer.git] / ROX-Filer / src / panel.c
blob5ba492665b29216e7e5cec0ac8e0c2cd3aefc549
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2005, the ROX-Filer team.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* panel.c - code for dealing with panel windows */
24 #include "config.h"
26 #include <stdio.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <ctype.h>
30 #include <unistd.h>
31 #include <libxml/parser.h>
33 #include <gtk/gtk.h>
34 #include <gdk/gdkx.h>
36 #include "global.h"
38 #include "panel.h"
39 #include "options.h"
40 #include "choices.h"
41 #include "main.h"
42 #include "type.h"
43 #include "gui_support.h"
44 #include "diritem.h"
45 #include "pixmaps.h"
46 #include "filer.h"
47 #include "display.h"
48 #include "bind.h"
49 #include "dnd.h"
50 #include "support.h"
51 #include "icon.h"
52 #include "run.h"
53 #include "appinfo.h"
54 #include "pixmaps.h"
55 #include "xml.h"
56 #include "pinboard.h" /* For pinboard_get_window() */
58 /* The width of the separator at the inner edge of the panel */
59 #define EDGE_WIDTH 2
61 /* The gap between panel icons */
62 #define PANEL_ICON_SPACING 8
64 enum {TEXT_BESIDE_ICON, TEXT_UNDER_ICON};
66 static gboolean tmp_icon_selected = FALSE; /* When dragging */
68 typedef struct _PanelIconClass PanelIconClass;
69 typedef struct _PanelIcon PanelIcon;
71 struct _PanelIconClass {
72 IconClass parent;
75 struct _PanelIcon {
76 Icon icon;
77 GdkPixbuf *image;
79 Panel *panel;
80 GtkWidget *widget; /* The drawing area for the icon */
81 GtkWidget *label;
82 GtkWidget *socket; /* For applets */
84 int style;
87 #define PANEL_ICON(obj) GTK_CHECK_CAST((obj), panel_icon_get_type(), PanelIcon)
88 #define IS_PANEL_ICON(obj) \
89 G_TYPE_CHECK_INSTANCE_TYPE((obj), panel_icon_get_type())
91 Panel *current_panel[PANEL_NUMBER_OF_SIDES];
93 /* NULL => Not loading a panel */
94 static Panel *loading_panel = NULL;
96 static GtkWidget *panel_options_dialog = NULL;
98 /* Static prototypes */
99 static int panel_delete(GtkWidget *widget, GdkEvent *event, Panel *panel);
100 static void panel_destroyed(GtkWidget *widget, Panel *panel);
101 static const char *pan_from_file(gchar *line);
102 static gint icon_button_release(GtkWidget *widget,
103 GdkEventButton *event,
104 PanelIcon *pi);
105 static gint icon_button_press(GtkWidget *widget,
106 GdkEventButton *event,
107 PanelIcon *pi);
108 static void reposition_panel(GtkWidget *window,
109 GtkAllocation *alloc, Panel *panel);
110 static gint expose_icon(GtkWidget *widget,
111 GdkEventExpose *event,
112 PanelIcon *pi);
113 static gint draw_icon(GtkWidget *widget,
114 GdkRectangle *badarea,
115 PanelIcon *pi);
116 static gint panel_button_release(GtkWidget *widget,
117 GdkEventButton *event,
118 Panel *panel);
119 static gint panel_button_press(GtkWidget *widget,
120 GdkEventButton *event,
121 Panel *panel);
122 static void panel_post_resize(GtkWidget *box,
123 GtkRequisition *req, Panel *panel);
124 static void drag_set_panel_dest(PanelIcon *pi);
125 static void add_uri_list(GtkWidget *widget,
126 GdkDragContext *context,
127 gint x,
128 gint y,
129 GtkSelectionData *selection_data,
130 guint info,
131 guint32 time,
132 Panel *panel);
133 static void panel_add_item(Panel *panel,
134 const gchar *path,
135 const gchar *name,
136 gboolean after,
137 const gchar *shortcut,
138 const gchar *args);
139 static gboolean panel_drag_motion(GtkWidget *widget,
140 GdkDragContext *context,
141 gint x,
142 gint y,
143 guint time,
144 Panel *panel);
145 static gboolean insert_drag_motion(GtkWidget *widget,
146 GdkDragContext *context,
147 gint x,
148 gint y,
149 guint time,
150 Panel *panel);
151 static gboolean drag_motion(GtkWidget *widget,
152 GdkDragContext *context,
153 gint x,
154 gint y,
155 guint time,
156 PanelIcon *pi);
157 static void panel_drag_leave(GtkWidget *widget,
158 GdkDragContext *context,
159 guint32 time,
160 Panel *panel);
161 static void drag_leave(GtkWidget *widget,
162 GdkDragContext *context,
163 guint32 time,
164 Icon *icon);
165 static GtkWidget *make_insert_frame(Panel *panel);
166 static gboolean enter_icon(GtkWidget *widget,
167 GdkEventCrossing *event,
168 Icon *icon);
169 static gint icon_motion_event(GtkWidget *widget,
170 GdkEventMotion *event,
171 PanelIcon *pi);
172 static gint panel_leave_event(GtkWidget *widget,
173 GdkEventCrossing *event,
174 Panel *panel);
175 static gint panel_motion_event(GtkWidget *widget,
176 GdkEventMotion *event,
177 Panel *panel);
178 static void reposition_icon(PanelIcon *pi, int index);
179 static void start_drag(PanelIcon *pi, GdkEventMotion *event);
180 static void drag_end(GtkWidget *widget,
181 GdkDragContext *context,
182 Icon *icon);
183 static void perform_action(Panel *panel,
184 PanelIcon *pi,
185 GdkEventButton *event);
186 static void run_applet(PanelIcon *pi);
187 static void size_request(GtkWidget *widget, GtkRequisition *req, PanelIcon *pi);
188 static void panel_load_from_xml(Panel *panel, xmlDocPtr doc);
189 static gboolean draw_panel_edge(GtkWidget *widget, GdkEventExpose *event,
190 Panel *panel);
191 static PanelIcon *panel_icon_new(Panel *panel,
192 const char *pathname,
193 const char *name);
194 static GType panel_icon_get_type(void);
195 static gboolean panel_want_show_text(PanelIcon *pi);
196 static void panel_show_menu(GdkEventButton *event, PanelIcon *pi, Panel *panel);
197 static void panel_style_changed(void);
198 static void motion_may_raise(Panel *panel, int x, int y);
199 static void panel_update(Panel *panel);
200 static gboolean panel_check_xinerama(void);
201 static GList *build_monitor_number(Option *option,
202 xmlNode *node, guchar *label);
203 static gboolean may_autoscroll(Panel *panel);
204 static void panel_show_options(gpointer data);
207 static GtkWidget *dnd_highlight = NULL; /* (stops flickering) */
209 #define SHOW_BOTH 0
210 #define SHOW_APPS_SMALL 1
211 #define SHOW_ICON 2
212 static Option o_panel_style;
213 static Option o_panel_width;
214 static Option o_panel_xinerama;
215 static Option o_panel_monitor;
216 static Option o_panel_avoid;
217 static Option o_panel_is_dock;
219 static gint panel_monitor = -1;
220 GdkRectangle panel_geometry;
222 static int closing_panel = 0; /* Don't panel_save; destroying! */
224 /****************************************************************
225 * EXTERNAL INTERFACE *
226 ****************************************************************/
228 void panel_init(void)
230 option_add_int(&o_panel_style, "panel_style", SHOW_APPS_SMALL);
231 option_add_int(&o_panel_width, "panel_width", 52);
233 option_add_int(&o_panel_xinerama, "panel_xinerama", 0);
234 option_add_int(&o_panel_monitor, "panel_monitor", 0);
236 option_add_int(&o_panel_avoid, "panel_avoid", TRUE);
237 option_add_int(&o_panel_is_dock, "panel_is_dock", FALSE);
239 option_add_notify(panel_style_changed);
241 option_register_widget("monitor-number", build_monitor_number);
243 panel_check_xinerama();
246 /* Return a free edge for a new panel.
247 * If no edge is free, returns PANEL_BOTTOM.
249 static PanelSide find_free_side()
251 if (!current_panel[PANEL_BOTTOM])
252 return PANEL_BOTTOM;
254 if (!current_panel[PANEL_TOP])
255 return PANEL_TOP;
257 if (!current_panel[PANEL_LEFT])
258 return PANEL_LEFT;
260 if (!current_panel[PANEL_RIGHT])
261 return PANEL_RIGHT;
263 return PANEL_BOTTOM;
266 /* 'name' may be NULL or "" to remove the panel */
267 Panel *panel_new(const gchar *name, PanelSide side)
269 guchar *load_path;
270 Panel *panel;
271 GtkWidget *vp, *box, *frame, *align;
272 xmlDocPtr panel_doc = NULL;
273 gboolean need_resave = FALSE;
275 g_return_val_if_fail(side == PANEL_DEFAULT_SIDE ||
276 (side >= 0 && side < PANEL_NUMBER_OF_SIDES), NULL);
277 g_return_val_if_fail(loading_panel == NULL, NULL);
279 if (name && *name == '\0')
280 name = NULL;
282 if (!name)
283 load_path = NULL;
284 else if (strchr(name, '/'))
285 load_path = g_strdup(name);
286 else
288 guchar *leaf;
290 leaf = g_strconcat("pan_", name, NULL);
291 load_path = choices_find_xdg_path_load(leaf, PROJECT, SITE);
292 g_free(leaf);
295 if (load_path && access(load_path, F_OK) == 0)
297 char *saved_side;
298 xmlNodePtr root;
300 panel_doc = xmlParseFile(load_path);
301 root = xmlDocGetRootElement(panel_doc);
303 saved_side = xmlGetProp(root, "side");
304 if (saved_side)
306 PanelSide old_side;
307 old_side = panel_name_to_side(saved_side);
308 g_free(saved_side);
310 if (side == PANEL_DEFAULT_SIDE)
311 side = old_side;
312 else if (side != old_side)
313 need_resave = TRUE;
315 else
316 need_resave = TRUE;
319 if (side == PANEL_DEFAULT_SIDE)
320 side = find_free_side();
322 if (current_panel[side])
324 if (name)
325 number_of_windows++;
326 closing_panel++;
327 gtk_widget_destroy(current_panel[side]->window);
328 closing_panel--;
329 if (name)
330 number_of_windows--;
333 if (name == NULL || *name == '\0')
334 return NULL;
336 panel = g_new(Panel, 1);
337 panel->name = g_strdup(name);
338 panel->side = side;
339 panel->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
340 panel->autoscroll_speed = 0;
341 gtk_window_set_resizable(GTK_WINDOW(panel->window), FALSE);
342 gtk_window_set_wmclass(GTK_WINDOW(panel->window), "ROX-Panel", PROJECT);
343 gtk_widget_set_name(panel->window, "rox-panel");
344 gtk_widget_set_events(panel->window,
345 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
346 GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
348 /* We make the panel a drop target only so that we can auto-raise! */
349 gtk_drag_dest_set(panel->window, 0, NULL, 0, GDK_ACTION_PRIVATE);
350 g_signal_connect(panel->window, "drag_leave",
351 G_CALLBACK(panel_drag_leave), panel);
352 g_signal_connect(panel->window, "drag_motion",
353 G_CALLBACK(panel_drag_motion), panel);
355 g_signal_connect(panel->window, "delete-event",
356 G_CALLBACK(panel_delete), panel);
357 g_signal_connect(panel->window, "destroy",
358 G_CALLBACK(panel_destroyed), panel);
359 g_signal_connect(panel->window, "button_press_event",
360 G_CALLBACK(panel_button_press), panel);
361 g_signal_connect(panel->window, "button_release_event",
362 G_CALLBACK(panel_button_release), panel);
363 g_signal_connect(panel->window, "motion-notify-event",
364 G_CALLBACK(panel_motion_event), panel);
365 g_signal_connect(panel->window, "leave-notify-event",
366 G_CALLBACK(panel_leave_event), panel);
368 if (panel->side == PANEL_RIGHT)
369 align = gtk_alignment_new(1.0, 0.0, 0.0, 1.0);
370 else if (panel->side == PANEL_BOTTOM)
371 align = gtk_alignment_new(0.0, 1.0, 1.0, 0.0);
372 else if (panel->side == PANEL_TOP)
373 align = gtk_alignment_new(0.0, 0.0, 1.0, 0.0);
374 else
375 align = gtk_alignment_new(0.0, 0.0, 0.0, 1.0);
377 gtk_container_add(GTK_CONTAINER(panel->window), align);
379 vp = gtk_viewport_new(NULL, NULL);
380 gtk_container_set_resize_mode(GTK_CONTAINER(vp), GTK_RESIZE_PARENT);
381 gtk_viewport_set_shadow_type(GTK_VIEWPORT(vp), GTK_SHADOW_NONE);
382 gtk_container_add(GTK_CONTAINER(align), vp);
384 g_signal_connect(align, "expose-event",
385 G_CALLBACK(draw_panel_edge), panel);
387 if (side == PANEL_TOP || side == PANEL_BOTTOM)
389 panel->adj = gtk_viewport_get_hadjustment(GTK_VIEWPORT(vp));
390 box = gtk_hbox_new(FALSE, 0);
391 panel->before = gtk_hbox_new(FALSE, 0);
392 panel->after = gtk_hbox_new(FALSE, 0);
394 else
396 panel->adj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(vp));
397 box = gtk_vbox_new(FALSE, 0);
398 panel->before = gtk_vbox_new(FALSE, 0);
399 panel->after = gtk_vbox_new(FALSE, 0);
402 gtk_container_add(GTK_CONTAINER(vp), box);
403 gtk_box_pack_start(GTK_BOX(box), panel->before, FALSE, TRUE, 0);
404 gtk_box_pack_end(GTK_BOX(box), panel->after, FALSE, TRUE, 0);
406 frame = make_insert_frame(panel);
407 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
409 /* This is used so that we can find the middle easily! */
410 panel->gap = gtk_event_box_new();
411 gtk_box_pack_start(GTK_BOX(box), panel->gap, FALSE, FALSE, 0);
413 frame = make_insert_frame(panel);
414 g_object_set_data(G_OBJECT(frame), "after", "yes");
415 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
417 if (o_panel_is_dock.int_value)
418 gtk_window_set_type_hint(GTK_WINDOW(panel->window),
419 GDK_WINDOW_TYPE_HINT_DOCK);
421 gtk_widget_realize(panel->window);
422 make_panel_window(panel->window);
423 gtk_window_stick(GTK_WINDOW(panel->window));
425 gtk_widget_show_all(align);
427 loading_panel = panel;
428 if (panel_doc)
430 panel_load_from_xml(panel, panel_doc);
431 xmlFreeDoc(panel_doc);
433 if (need_resave)
434 panel_save(panel);
436 else if (load_path)
438 parse_file(load_path, pan_from_file);
439 info_message(_("Your old panel file has been "
440 "converted to the new XML format."));
441 panel_save(panel);
443 else
445 /* Don't scare users with an empty panel... */
446 guchar *apps;
448 panel_add_item(panel, "~", "Home", FALSE, NULL, NULL);
450 apps = pathdup(make_path(app_dir, ".."));
451 if (apps)
453 panel_add_item(panel, apps, "Apps", FALSE, NULL, NULL);
454 g_free(apps);
457 loading_panel = NULL;
458 g_free(load_path);
460 current_panel[side] = panel;
462 gtk_widget_queue_resize(box);
463 g_signal_connect(panel->window, "size-request",
464 G_CALLBACK(panel_post_resize), panel);
465 g_signal_connect(panel->window, "size-allocate",
466 G_CALLBACK(reposition_panel), panel);
468 number_of_windows++;
469 gdk_window_lower(panel->window->window);
470 gtk_widget_show(panel->window);
471 /* This has no effect until after window is showing; GTK+ bug? */
472 keep_below(panel->window->window, TRUE);
475 GdkWindow *pinboard;
477 pinboard = pinboard_get_window();
478 /* (if pinboard is NULL, will go right to the back) */
479 window_put_just_above(panel->window->window, pinboard);
482 return panel;
485 /* Externally visible function to add an item to a panel */
486 gboolean panel_add(PanelSide side,
487 const gchar *path, const gchar *label, gboolean after, const gchar *shortcut, const gchar *args)
489 g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, FALSE);
491 g_return_val_if_fail(current_panel[side] != NULL, FALSE);
493 panel_add_item(current_panel[side], path, label, after, shortcut, args);
495 return TRUE;
498 /* Add the area covered by the panels to the region */
499 void panel_mark_used(GdkRegion *used)
501 int i;
503 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
505 Panel *panel = current_panel[i];
506 GdkRectangle rect;
508 if (!panel)
509 continue;
511 gdk_window_get_root_origin(panel->window->window,
512 &rect.x, &rect.y);
513 rect.width = panel->window->allocation.width;
514 rect.height = panel->window->allocation.height;
516 gdk_region_union_with_rect(used, &rect);
520 /* On xrandr screen size changes, update all panels */
521 void panel_update_size(void)
523 int i;
525 panel_check_xinerama();
527 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
529 if (current_panel[i])
531 reposition_panel(current_panel[i]->window,
532 &current_panel[i]->window->allocation,
533 current_panel[i]);
534 gtk_widget_queue_resize(current_panel[i]->window);
539 /****************************************************************
540 * INTERNAL FUNCTIONS *
541 ****************************************************************/
543 /* User has tried to close the panel via the window manager - confirm */
544 static int panel_delete(GtkWidget *widget, GdkEvent *event, Panel *panel)
546 return !confirm(_("You have tried to close a panel via the window "
547 "manager - I usually find that this is accidental... "
548 "really close?"),
549 GTK_STOCK_CLOSE, NULL);
552 static void panel_destroyed(GtkWidget *widget, Panel *panel)
554 if (panel_options_dialog)
556 PanelSide side;
557 side = GPOINTER_TO_INT(g_object_get_data(
558 G_OBJECT(panel_options_dialog),
559 "rox-panel-side"));
560 if (side == panel->side)
561 gtk_widget_destroy(panel_options_dialog);
564 if (current_panel[panel->side] == panel)
565 current_panel[panel->side] = NULL;
567 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
569 if (current_panel[PANEL_RIGHT])
570 gtk_widget_queue_resize(
571 current_panel[PANEL_RIGHT]->window);
572 if (current_panel[PANEL_LEFT])
573 gtk_widget_queue_resize(
574 current_panel[PANEL_LEFT]->window);
577 if (panel->autoscroll_speed)
578 g_source_remove(panel->autoscroll_to);
580 g_free(panel->name);
581 g_free(panel);
583 one_less_window();
586 static void panel_load_side(Panel *panel, xmlNodePtr side, gboolean after)
588 xmlNodePtr node;
589 char *label, *path, *shortcut, *args;
591 for (node = side->xmlChildrenNode; node; node = node->next)
593 if (node->type != XML_ELEMENT_NODE)
594 continue;
595 if (strcmp(node->name, "icon") != 0)
596 continue;
598 label = xmlGetProp(node, "label");
599 if (!label)
600 label = g_strdup("<missing label>");
601 path = xmlNodeGetContent(node);
602 if (!path)
603 path = g_strdup("<missing path>");
604 shortcut = xmlGetProp(node, "shortcut");
605 args = xmlGetProp(node, "args");
607 panel_add_item(panel, path, label, after, shortcut, args);
609 g_free(path);
610 g_free(label);
611 g_free(shortcut);
612 g_free(args);
616 /* Create one panel icon for each icon in the doc */
617 static void panel_load_from_xml(Panel *panel, xmlDocPtr doc)
619 xmlNodePtr root;
621 root = xmlDocGetRootElement(doc);
622 panel_load_side(panel, get_subnode(root, NULL, "start"), FALSE);
623 panel_load_side(panel, get_subnode(root, NULL, "end"), TRUE);
626 /* Called for each line in the config file while loading a new panel */
627 static const char *pan_from_file(gchar *line)
629 gchar *sep, *leaf;
631 g_return_val_if_fail(line != NULL, NULL);
632 g_return_val_if_fail(loading_panel != NULL, NULL);
634 if (*line == '\0')
635 return NULL;
637 sep = strpbrk(line, "<>");
638 if (!sep)
639 return _("Missing < or > in panel config file");
641 if (sep != line)
642 leaf = g_strndup(line, sep - line);
643 else
644 leaf = NULL;
646 panel_add_item(loading_panel, sep + 1, leaf, sep[0] == '>',
647 NULL, NULL);
649 g_free(leaf);
651 return NULL;
654 static gboolean icon_pointer_in(GtkWidget *widget,
655 GdkEventCrossing *event,
656 Icon *icon)
658 gtk_widget_set_state(widget,
659 icon->selected ? GTK_STATE_SELECTED : GTK_STATE_PRELIGHT);
661 return 0;
664 static gboolean icon_pointer_out(GtkWidget *widget,
665 GdkEventCrossing *event,
666 Icon *icon)
668 gtk_widget_set_state(widget,
669 icon->selected ? GTK_STATE_SELECTED : GTK_STATE_NORMAL);
671 return 0;
674 static void panel_icon_destroyed(PanelIcon *pi)
676 g_return_if_fail(pi->widget != NULL);
678 pi->widget = NULL;
680 g_object_unref(pi);
683 /* Set the tooltip AND hide/show the label */
684 static void panel_icon_set_tip(PanelIcon *pi)
686 XMLwrapper *ai;
687 xmlNode *node;
688 Icon *icon = (Icon *) pi;
690 g_return_if_fail(pi != NULL);
692 if (pi->label)
694 if (panel_want_show_text(pi))
695 gtk_widget_show(pi->label);
696 else
697 gtk_widget_hide(pi->label);
700 if (pi->socket)
701 ai = NULL;
702 else
703 ai = appinfo_get(icon->path, icon->item);
705 if (ai && ((node = xml_get_section(ai, NULL, "Summary"))))
707 guchar *str;
708 str = xmlNodeListGetString(node->doc,
709 node->xmlChildrenNode, 1);
710 if (str)
712 gtk_tooltips_set_tip(tooltips, pi->widget, str, NULL);
713 g_free(str);
716 else if ((!panel_want_show_text(pi)) && !pi->socket)
718 if (icon->item->leafname && icon->item->leafname[0])
719 gtk_tooltips_set_tip(tooltips, pi->widget,
720 icon->item->leafname, NULL);
722 else
723 gtk_tooltips_set_tip(tooltips, pi->widget, NULL, NULL);
725 if (ai)
726 g_object_unref(ai);
729 /* Add an icon with this path to the panel. If after is TRUE then the
730 * icon is added to the right/bottom end of the panel.
732 * If name is NULL a suitable name is taken from path.
734 static void panel_add_item(Panel *panel,
735 const gchar *path,
736 const gchar *name,
737 gboolean after,
738 const gchar *shortcut,
739 const gchar *args)
741 GtkWidget *widget;
742 PanelIcon *pi;
743 Icon *icon;
745 g_return_if_fail(panel != NULL);
746 g_return_if_fail(path != NULL);
748 widget = gtk_event_box_new();
749 gtk_widget_set_events(widget,
750 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
751 GDK_BUTTON3_MOTION_MASK |
752 GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
753 GDK_BUTTON_RELEASE_MASK);
755 gtk_box_pack_start(GTK_BOX(after ? panel->after : panel->before),
756 widget, FALSE, TRUE, 0);
757 if (after)
758 gtk_box_reorder_child(GTK_BOX(panel->after), widget, 0);
760 gtk_widget_realize(widget);
762 pi = panel_icon_new(panel, path, name);
763 icon = (Icon *) pi;
765 /* Widget takes the initial ref of Icon */
766 g_object_set_data(G_OBJECT(widget), "icon", pi);
768 pi->widget = widget;
769 g_object_ref(widget);
771 gtk_widget_set_name(pi->widget, "panel-icon");
773 g_signal_connect_swapped(widget, "destroy",
774 G_CALLBACK(panel_icon_destroyed), pi);
776 if (icon->item->base_type == TYPE_DIRECTORY)
777 run_applet(pi);
779 g_signal_connect(widget, "button_release_event",
780 G_CALLBACK(icon_button_release), pi);
781 g_signal_connect(widget, "button_press_event",
782 G_CALLBACK(icon_button_press), pi);
783 g_signal_connect(widget, "motion-notify-event",
784 G_CALLBACK(icon_motion_event), pi);
785 g_signal_connect(widget, "enter-notify-event",
786 G_CALLBACK(icon_pointer_in), pi);
787 g_signal_connect(widget, "leave-notify-event",
788 G_CALLBACK(icon_pointer_out), pi);
790 if (!pi->socket)
792 g_signal_connect(widget, "enter-notify-event",
793 G_CALLBACK(enter_icon), pi);
794 g_signal_connect_after(widget, "expose_event",
795 G_CALLBACK(expose_icon), pi);
796 g_signal_connect(widget, "drag_data_get",
797 G_CALLBACK(drag_data_get), NULL);
799 g_signal_connect(widget, "size_request",
800 G_CALLBACK(size_request), pi);
802 drag_set_panel_dest(pi);
804 pi->label = gtk_label_new(icon->item->leafname);
805 gtk_container_add(GTK_CONTAINER(pi->widget), pi->label);
806 gtk_misc_set_alignment(GTK_MISC(pi->label), 0.5, 1);
807 gtk_misc_set_padding(GTK_MISC(pi->label), 1, 2);
810 icon_set_shortcut(icon, shortcut);
811 icon_set_arguments(icon, args);
813 if (!loading_panel)
814 panel_save(panel);
816 panel_icon_set_tip(pi);
817 gtk_widget_show(widget);
820 static gboolean remove_item_from_side(GtkWidget *container, const gchar *path,
821 const gchar *label)
823 GList *kids, *next;
824 gboolean found = FALSE;
826 kids = gtk_container_get_children(GTK_CONTAINER(container));
828 for (next = kids; next; next = next->next)
830 Icon *icon;
831 icon = g_object_get_data(G_OBJECT(next->data), "icon");
832 if (!icon)
833 continue;
835 if (strcmp(path, icon->src_path) == 0 &&
836 (!label || strcmp(label, icon->item->leafname)==0))
838 icon_destroy(icon);
839 found = TRUE;
840 break;
844 g_list_free(kids);
846 return found;
849 /* Remove an item with this path. If more than one item matches, only
850 * one is removed. If label is not NULL then it must also match the item.
851 * Returns TRUE if an item was successfully removed.
853 gboolean panel_remove_item(PanelSide side, const gchar *path,
854 const gchar *label)
856 Panel *panel;
858 g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, FALSE);
859 g_return_val_if_fail(path != NULL, FALSE);
861 panel = current_panel[side];
862 if (!panel)
864 g_warning("No panel on this side of the screen!");
865 return FALSE;
868 if (remove_item_from_side(panel->before, path, label) ||
869 remove_item_from_side(panel->after, path, label))
871 panel_save(panel);
872 panel_update(panel);
873 return TRUE;
877 g_warning("Panel item '%s' not found", path);
878 return FALSE;
881 /* Called when Gtk+ wants to know how much space an icon needs.
882 * 'req' is already big enough for the label, if shown.
884 static void size_request(GtkWidget *widget, GtkRequisition *req, PanelIcon *pi)
886 Icon *icon = (Icon *) pi;
887 gboolean horz = (pi->panel->side == PANEL_TOP ||
888 pi->panel->side == PANEL_BOTTOM);
889 int max_width = 100;
890 int max_height = 100;
891 int image_width, image_height;
893 if (horz)
894 max_height = o_panel_width.int_value - req->height;
895 else
896 max_width = MAX(o_panel_width.int_value, req->width);
898 /* TODO: really need to recreate? */
899 if (pi->image)
900 g_object_unref(pi->image);
902 pi->image = scale_pixbuf(di_image(icon->item)->src_pixbuf,
903 MAX(20, max_width), MAX(20, max_height));
905 image_width = gdk_pixbuf_get_width(pi->image);
906 image_height = gdk_pixbuf_get_height(pi->image);
908 if (req->height > 0 && max_height < req->height)
910 pi->style = TEXT_BESIDE_ICON;
911 req->width += image_width;
912 req->height = MAX(req->height, image_height);
913 gtk_misc_set_alignment(GTK_MISC(pi->label), 1, 0.5);
915 else
917 pi->style = TEXT_UNDER_ICON;
918 req->width = MAX(req->width, image_width);
919 req->height += image_height;
920 gtk_misc_set_alignment(GTK_MISC(pi->label), 0.5, 1);
923 if (horz)
924 req->width += PANEL_ICON_SPACING;
925 else
926 req->height += PANEL_ICON_SPACING;
929 static gint expose_icon(GtkWidget *widget,
930 GdkEventExpose *event,
931 PanelIcon *pi)
933 return draw_icon(widget, &event->area, pi);
936 static gint draw_icon(GtkWidget *widget, GdkRectangle *badarea, PanelIcon *pi)
938 GdkRectangle area;
939 int width, height;
940 Icon *icon = (Icon *) pi;
941 int image_x;
942 int image_y;
943 GdkPixbuf *image;
944 int text_height = 0;
946 gdk_drawable_get_size(widget->window, &area.width, &area.height);
948 if (panel_want_show_text(pi))
949 text_height = pi->label->requisition.height;
951 g_return_val_if_fail(pi->image != NULL, FALSE);
953 image = pi->image;
955 width = gdk_pixbuf_get_width(image);
956 height = gdk_pixbuf_get_height(image);
958 if (pi->style == TEXT_UNDER_ICON)
960 image_x = (area.width - width) >> 1;
961 image_y = (area.height - height - text_height) >> 1;
963 else
965 image_x = PANEL_ICON_SPACING - 2;
966 image_y = (area.height - height) >> 1;
969 gdk_pixbuf_render_to_drawable_alpha(
970 image,
971 widget->window,
972 0, 0, /* src */
973 image_x, image_y, /* dest */
974 width, height,
975 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
976 GDK_RGB_DITHER_NORMAL, 0, 0);
978 if (icon->item->flags & ITEM_FLAG_SYMLINK)
980 gdk_pixbuf_render_to_drawable_alpha(im_symlink->pixbuf,
981 widget->window,
982 0, 0, /* src */
983 image_x, image_y + 2, /* dest */
984 -1, -1,
985 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
986 GDK_RGB_DITHER_NORMAL, 0, 0);
988 if (icon->item->flags & ITEM_FLAG_MOUNT_POINT)
990 MaskedPixmap *mp = icon->item->flags & ITEM_FLAG_MOUNTED
991 ? im_mounted
992 : im_unmounted;
994 gdk_pixbuf_render_to_drawable_alpha(mp->pixbuf,
995 widget->window,
996 0, 0, /* src */
997 image_x, image_y + 2, /* dest */
998 -1, -1,
999 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
1000 GDK_RGB_DITHER_NORMAL, 0, 0);
1002 return FALSE;
1005 static void panel_icon_wink(Icon *icon)
1007 PanelIcon *pi = (PanelIcon *) icon;
1009 wink_widget(pi->widget);
1012 /* icon may be NULL if the event is on the background */
1013 static void perform_action(Panel *panel, PanelIcon *pi, GdkEventButton *event)
1015 BindAction action;
1016 Icon *icon = (Icon *) pi;
1018 action = bind_lookup_bev(icon ? BIND_PANEL_ICON : BIND_PANEL, event);
1020 if (pi && pi->socket)
1021 if (action != ACT_POPUP_MENU && action != ACT_MOVE_ICON)
1022 return;
1024 switch (action)
1026 case ACT_OPEN_ITEM:
1027 dnd_motion_ungrab();
1028 wink_widget(pi->widget);
1029 icon_run(icon);
1030 break;
1031 case ACT_EDIT_ITEM:
1032 dnd_motion_ungrab();
1033 wink_widget(pi->widget);
1034 run_diritem(icon->path, icon->item, NULL, NULL, TRUE);
1035 break;
1036 case ACT_POPUP_MENU:
1037 dnd_motion_ungrab();
1038 panel_show_menu(event, pi, panel);
1039 break;
1040 case ACT_MOVE_ICON:
1041 dnd_motion_start(MOTION_REPOSITION);
1042 break;
1043 case ACT_PRIME_AND_SELECT:
1044 if (!icon->selected)
1045 icon_select_only(icon);
1046 dnd_motion_start(MOTION_READY_FOR_DND);
1047 break;
1048 case ACT_PRIME_AND_TOGGLE:
1049 icon_set_selected(icon, !icon->selected);
1050 dnd_motion_start(MOTION_READY_FOR_DND);
1051 break;
1052 case ACT_PRIME_FOR_DND:
1053 dnd_motion_start(MOTION_READY_FOR_DND);
1054 break;
1055 case ACT_TOGGLE_SELECTED:
1056 icon_set_selected(icon, !icon->selected);
1057 break;
1058 case ACT_SELECT_EXCL:
1059 icon_set_selected(icon, TRUE);
1060 break;
1061 case ACT_IGNORE:
1062 break;
1063 case ACT_CLEAR_SELECTION:
1064 dnd_motion_ungrab();
1065 icon_select_only(NULL);
1066 break;
1067 default:
1068 g_warning("Unsupported action : %d\n", action);
1069 break;
1073 static gint panel_button_release(GtkWidget *widget,
1074 GdkEventButton *event,
1075 Panel *panel)
1077 if (dnd_motion_release(event))
1078 return TRUE;
1080 perform_action(panel, NULL, event);
1082 return TRUE;
1085 static gint panel_button_press(GtkWidget *widget,
1086 GdkEventButton *event,
1087 Panel *panel)
1089 if (dnd_motion_press(panel->window, event))
1090 perform_action(panel, NULL, event);
1092 return TRUE;
1095 static gint icon_button_release(GtkWidget *widget,
1096 GdkEventButton *event,
1097 PanelIcon *pi)
1099 if (pi->socket && event->button == 1)
1100 return FALSE; /* Restart button */
1102 if (dnd_motion_release(event))
1103 return TRUE;
1105 perform_action(pi->panel, pi, event);
1107 return TRUE;
1110 static gint icon_button_press(GtkWidget *widget,
1111 GdkEventButton *event,
1112 PanelIcon *pi)
1114 if (pi->socket && event->button == 1)
1115 return FALSE; /* Restart button */
1117 if (dnd_motion_press(widget, event))
1118 perform_action(pi->panel, pi, event);
1120 return TRUE;
1123 static void reposition_panel(GtkWidget *window,
1124 GtkAllocation *alloc, Panel *panel)
1126 int x = panel_geometry.x;
1127 int y = panel_geometry.y;
1128 int thickness;
1129 PanelSide side = panel->side;
1131 if (side == PANEL_LEFT || side == PANEL_RIGHT)
1133 if (side == PANEL_RIGHT)
1134 x += panel_geometry.width - alloc->width;
1136 if (current_panel[PANEL_TOP])
1138 GtkWidget *win = current_panel[PANEL_TOP]->window;
1139 y += win->allocation.height;
1143 if (side == PANEL_BOTTOM)
1144 y += panel_geometry.height - alloc->height;
1146 gtk_window_move(GTK_WINDOW(panel->window), x, y);
1147 gdk_window_move(panel->window->window, x, y);
1149 if (side == PANEL_BOTTOM || side == PANEL_TOP)
1151 if (current_panel[PANEL_RIGHT])
1152 gtk_widget_queue_resize(
1153 current_panel[PANEL_RIGHT]->window);
1154 if (current_panel[PANEL_LEFT])
1155 gtk_widget_queue_resize(
1156 current_panel[PANEL_LEFT]->window);
1159 /* Stop windows from maximising over all/part of us */
1161 struct {
1162 gulong left, right, top, bottom;
1163 gulong left_start_y, left_end_y;
1164 gulong right_start_y, right_end_y;
1165 gulong top_start_x, top_end_x;
1166 gulong bottom_start_x, bottom_end_x;
1167 } strut = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1169 if (o_panel_avoid.int_value == FALSE)
1170 thickness = 2;
1171 else if (panel->side == PANEL_TOP ||
1172 panel->side == PANEL_BOTTOM)
1173 thickness = alloc->height;
1174 else
1175 thickness = alloc->width;
1177 switch (panel->side)
1179 case PANEL_LEFT:
1180 if (!o_panel_xinerama.int_value ||
1181 !monitor_adjacent
1182 [o_panel_monitor.int_value].left)
1184 strut.left = panel_geometry.x +
1185 thickness;
1186 strut.left_start_y = panel_geometry.y;
1187 strut.left_end_y = panel_geometry.y +
1188 panel_geometry.height - 1;
1190 /* else there is (part of) a monitor
1191 * to the left */
1192 else
1194 thickness = 0;
1196 break;
1197 case PANEL_RIGHT:
1198 if (!o_panel_xinerama.int_value ||
1199 !monitor_adjacent
1200 [o_panel_monitor.int_value].right)
1202 /* RHS of monitor might not abut edge
1203 * of total virtual screen */
1204 strut.right = screen_width -
1205 panel_geometry.x -
1206 panel_geometry.width +
1207 thickness;
1208 strut.right_start_y = panel_geometry.y;
1209 strut.right_end_y = panel_geometry.y +
1210 panel_geometry.height - 1;
1212 /* else there is (part of) a monitor
1213 * to the right */
1214 else
1216 thickness = 0;
1218 break;
1219 case PANEL_TOP:
1220 if (!o_panel_xinerama.int_value ||
1221 !monitor_adjacent
1222 [o_panel_monitor.int_value].top)
1224 strut.top = panel_geometry.y +
1225 thickness;
1226 strut.top_start_x = panel_geometry.x;
1227 strut.top_end_x = panel_geometry.x +
1228 panel_geometry.width - 1;
1230 /* else there is (part of) a monitor above */
1231 else
1233 thickness = 0;
1235 break;
1236 default: /* PANEL_BOTTOM */
1237 if (!o_panel_xinerama.int_value ||
1238 !monitor_adjacent
1239 [o_panel_monitor.int_value].bottom)
1241 /* Bottom of monitor might not abut
1242 * edge of total virtual screen */
1243 strut.bottom = screen_height -
1244 panel_geometry.y -
1245 panel_geometry.height +
1246 thickness;
1247 strut.bottom_start_x = panel_geometry.x;
1248 strut.bottom_end_x = panel_geometry.x +
1249 panel_geometry.width - 1;
1251 /* else there is (part of) a monitor below */
1252 else
1254 thickness = 0;
1256 break;
1259 if (thickness)
1261 /* Set full-width strut as well as partial in case
1262 * partial isn't supported by wm */
1263 gdk_property_change(panel->window->window,
1264 gdk_atom_intern("_NET_WM_STRUT",
1265 FALSE),
1266 gdk_atom_intern("CARDINAL", FALSE),
1267 32, GDK_PROP_MODE_REPLACE,
1268 (gchar *) &strut, 4);
1269 gdk_property_change(panel->window->window,
1270 gdk_atom_intern("_NET_WM_STRUT_PARTIAL",
1271 FALSE),
1272 gdk_atom_intern("CARDINAL", FALSE),
1273 32, GDK_PROP_MODE_REPLACE,
1274 (gchar *) &strut, 12);
1276 else
1278 gdk_property_delete(panel->window->window,
1279 gdk_atom_intern("_NET_WM_STRUT_PARTIAL",
1280 FALSE));
1281 gdk_property_delete(panel->window->window,
1282 gdk_atom_intern("_NET_WM_STRUT",
1283 FALSE));
1289 /* Same as drag_set_dest(), but for panel icons */
1290 static void drag_set_panel_dest(PanelIcon *pi)
1292 GtkWidget *obj = pi->widget;
1294 make_drop_target(pi->widget, 0);
1296 g_signal_connect(obj, "drag_motion", G_CALLBACK(drag_motion), pi);
1297 g_signal_connect(obj, "drag_leave", G_CALLBACK(drag_leave), pi);
1298 g_signal_connect(obj, "drag_end", G_CALLBACK(drag_end), pi);
1301 static gboolean drag_motion(GtkWidget *widget,
1302 GdkDragContext *context,
1303 gint x,
1304 gint y,
1305 guint time,
1306 PanelIcon *pi)
1308 GdkDragAction action = context->suggested_action;
1309 const char *type = NULL;
1310 Icon *icon = (Icon *) pi;
1311 DirItem *item = icon->item;
1312 int panel_x, panel_y;
1314 gdk_window_get_pointer(pi->panel->window->window,
1315 &panel_x, &panel_y, NULL);
1316 motion_may_raise(pi->panel, panel_x, panel_y);
1318 /* Should we scroll the panel when dragging? */
1319 if (motion_state != MOTION_REPOSITION)
1320 if (pi->panel->autoscroll_speed == 0)
1321 may_autoscroll(pi->panel);
1323 if (icon->selected)
1324 goto out; /* Can't drag a selection to itself */
1326 type = dnd_motion_item(context, &item);
1328 if ((context->actions & GDK_ACTION_ASK) && o_dnd_left_menu.int_value
1329 && type != drop_dest_prog)
1331 guint state;
1332 gdk_window_get_pointer(NULL, NULL, NULL, &state);
1333 if (state & GDK_BUTTON1_MASK)
1334 action = GDK_ACTION_ASK;
1337 if (!item)
1338 type = NULL;
1339 out:
1340 /* We actually must pretend to accept the drop, even if the
1341 * directory isn't writeable, so that the spring-opening
1342 * thing works.
1345 /* Don't allow drops to non-writeable directories */
1346 if (o_dnd_spring_open.int_value == FALSE &&
1347 type == drop_dest_dir &&
1348 access(icon->path, W_OK) != 0)
1350 type = NULL;
1353 g_dataset_set_data(context, "drop_dest_type", (gpointer) type);
1354 if (type)
1356 gdk_drag_status(context, action, time);
1357 g_dataset_set_data_full(context, "drop_dest_path",
1358 g_strdup(icon->path), g_free);
1359 if (type == drop_dest_dir)
1360 dnd_spring_load(context, NULL);
1362 if (dnd_highlight && dnd_highlight != pi->widget)
1364 gtk_drag_unhighlight(dnd_highlight);
1365 dnd_highlight = NULL;
1368 if (dnd_highlight == NULL)
1370 gtk_drag_highlight(pi->widget);
1371 dnd_highlight = pi->widget;
1375 return type != NULL;
1379 static void add_uri_list(GtkWidget *widget,
1380 GdkDragContext *context,
1381 gint x,
1382 gint y,
1383 GtkSelectionData *selection_data,
1384 guint info,
1385 guint32 time,
1386 Panel *panel)
1388 gboolean after = FALSE;
1389 GList *uris, *next;
1391 if (!selection_data->data)
1392 return;
1394 g_return_if_fail(selection_data->data[selection_data->length] == '\0');
1396 if (g_object_get_data(G_OBJECT(widget), "after"))
1397 after = TRUE;
1399 uris = uri_list_to_glist(selection_data->data);
1401 for (next = uris; next; next = next->next)
1403 guchar *path;
1405 path = get_local_path((EscapedPath *) next->data);
1407 if (path) {
1408 panel_add_item(panel, path, NULL, after, NULL, NULL);
1409 g_free(path);
1413 g_list_free(uris);
1416 static void drag_end(GtkWidget *widget,
1417 GdkDragContext *context,
1418 Icon *icon)
1420 if (tmp_icon_selected)
1422 icon_select_only(NULL);
1423 tmp_icon_selected = FALSE;
1427 static void drag_leave(GtkWidget *widget,
1428 GdkDragContext *context,
1429 guint32 time,
1430 Icon *icon)
1432 panel_drag_leave(widget, context, time, ((PanelIcon *) icon)->panel);
1434 if (dnd_highlight && dnd_highlight == widget)
1436 gtk_drag_unhighlight(dnd_highlight);
1437 dnd_highlight = NULL;
1440 dnd_spring_abort();
1443 /* Create XML icon nodes for these widgets.
1444 * Always frees the widgets list.
1446 static void make_widgets(xmlNodePtr side, GList *widgets)
1448 GList *next;
1450 for (next = widgets; next; next = next->next)
1452 Icon *icon;
1453 xmlNodePtr tree;
1455 icon = g_object_get_data(G_OBJECT(next->data), "icon");
1457 if (!icon)
1459 g_warning("Can't find Icon from widget\n");
1460 continue;
1463 tree = xmlNewTextChild(side, NULL, "icon", icon->src_path);
1465 xmlSetProp(tree, "label", icon->item->leafname);
1466 if (icon->shortcut)
1467 xmlSetProp(tree, "shortcut", icon->shortcut);
1468 if (icon->args)
1469 xmlSetProp(tree, "args", icon->args);
1472 if (widgets)
1473 g_list_free(widgets);
1476 void panel_save(Panel *panel)
1478 xmlDocPtr doc;
1479 xmlNodePtr root;
1480 guchar *save = NULL;
1481 guchar *save_new = NULL;
1483 g_return_if_fail(panel != NULL);
1485 if (strchr(panel->name, '/'))
1486 save = g_strdup(panel->name);
1487 else
1489 guchar *leaf;
1491 leaf = g_strconcat("pan_", panel->name, NULL);
1492 save = choices_find_xdg_path_save(leaf, PROJECT, SITE, TRUE);
1493 g_free(leaf);
1496 if (!save)
1497 return;
1499 doc = xmlNewDoc("1.0");
1500 xmlDocSetRootElement(doc, xmlNewDocNode(doc, NULL, "panel", NULL));
1502 root = xmlDocGetRootElement(doc);
1504 xmlSetProp(root, "side",
1505 panel->side == PANEL_TOP ? "Top" :
1506 panel->side == PANEL_BOTTOM ? "Bottom" :
1507 panel->side == PANEL_LEFT ? "Left" :
1508 "Right");
1510 make_widgets(xmlNewChild(root, NULL, "start", NULL),
1511 gtk_container_get_children(GTK_CONTAINER(panel->before)));
1513 make_widgets(xmlNewChild(root, NULL, "end", NULL),
1514 g_list_reverse(gtk_container_get_children(
1515 GTK_CONTAINER(panel->after))));
1517 save_new = g_strconcat(save, ".new", NULL);
1518 if (save_xml_file(doc, save_new) || rename(save_new, save))
1519 delayed_error(_("Error saving panel %s: %s"),
1520 save, g_strerror(errno));
1521 g_free(save_new);
1523 g_free(save);
1524 if (doc)
1525 xmlFreeDoc(doc);
1528 /* Create a frame widget which can be used to add icons to the panel */
1529 static GtkWidget *make_insert_frame(Panel *panel)
1531 GtkWidget *frame;
1532 GtkTargetEntry target_table[] = {
1533 {"text/uri-list", 0, TARGET_URI_LIST},
1536 frame = gtk_frame_new(NULL);
1537 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
1538 gtk_widget_set_size_request(frame, 16, 16);
1540 g_signal_connect(frame, "drag-motion",
1541 G_CALLBACK(insert_drag_motion), panel);
1542 g_signal_connect(frame, "drag-leave",
1543 G_CALLBACK(panel_drag_leave), panel);
1545 g_signal_connect(frame, "drag-data-received",
1546 G_CALLBACK(add_uri_list), panel);
1547 gtk_drag_dest_set(frame,
1548 GTK_DEST_DEFAULT_ALL,
1549 target_table,
1550 sizeof(target_table) / sizeof(*target_table),
1551 GDK_ACTION_COPY);
1553 return frame;
1556 static gboolean enter_icon(GtkWidget *widget,
1557 GdkEventCrossing *event,
1558 Icon *icon)
1560 icon_may_update(icon);
1561 panel_icon_set_tip((PanelIcon *) icon);
1563 return FALSE;
1566 static gint panel_leave_event(GtkWidget *widget,
1567 GdkEventCrossing *event,
1568 Panel *panel)
1570 GdkWindow *pinboard;
1572 if (event->mode != GDK_CROSSING_NORMAL)
1573 return FALSE; /* Grab for menu, DnD, etc */
1575 keep_below(panel->window->window, TRUE);
1577 /* Shouldn't need this as well as keep_below but some WMs don't
1578 * automatically lower as soon as the hint is set */
1579 pinboard = pinboard_get_window();
1580 window_put_just_above(panel->window->window, pinboard);
1582 return FALSE;
1585 /* If (x, y) is at the edge of the panel then raise */
1586 static void motion_may_raise(Panel *panel, int x, int y)
1588 gboolean raise;
1590 if (panel->side == PANEL_TOP)
1591 raise = y == 0;
1592 else if (panel->side == PANEL_BOTTOM)
1593 raise = y == panel->window->allocation.height - 1;
1594 else if (panel->side == PANEL_LEFT)
1595 raise = x == 0;
1596 else
1597 raise = x == panel->window->allocation.width - 1;
1599 if (raise)
1601 keep_below(panel->window->window, FALSE);
1603 /* Shouldn't need this as well as keep_below but some WMs don't
1604 * automatically raise as soon as the hint is set */
1605 gdk_window_raise(panel->window->window);
1609 static gboolean may_autoscroll(Panel *panel)
1611 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1612 gint max, panel_x, panel_y, delta, new;
1614 if (panel->adj->upper <= panel->adj->page_size)
1615 goto stop_scrolling; /* Can see everything already */
1617 gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
1619 if (horz)
1621 delta = panel_x;
1622 max = panel->window->allocation.width;
1623 if (panel_y < 0 || panel_y > panel->window->allocation.height)
1624 goto stop_scrolling; /* Not over the panel */
1626 else
1628 delta = panel_y;
1629 max = panel->window->allocation.height;
1630 if (panel_x < 0 || panel_x > panel->window->allocation.width)
1631 goto stop_scrolling; /* Not over the panel */
1634 if (delta >= 20 && delta <= max - 20)
1635 goto stop_scrolling; /* Not at either end */
1637 panel->autoscroll_speed = MIN(panel->autoscroll_speed + 2, 200);
1639 new = panel->adj->value - ((delta < 20) ? panel->autoscroll_speed
1640 : -panel->autoscroll_speed);
1641 new = CLAMP(new, 0, panel->adj->upper - panel->adj->page_size);
1642 gtk_adjustment_set_value(panel->adj, new);
1644 panel->autoscroll_to = g_timeout_add(40,
1645 (GSourceFunc) may_autoscroll, panel);
1647 return FALSE;
1649 stop_scrolling:
1650 panel->autoscroll_speed = 0;
1651 return FALSE;
1654 static gint panel_motion_event(GtkWidget *widget,
1655 GdkEventMotion *event,
1656 Panel *panel)
1658 motion_may_raise(panel, event->x, event->y);
1660 if (motion_state != MOTION_REPOSITION)
1661 if (panel->autoscroll_speed == 0)
1662 may_autoscroll(panel);
1664 return FALSE;
1667 static gint icon_motion_event(GtkWidget *widget,
1668 GdkEventMotion *event,
1669 PanelIcon *pi)
1671 Panel *panel = pi->panel;
1672 GList *list, *me;
1673 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1674 int val;
1675 int dir = 0;
1677 if (motion_state == MOTION_READY_FOR_DND)
1679 if (dnd_motion_moved(event))
1680 start_drag(pi, event);
1681 return TRUE;
1683 else if (motion_state != MOTION_REPOSITION)
1684 return FALSE;
1686 list = gtk_container_get_children(GTK_CONTAINER(panel->before));
1687 list = g_list_append(list, NULL); /* The gap in the middle */
1688 list = g_list_concat(list,
1689 gtk_container_get_children(GTK_CONTAINER(panel->after)));
1690 me = g_list_find(list, widget);
1692 g_return_val_if_fail(me != NULL, TRUE);
1694 val = horz ? event->x_root : event->y_root;
1696 if (me->prev)
1698 GtkWidget *prev;
1699 int x, y;
1701 if (me->prev->data)
1702 prev = GTK_WIDGET(me->prev->data);
1703 else
1704 prev = panel->gap;
1706 gdk_window_get_origin(prev->window, &x, &y);
1708 if (val <= (horz ? x : y))
1709 dir = -1;
1712 if (dir == 0 && me->next)
1714 GtkWidget *next;
1715 int x, y, w, h;
1717 if (me->next->data)
1718 next = GTK_WIDGET(me->next->data);
1719 else
1720 next = panel->gap;
1722 gdk_window_get_origin(next->window, &x, &y);
1724 gdk_drawable_get_size(next->window, &w, &h);
1726 x += w;
1727 y += h;
1729 if (val >= (horz ? x : y)-1)
1731 if (next == panel->gap)
1732 dir = +2;
1733 else
1734 dir = +1;
1738 if (dir)
1739 reposition_icon(pi, g_list_index(list, widget) + dir);
1741 return TRUE;
1744 static void reposition_icon_on_side(GtkWidget *side, GtkWidget *widget,
1745 int index)
1747 GList *list;
1749 list = gtk_container_get_children(GTK_CONTAINER(side));
1751 /* Want to move icon to the list in the given 'side'. Is it there
1752 * already?
1755 if (!g_list_find(list, widget))
1757 /* No, reparent */
1758 gtk_grab_remove(widget);
1759 gtk_widget_reparent(widget, side);
1760 dnd_motion_grab_pointer();
1761 gtk_grab_add(widget);
1764 gtk_box_reorder_child(GTK_BOX(side), widget, index);
1766 g_list_free(list);
1769 /* Move icon to this index in the complete widget list.
1770 * 0 makes the icon the left-most icon. The gap in the middle has
1771 * an index number, which allows you to specify that the icon should
1772 * go on the left or right side.
1774 static void reposition_icon(PanelIcon *pi, int index)
1776 Panel *panel = pi->panel;
1777 GtkWidget *widget = pi->widget;
1778 GList *list;
1779 int before_len;
1781 list = gtk_container_get_children(GTK_CONTAINER(panel->before));
1782 before_len = g_list_length(list);
1783 g_list_free(list);
1785 if (index <= before_len)
1786 reposition_icon_on_side(panel->before, widget, index);
1787 else
1788 reposition_icon_on_side(panel->after, widget,
1789 index - (before_len + 1));
1791 panel_save(panel);
1794 static void start_drag(PanelIcon *pi, GdkEventMotion *event)
1796 GtkWidget *widget = pi->widget;
1797 Icon *icon = (Icon *) pi;
1799 if (!icon->selected)
1801 if (event->state & GDK_BUTTON1_MASK)
1803 /* Select just this one */
1804 icon_select_only(icon);
1805 tmp_icon_selected = TRUE;
1807 else
1808 icon_set_selected(icon, TRUE);
1811 g_return_if_fail(icon_selection != NULL);
1813 if (icon_selection->next == NULL)
1814 drag_one_item(widget, event, icon->path, icon->item, NULL);
1815 else
1817 guchar *uri_list;
1819 uri_list = icon_create_uri_list();
1820 drag_selection(widget, event, uri_list);
1821 g_free(uri_list);
1825 static void applet_died(GtkWidget *socket)
1827 gboolean never_plugged;
1829 never_plugged = (!g_object_get_data(G_OBJECT(socket), "lost_plug"))
1830 && !GTK_SOCKET(socket)->plug_window;
1832 if (never_plugged)
1834 report_error(
1835 _("Applet quit without ever creating a widget!"));
1836 gtk_widget_destroy(socket);
1839 gtk_widget_unref(socket);
1842 static void socket_destroyed(GtkWidget *socket, GtkWidget *widget)
1844 g_object_set_data(G_OBJECT(socket), "lost_plug", "yes");
1846 gtk_widget_unref(socket);
1848 gtk_widget_destroy(widget); /* Remove from panel */
1850 if (!closing_panel)
1851 panel_save(g_object_get_data(G_OBJECT(socket), "panel"));
1854 /* Try to run this applet.
1855 * Cases:
1857 * - No executable AppletRun:
1858 * icon->socket == NULL (unchanged) on return.
1860 * Otherwise, create socket (setting icon->socket) and ref it twice.
1862 * - AppletRun quits without connecting a plug:
1863 * On child death lost_plug is unset and socket is empty.
1864 * Unref socket.
1865 * Report error and destroy widget (to 'socket destroyed').
1867 * - AppletRun quits while plug is in socket:
1868 * Unref socket once. Socket will be destroyed later.
1870 * - Socket is destroyed.
1871 * Set lost_plug = "yes" and remove widget from panel.
1872 * Unref socket.
1874 static void run_applet(PanelIcon *pi)
1876 GError *error = NULL;
1877 char *argv[3];
1878 gint pid;
1879 Icon *icon = (Icon *) pi;
1881 argv[0] = (char *) make_path(icon->path, "AppletRun");
1883 if (access(argv[0], X_OK) != 0)
1884 return;
1886 pi->socket = gtk_socket_new();
1888 gtk_container_add(GTK_CONTAINER(pi->widget), pi->socket);
1889 gtk_widget_show_all(pi->socket);
1890 gtk_widget_realize(pi->socket);
1892 /* Always get button-2 events so we can drag */
1893 XGrabButton(gdk_display, Button2, AnyModifier,
1894 GDK_WINDOW_XWINDOW(pi->socket->window),
1895 False,
1896 ButtonPressMask | ButtonReleaseMask | Button2MotionMask,
1897 GrabModeAsync, /* Pointer */
1898 GrabModeAsync, /* Keyboard */
1899 None, None);
1902 gchar *pos;
1903 PanelSide side = pi->panel->side;
1905 /* Set a hint to let applets position their menus correctly */
1906 pos = g_strdup_printf("%s,%d",
1907 side == PANEL_TOP ? "Top" :
1908 side == PANEL_BOTTOM ? "Bottom" :
1909 side == PANEL_LEFT ? "Left" :
1910 "Right", MENU_MARGIN(side));
1911 gdk_property_change(pi->socket->window,
1912 gdk_atom_intern("_ROX_PANEL_MENU_POS", FALSE),
1913 gdk_atom_intern("STRING", FALSE),
1914 8, GDK_PROP_MODE_REPLACE,
1915 pos, strlen(pos));
1916 g_free(pos);
1918 /* Ensure that the properties are set before starting the
1919 * applet.
1921 gdk_flush();
1924 g_object_set_data(G_OBJECT(pi->widget), "icon", pi);
1925 g_object_set_data(G_OBJECT(pi->socket), "panel", pi->panel);
1927 argv[1] = g_strdup_printf("%ld",
1928 GDK_WINDOW_XWINDOW(pi->socket->window));
1929 argv[2] = NULL;
1931 if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
1932 NULL, NULL, &pid, &error))
1934 delayed_error(_("Error running applet:\n%s"), error->message);
1935 g_error_free(error);
1936 gtk_widget_destroy(pi->socket);
1937 pi->socket = NULL;
1939 else
1941 gtk_widget_ref(pi->socket);
1942 on_child_death(pid, (CallbackFn) applet_died, pi->socket);
1944 gtk_widget_ref(pi->socket);
1945 g_signal_connect(pi->socket, "destroy",
1946 G_CALLBACK(socket_destroyed), pi->widget);
1949 g_free(argv[1]);
1952 static void panel_post_resize(GtkWidget *win, GtkRequisition *req, Panel *panel)
1954 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
1956 req->width = panel_geometry.width;
1957 req->height += EDGE_WIDTH;
1959 else
1961 int h = panel_geometry.height;
1963 if (current_panel[PANEL_TOP])
1965 GtkWidget *win = current_panel[PANEL_TOP]->window;
1966 h -= win->allocation.height;
1969 if (current_panel[PANEL_BOTTOM])
1971 GtkWidget *win = current_panel[PANEL_BOTTOM]->window;
1972 h -= win->allocation.height;
1975 req->height = h;
1976 req->width += EDGE_WIDTH;
1980 static void update_side(GtkWidget *side)
1982 GList *kids, *next;
1984 kids = gtk_container_get_children(GTK_CONTAINER(side));
1985 for (next = kids; next; next = next->next)
1987 PanelIcon *pi;
1988 pi = g_object_get_data(next->data, "icon");
1989 panel_icon_set_tip(pi);
1991 g_list_free(kids);
1994 /* Tips or style has changed -- update everything on this panel */
1995 static void panel_set_style(Panel *panel)
1997 update_side(panel->before);
1998 update_side(panel->after);
1999 gtk_widget_queue_resize(panel->window);
2002 static gboolean recreate_panels(char **names)
2004 int i;
2006 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
2008 if (names[i])
2010 panel_new(names[i], i);
2011 g_free(names[i]);
2015 g_free(names);
2017 return FALSE;
2020 static void update_side_size(GtkWidget *side)
2022 GList *kids, *next;
2024 kids = gtk_container_get_children(GTK_CONTAINER(side));
2025 for (next = kids; next; next = next->next)
2027 PanelIcon *pi;
2028 pi = g_object_get_data(next->data, "icon");
2029 gtk_widget_queue_resize(pi->widget);
2031 g_list_free(kids);
2034 /* Update panel size and redraw */
2035 static void panel_update(Panel *panel)
2037 update_side_size(panel->before);
2038 update_side_size(panel->after);
2039 gtk_widget_queue_resize(panel->window);
2040 gtk_widget_queue_draw(panel->window);
2043 static void panel_style_changed(void)
2045 int i;
2047 if (o_override_redirect.has_changed)
2049 gchar **names;
2051 names = g_new(char *, PANEL_NUMBER_OF_SIDES);
2053 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
2055 Panel *panel = current_panel[i];
2056 names[i] = panel ? g_strdup(panel->name) : NULL;
2057 panel_new(NULL, i);
2060 g_idle_add((GtkFunction) recreate_panels, names);
2063 if (o_panel_style.has_changed)
2065 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
2067 if (current_panel[i])
2068 panel_set_style(current_panel[i]);
2071 if (o_panel_width.has_changed)
2073 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
2075 if (current_panel[i])
2076 panel_update(current_panel[i]);
2080 if (o_panel_xinerama.has_changed || o_panel_monitor.has_changed ||
2081 o_panel_avoid.has_changed)
2083 if (panel_check_xinerama() || o_panel_avoid.has_changed)
2085 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
2087 if (current_panel[i])
2089 reposition_panel(
2090 current_panel[i]->window,
2091 &current_panel[i]->
2092 window->allocation,
2093 current_panel[i]);
2094 gtk_widget_queue_resize(
2095 current_panel[i]->window);
2102 static gboolean draw_panel_edge(GtkWidget *widget, GdkEventExpose *event,
2103 Panel *panel)
2105 int x, y, width, height;
2107 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
2109 width = panel_geometry.width;
2110 height = EDGE_WIDTH;
2112 x = 0;
2113 if (panel->side == PANEL_BOTTOM)
2114 y = 0;
2115 else
2116 y = widget->allocation.height - EDGE_WIDTH;
2118 else
2120 width = EDGE_WIDTH;
2121 height = panel_geometry.height;
2123 y = 0;
2124 if (panel->side == PANEL_RIGHT)
2125 x = 0;
2126 else
2127 x = widget->allocation.width - EDGE_WIDTH;
2130 gdk_draw_rectangle(widget->window,
2131 widget->style->fg_gc[GTK_STATE_NORMAL], TRUE,
2132 x, y, width, height);
2134 return FALSE;
2137 static gpointer parent_class;
2139 static void panel_icon_destroy(Icon *icon)
2141 PanelIcon *pi = (PanelIcon *) icon;
2143 g_return_if_fail(pi != NULL);
2145 if (pi->image)
2146 g_object_unref(pi->image);
2148 g_return_if_fail(pi->widget != NULL);
2150 gtk_widget_destroy(pi->widget);
2153 static void panel_remove_items(void)
2155 Panel *panel;
2157 g_return_if_fail(icon_selection != NULL);
2159 panel = ((PanelIcon *) icon_selection->data)->panel;
2161 while (icon_selection)
2162 icon_destroy((Icon *) icon_selection->data);
2164 panel_save(panel);
2167 static void panel_icon_redraw(Icon *icon)
2169 gtk_widget_set_state(((PanelIcon *) icon)->widget,
2170 icon->selected ? GTK_STATE_SELECTED
2171 : GTK_STATE_NORMAL);
2172 gtk_widget_queue_draw(PANEL_ICON(icon)->widget);
2173 panel_icon_set_tip((PanelIcon *) icon);
2176 static void panel_icon_update(Icon *icon)
2178 PanelIcon *pi = (PanelIcon *) icon;
2180 gtk_widget_queue_draw(pi->widget);
2181 gtk_label_set_text(GTK_LABEL(pi->label), icon->item->leafname);
2182 panel_save(pi->panel);
2185 /* The point of this is to clear the selection if the existing icons
2186 * aren't from the same panel...
2188 static gboolean panel_icon_same_group(Icon *icon, Icon *other)
2190 if (IS_PANEL_ICON(other))
2192 PanelIcon *a = (PanelIcon *) icon;
2193 PanelIcon *b = (PanelIcon *) other;
2195 return a->panel == b->panel;
2197 else
2198 return FALSE;
2201 static void panel_icon_class_init(gpointer gclass, gpointer data)
2203 IconClass *icon = (IconClass *) gclass;
2205 parent_class = g_type_class_peek_parent(gclass);
2207 icon->destroy = panel_icon_destroy;
2208 icon->redraw = panel_icon_redraw;
2209 icon->update = panel_icon_update;
2210 icon->remove_items = panel_remove_items;
2211 icon->same_group = panel_icon_same_group;
2212 icon->wink = panel_icon_wink;
2215 static void panel_icon_init(GTypeInstance *object, gpointer gclass)
2217 PanelIcon *pi = (PanelIcon *) object;
2219 pi->widget = NULL;
2220 pi->image = NULL;
2221 pi->label = NULL;
2222 pi->socket = NULL;
2223 pi->style = TEXT_UNDER_ICON;
2226 static GType panel_icon_get_type(void)
2228 static GType type = 0;
2230 if (!type)
2232 static const GTypeInfo info =
2234 sizeof (PanelIconClass),
2235 NULL, /* base_init */
2236 NULL, /* base_finalise */
2237 panel_icon_class_init,
2238 NULL, /* class_finalise */
2239 NULL, /* class_data */
2240 sizeof(PanelIcon),
2241 0, /* n_preallocs */
2242 panel_icon_init
2245 type = g_type_register_static(icon_get_type(),
2246 "PanelIcon", &info, 0);
2249 return type;
2252 static PanelIcon *panel_icon_new(Panel *panel,
2253 const char *pathname,
2254 const char *name)
2256 PanelIcon *pi;
2257 Icon *icon;
2259 pi = g_object_new(panel_icon_get_type(), NULL);
2260 icon = (Icon *) pi;
2262 icon_set_path(icon, pathname, name);
2263 pi->panel = panel;
2265 return pi;
2268 static gboolean panel_want_show_text(PanelIcon *pi)
2270 Icon *icon = (Icon *) pi;
2272 if (!icon->item->leafname[0])
2273 return FALSE;
2275 if (o_panel_style.int_value == SHOW_BOTH)
2276 return TRUE;
2277 if (o_panel_style.int_value == SHOW_ICON)
2278 return FALSE;
2280 if (icon->item->flags & ITEM_FLAG_APPDIR)
2281 return FALSE;
2283 return TRUE;
2286 static void panel_position_menu(GtkMenu *menu, gint *x, gint *y,
2287 gboolean *push_in, gpointer data)
2289 int *pos = (int *) data;
2290 GtkRequisition requisition;
2291 int margin = pos[2];
2292 int mon = pos[3];
2293 int mon_right = monitor_geom[mon].x +
2294 monitor_geom[mon].width;
2295 int mon_bottom = monitor_geom[mon].y +
2296 monitor_geom[mon].height;
2298 gtk_widget_size_request(GTK_WIDGET(menu), &requisition);
2300 if (pos[0] == -1)
2301 *x = mon_right - margin - requisition.width;
2302 else if (pos[0] == -2)
2303 *x = monitor_geom[mon].x + margin;
2304 else
2305 *x = pos[0] - (requisition.width >> 2);
2307 if (pos[1] == -1)
2308 *y = mon_bottom - margin - requisition.height;
2309 else if (pos[1] == -2)
2310 *y = monitor_geom[mon].y + margin;
2311 else
2312 *y = pos[1] - (requisition.height >> 2);
2314 *x = CLAMP(*x, 0, mon_right - requisition.width);
2315 *y = CLAMP(*y, 0, mon_bottom - requisition.height);
2317 *push_in = FALSE;
2320 static void panel_show_menu(GdkEventButton *event, PanelIcon *pi, Panel *panel)
2322 GtkWidget *option_item;
2323 PanelSide side = panel->side;
2324 int pos[4];
2326 pos[0] = event->x_root;
2327 pos[1] = event->y_root;
2328 pos[2] = MENU_MARGIN(side);
2329 /* FIXME: Should we read screen from event's window rather than
2330 * using default? */
2331 pos[3] = gdk_screen_get_monitor_at_point(
2332 gdk_screen_get_default(),
2333 event->x_root, event->y_root);
2335 option_item = gtk_image_menu_item_new_with_label(_("Panel Options..."));
2336 g_signal_connect_swapped(option_item, "activate",
2337 G_CALLBACK(panel_show_options),
2338 GINT_TO_POINTER(panel->side));
2340 icon_prepare_menu((Icon *) pi, option_item);
2342 if (side == PANEL_LEFT)
2343 pos[0] = -2;
2344 else if (side == PANEL_RIGHT)
2345 pos[0] = -1;
2347 if (side == PANEL_TOP)
2348 pos[1] = -2;
2349 else if (side == PANEL_BOTTOM)
2350 pos[1] = -1;
2352 gtk_menu_popup(GTK_MENU(icon_menu), NULL, NULL,
2353 panel_position_menu,
2354 (gpointer) pos, event->button, event->time);
2357 /* Note: also called from icon handler */
2358 static gboolean panel_drag_motion(GtkWidget *widget,
2359 GdkDragContext *context,
2360 gint x,
2361 gint y,
2362 guint time,
2363 Panel *panel)
2365 int panel_x, panel_y;
2367 gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
2369 motion_may_raise(panel, panel_x, panel_y);
2370 gdk_drag_status(context, 0, time);
2372 return TRUE;
2375 static gboolean insert_drag_motion(GtkWidget *widget,
2376 GdkDragContext *context,
2377 gint x,
2378 gint y,
2379 guint time,
2380 Panel *panel)
2382 int panel_x, panel_y;
2384 gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
2385 motion_may_raise(panel, panel_x, panel_y);
2387 return FALSE;
2390 /* Note: also called from icon handler */
2391 static void panel_drag_leave(GtkWidget *widget,
2392 GdkDragContext *context,
2393 guint32 time,
2394 Panel *panel)
2396 GdkWindow *pinboard, *window;
2397 GtkAllocation *alloc = &panel->window->allocation;
2398 int x, y;
2400 window = panel->window->window;
2401 gdk_window_get_pointer(window, &x, &y, NULL);
2402 if (x < 0 || y < 0 || x > alloc->width || y > alloc->height)
2404 keep_below(panel->window->window, TRUE);
2406 /* Shouldn't need this as well as keep_below but some WMs don't
2407 * automatically lower as soon as the hint is set */
2408 pinboard = pinboard_get_window();
2409 window_put_just_above(panel->window->window, pinboard);
2413 static gboolean panel_check_xinerama(void)
2415 gint old_monitor = panel_monitor;
2417 panel_monitor = -1;
2419 if (o_panel_xinerama.int_value)
2421 if (o_panel_monitor.int_value < n_monitors)
2423 panel_monitor = o_panel_monitor.int_value;
2425 else
2427 g_warning(_("Xinerama monitor %d unavailable"),
2428 o_panel_monitor.int_value);
2432 if (panel_monitor == -1)
2434 panel_geometry.x = panel_geometry.y = 0;
2435 panel_geometry.width = screen_width;
2436 panel_geometry.height = screen_height;
2438 else
2440 panel_geometry = monitor_geom[panel_monitor];
2443 return old_monitor != panel_monitor;
2446 static GList *build_monitor_number(Option *option, xmlNode *node, guchar *label)
2448 GtkObject *adj;
2450 adj = gtk_adjustment_new(MAX(0, panel_monitor),
2451 0, n_monitors - 1, 1, 10, 1);
2452 return build_numentry_base(option, node, label, GTK_ADJUSTMENT(adj));
2455 static void side_changed(Radios *radios, gpointer data)
2457 Panel *panel;
2458 PanelSide side, new_side;
2459 GObject *dialog = G_OBJECT(data);
2460 char *name, *other_side_name;
2462 side = GPOINTER_TO_INT(g_object_get_data(dialog, "rox-panel-side"));
2463 g_return_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES);
2464 panel = current_panel[side];
2465 g_return_if_fail(panel != NULL);
2467 new_side = radios_get_value(radios);
2469 if (new_side == side)
2470 return;
2472 name = g_strdup(panel->name);
2473 other_side_name = current_panel[new_side]
2474 ? g_strdup(current_panel[new_side]->name)
2475 : NULL;
2477 panel_new(name, new_side);
2478 g_object_set_data(G_OBJECT(dialog), "rox-panel-side",
2479 GINT_TO_POINTER(new_side));
2480 panel_new(other_side_name, side);
2482 g_free(name);
2483 g_free(other_side_name);
2486 static void panel_show_options(gpointer data)
2488 GtkWidget *dialog;
2489 GtkWidget *vbox, *label;
2490 Radios *radios;
2491 Panel *panel;
2492 PanelSide side = GPOINTER_TO_INT(data);
2493 char *tmp;
2495 g_return_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES);
2496 panel = current_panel[side];
2497 g_return_if_fail(panel != NULL);
2499 if (panel_options_dialog)
2500 gtk_widget_destroy(panel_options_dialog);
2502 dialog = gtk_dialog_new_with_buttons(_("Panel Options"), NULL,
2503 GTK_DIALOG_NO_SEPARATOR,
2504 GTK_STOCK_CLOSE, GTK_RESPONSE_OK,
2505 NULL);
2506 panel_options_dialog = dialog;
2508 g_object_set_data(G_OBJECT(dialog), "rox-panel-side",
2509 GINT_TO_POINTER(panel->side));
2511 vbox = gtk_vbox_new(FALSE, 0);
2512 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
2513 vbox, TRUE, TRUE, 0);
2515 gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
2517 tmp = g_strdup_printf(_("Panel: %s"), panel->name);
2518 label = gtk_label_new(tmp);
2519 make_heading(label, PANGO_SCALE_LARGE);
2520 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
2521 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
2522 g_free(tmp);
2524 gtk_box_pack_start(GTK_BOX(vbox),
2525 gtk_label_new(_("Select the panel's position:")),
2526 FALSE, TRUE, 4);
2528 /* Radio buttons to set the side */
2529 radios = radios_new(side_changed, dialog);
2531 radios_add(radios, _("Top-edge panel"),
2532 PANEL_TOP, _("Top edge"));
2533 radios_add(radios, _("Bottom edge panel"),
2534 PANEL_BOTTOM, _("Bottom edge"));
2535 radios_add(radios, _("Left edge panel"),
2536 PANEL_LEFT, _("Left edge"));
2537 radios_add(radios, _("Right-edge panel"),
2538 PANEL_RIGHT, _("Right edge"));
2540 radios_set_value(radios, panel->side);
2542 radios_pack(radios, GTK_BOX(vbox));
2544 g_signal_connect(dialog, "destroy",
2545 G_CALLBACK(gtk_widget_destroyed),
2546 &panel_options_dialog);
2548 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
2550 g_signal_connect(dialog, "response",
2551 G_CALLBACK(gtk_widget_destroy), NULL);
2552 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
2554 gtk_widget_show_all(dialog);
2557 /* Returns PANEL_NUMBER_OF_SIDES if name is invalid */
2558 PanelSide panel_name_to_side(gchar *side)
2560 if (strcmp(side, "Top") == 0)
2561 return PANEL_TOP;
2562 else if (strcmp(side, "Bottom") == 0)
2563 return PANEL_BOTTOM;
2564 else if (strcmp(side, "Left") == 0)
2565 return PANEL_LEFT;
2566 else if (strcmp(side, "Right") == 0)
2567 return PANEL_RIGHT;
2568 else
2569 g_warning("Unknown panel side '%s'", side);
2570 return PANEL_NUMBER_OF_SIDES;