r4523: Updated headers:
[rox-filer/dt.git] / ROX-Filer / src / panel.c
blobf6166d74d65adc9b20f0365f4ebd6158419f1796
1 /*
2 * ROX-Filer, filer for the ROX desktop project
3 * Copyright (C) 2006, Thomas Leonard and others (see changelog for details).
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
17 * Place, Suite 330, Boston, MA 02111-1307 USA
20 /* panel.c - code for dealing with panel windows */
22 #include "config.h"
24 #include <stdio.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <ctype.h>
28 #include <unistd.h>
29 #include <libxml/parser.h>
31 #include <gtk/gtk.h>
32 #include <gdk/gdkx.h>
34 #include "global.h"
36 #include "panel.h"
37 #include "options.h"
38 #include "choices.h"
39 #include "main.h"
40 #include "type.h"
41 #include "gui_support.h"
42 #include "diritem.h"
43 #include "pixmaps.h"
44 #include "filer.h"
45 #include "display.h"
46 #include "bind.h"
47 #include "dnd.h"
48 #include "support.h"
49 #include "icon.h"
50 #include "run.h"
51 #include "appinfo.h"
52 #include "pixmaps.h"
53 #include "xml.h"
54 #include "pinboard.h" /* For pinboard_get_window() */
56 /* The width of the separator at the inner edge of the panel */
57 #define EDGE_WIDTH 2
59 /* The gap between panel icons */
60 #define PANEL_ICON_SPACING 8
62 enum {TEXT_BESIDE_ICON, TEXT_UNDER_ICON};
64 static gboolean tmp_icon_selected = FALSE; /* When dragging */
66 typedef struct _PanelIconClass PanelIconClass;
67 typedef struct _PanelIcon PanelIcon;
69 struct _PanelIconClass {
70 IconClass parent;
73 struct _PanelIcon {
74 Icon icon;
75 GdkPixbuf *image;
77 Panel *panel;
78 GtkWidget *widget; /* The drawing area for the icon */
79 GtkWidget *label;
80 GtkWidget *socket; /* For applets */
82 int style;
85 #define PANEL_ICON(obj) GTK_CHECK_CAST((obj), panel_icon_get_type(), PanelIcon)
86 #define IS_PANEL_ICON(obj) \
87 G_TYPE_CHECK_INSTANCE_TYPE((obj), panel_icon_get_type())
89 Panel *current_panel[PANEL_NUMBER_OF_SIDES];
91 /* NULL => Not loading a panel */
92 static Panel *loading_panel = NULL;
94 static GtkWidget *panel_options_dialog = NULL;
96 /* Static prototypes */
97 static int panel_delete(GtkWidget *widget, GdkEvent *event, Panel *panel);
98 static void panel_destroyed(GtkWidget *widget, Panel *panel);
99 static const char *pan_from_file(gchar *line);
100 static gint icon_button_release(GtkWidget *widget,
101 GdkEventButton *event,
102 PanelIcon *pi);
103 static gint icon_button_press(GtkWidget *widget,
104 GdkEventButton *event,
105 PanelIcon *pi);
106 static void reposition_panel(GtkWidget *window,
107 GtkAllocation *alloc, Panel *panel);
108 static gint expose_icon(GtkWidget *widget,
109 GdkEventExpose *event,
110 PanelIcon *pi);
111 static gint draw_icon(GtkWidget *widget,
112 GdkRectangle *badarea,
113 PanelIcon *pi);
114 static gint panel_button_release(GtkWidget *widget,
115 GdkEventButton *event,
116 Panel *panel);
117 static gint panel_button_press(GtkWidget *widget,
118 GdkEventButton *event,
119 Panel *panel);
120 static void panel_post_resize(GtkWidget *box,
121 GtkRequisition *req, Panel *panel);
122 static void drag_set_panel_dest(PanelIcon *pi);
123 static void add_uri_list(GtkWidget *widget,
124 GdkDragContext *context,
125 gint x,
126 gint y,
127 GtkSelectionData *selection_data,
128 guint info,
129 guint32 time,
130 Panel *panel);
131 static void panel_add_item(Panel *panel,
132 const gchar *path,
133 const gchar *name,
134 gboolean after,
135 const gchar *shortcut,
136 const gchar *args);
137 static gboolean panel_drag_motion(GtkWidget *widget,
138 GdkDragContext *context,
139 gint x,
140 gint y,
141 guint time,
142 Panel *panel);
143 static gboolean insert_drag_motion(GtkWidget *widget,
144 GdkDragContext *context,
145 gint x,
146 gint y,
147 guint time,
148 Panel *panel);
149 static gboolean drag_motion(GtkWidget *widget,
150 GdkDragContext *context,
151 gint x,
152 gint y,
153 guint time,
154 PanelIcon *pi);
155 static void panel_drag_leave(GtkWidget *widget,
156 GdkDragContext *context,
157 guint32 time,
158 Panel *panel);
159 static void drag_leave(GtkWidget *widget,
160 GdkDragContext *context,
161 guint32 time,
162 Icon *icon);
163 static GtkWidget *make_insert_frame(Panel *panel);
164 static gboolean enter_icon(GtkWidget *widget,
165 GdkEventCrossing *event,
166 Icon *icon);
167 static gint icon_motion_event(GtkWidget *widget,
168 GdkEventMotion *event,
169 PanelIcon *pi);
170 static gint panel_leave_event(GtkWidget *widget,
171 GdkEventCrossing *event,
172 Panel *panel);
173 static gint panel_motion_event(GtkWidget *widget,
174 GdkEventMotion *event,
175 Panel *panel);
176 static void reposition_icon(PanelIcon *pi, int index);
177 static void start_drag(PanelIcon *pi, GdkEventMotion *event);
178 static void drag_end(GtkWidget *widget,
179 GdkDragContext *context,
180 Icon *icon);
181 static void perform_action(Panel *panel,
182 PanelIcon *pi,
183 GdkEventButton *event);
184 static void run_applet(PanelIcon *pi);
185 static void size_request(GtkWidget *widget, GtkRequisition *req, PanelIcon *pi);
186 static void panel_load_from_xml(Panel *panel, xmlDocPtr doc);
187 static gboolean draw_panel_edge(GtkWidget *widget, GdkEventExpose *event,
188 Panel *panel);
189 static PanelIcon *panel_icon_new(Panel *panel,
190 const char *pathname,
191 const char *name);
192 static GType panel_icon_get_type(void);
193 static gboolean panel_want_show_text(PanelIcon *pi);
194 static void panel_show_menu(GdkEventButton *event, PanelIcon *pi, Panel *panel);
195 static void panel_style_changed(void);
196 static void motion_may_raise(Panel *panel, int x, int y);
197 static void panel_update(Panel *panel);
198 static gboolean panel_check_xinerama(void);
199 static GList *build_monitor_number(Option *option,
200 xmlNode *node, guchar *label);
201 static gboolean may_autoscroll(Panel *panel);
202 static void panel_show_options(gpointer data);
205 static GtkWidget *dnd_highlight = NULL; /* (stops flickering) */
207 #define SHOW_BOTH 0
208 #define SHOW_APPS_SMALL 1
209 #define SHOW_ICON 2
210 static Option o_panel_style;
211 static Option o_panel_width;
212 static Option o_panel_xinerama;
213 static Option o_panel_monitor;
214 static Option o_panel_avoid;
215 static Option o_panel_is_dock;
217 static gint panel_monitor = -1;
218 GdkRectangle panel_geometry;
220 static int closing_panel = 0; /* Don't panel_save; destroying! */
222 /****************************************************************
223 * EXTERNAL INTERFACE *
224 ****************************************************************/
226 void panel_init(void)
228 option_add_int(&o_panel_style, "panel_style", SHOW_APPS_SMALL);
229 option_add_int(&o_panel_width, "panel_width", 52);
231 option_add_int(&o_panel_xinerama, "panel_xinerama", 0);
232 option_add_int(&o_panel_monitor, "panel_monitor", 0);
234 option_add_int(&o_panel_avoid, "panel_avoid", TRUE);
235 option_add_int(&o_panel_is_dock, "panel_is_dock", FALSE);
237 option_add_notify(panel_style_changed);
239 option_register_widget("monitor-number", build_monitor_number);
241 panel_check_xinerama();
244 /* Return a free edge for a new panel.
245 * If no edge is free, returns PANEL_BOTTOM.
247 static PanelSide find_free_side()
249 if (!current_panel[PANEL_BOTTOM])
250 return PANEL_BOTTOM;
252 if (!current_panel[PANEL_TOP])
253 return PANEL_TOP;
255 if (!current_panel[PANEL_LEFT])
256 return PANEL_LEFT;
258 if (!current_panel[PANEL_RIGHT])
259 return PANEL_RIGHT;
261 return PANEL_BOTTOM;
264 /* 'name' may be NULL or "" to remove the panel */
265 Panel *panel_new(const gchar *name, PanelSide side)
267 guchar *load_path;
268 Panel *panel;
269 GtkWidget *vp, *box, *frame, *align;
270 xmlDocPtr panel_doc = NULL;
271 gboolean need_resave = FALSE;
273 g_return_val_if_fail(side == PANEL_DEFAULT_SIDE ||
274 (side >= 0 && side < PANEL_NUMBER_OF_SIDES), NULL);
275 g_return_val_if_fail(loading_panel == NULL, NULL);
277 if (name && *name == '\0')
278 name = NULL;
280 if (!name)
281 load_path = NULL;
282 else if (strchr(name, '/'))
283 load_path = g_strdup(name);
284 else
286 guchar *leaf;
288 leaf = g_strconcat("pan_", name, NULL);
289 load_path = choices_find_xdg_path_load(leaf, PROJECT, SITE);
290 g_free(leaf);
293 if (load_path && access(load_path, F_OK) == 0)
295 char *saved_side;
296 xmlNodePtr root;
298 panel_doc = xmlParseFile(load_path);
299 root = xmlDocGetRootElement(panel_doc);
301 saved_side = xmlGetProp(root, "side");
302 if (saved_side)
304 PanelSide old_side;
305 old_side = panel_name_to_side(saved_side);
306 g_free(saved_side);
308 if (side == PANEL_DEFAULT_SIDE)
309 side = old_side;
310 else if (side != old_side)
311 need_resave = TRUE;
313 else
314 need_resave = TRUE;
317 if (side == PANEL_DEFAULT_SIDE)
318 side = find_free_side();
320 if (current_panel[side])
322 if (name)
323 number_of_windows++;
324 closing_panel++;
325 gtk_widget_destroy(current_panel[side]->window);
326 closing_panel--;
327 if (name)
328 number_of_windows--;
331 if (name == NULL || *name == '\0')
332 return NULL;
334 panel = g_new(Panel, 1);
335 panel->name = g_strdup(name);
336 panel->side = side;
337 panel->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
338 panel->autoscroll_speed = 0;
339 gtk_window_set_resizable(GTK_WINDOW(panel->window), FALSE);
340 gtk_window_set_wmclass(GTK_WINDOW(panel->window), "ROX-Panel", PROJECT);
341 gtk_widget_set_name(panel->window, "rox-panel");
342 gtk_widget_set_events(panel->window,
343 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
344 GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
346 /* We make the panel a drop target only so that we can auto-raise! */
347 gtk_drag_dest_set(panel->window, 0, NULL, 0, GDK_ACTION_PRIVATE);
348 g_signal_connect(panel->window, "drag_leave",
349 G_CALLBACK(panel_drag_leave), panel);
350 g_signal_connect(panel->window, "drag_motion",
351 G_CALLBACK(panel_drag_motion), panel);
353 g_signal_connect(panel->window, "delete-event",
354 G_CALLBACK(panel_delete), panel);
355 g_signal_connect(panel->window, "destroy",
356 G_CALLBACK(panel_destroyed), panel);
357 g_signal_connect(panel->window, "button_press_event",
358 G_CALLBACK(panel_button_press), panel);
359 g_signal_connect(panel->window, "button_release_event",
360 G_CALLBACK(panel_button_release), panel);
361 g_signal_connect(panel->window, "motion-notify-event",
362 G_CALLBACK(panel_motion_event), panel);
363 g_signal_connect(panel->window, "leave-notify-event",
364 G_CALLBACK(panel_leave_event), panel);
366 if (panel->side == PANEL_RIGHT)
367 align = gtk_alignment_new(1.0, 0.0, 0.0, 1.0);
368 else if (panel->side == PANEL_BOTTOM)
369 align = gtk_alignment_new(0.0, 1.0, 1.0, 0.0);
370 else if (panel->side == PANEL_TOP)
371 align = gtk_alignment_new(0.0, 0.0, 1.0, 0.0);
372 else
373 align = gtk_alignment_new(0.0, 0.0, 0.0, 1.0);
375 gtk_container_add(GTK_CONTAINER(panel->window), align);
377 vp = gtk_viewport_new(NULL, NULL);
378 gtk_container_set_resize_mode(GTK_CONTAINER(vp), GTK_RESIZE_PARENT);
379 gtk_viewport_set_shadow_type(GTK_VIEWPORT(vp), GTK_SHADOW_NONE);
380 gtk_container_add(GTK_CONTAINER(align), vp);
382 g_signal_connect(align, "expose-event",
383 G_CALLBACK(draw_panel_edge), panel);
385 if (side == PANEL_TOP || side == PANEL_BOTTOM)
387 panel->adj = gtk_viewport_get_hadjustment(GTK_VIEWPORT(vp));
388 box = gtk_hbox_new(FALSE, 0);
389 panel->before = gtk_hbox_new(FALSE, 0);
390 panel->after = gtk_hbox_new(FALSE, 0);
392 else
394 panel->adj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(vp));
395 box = gtk_vbox_new(FALSE, 0);
396 panel->before = gtk_vbox_new(FALSE, 0);
397 panel->after = gtk_vbox_new(FALSE, 0);
400 gtk_container_add(GTK_CONTAINER(vp), box);
401 gtk_box_pack_start(GTK_BOX(box), panel->before, FALSE, TRUE, 0);
402 gtk_box_pack_end(GTK_BOX(box), panel->after, FALSE, TRUE, 0);
404 frame = make_insert_frame(panel);
405 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
407 /* This is used so that we can find the middle easily! */
408 panel->gap = gtk_event_box_new();
409 gtk_box_pack_start(GTK_BOX(box), panel->gap, FALSE, FALSE, 0);
411 frame = make_insert_frame(panel);
412 g_object_set_data(G_OBJECT(frame), "after", "yes");
413 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
415 if (o_panel_is_dock.int_value)
416 gtk_window_set_type_hint(GTK_WINDOW(panel->window),
417 GDK_WINDOW_TYPE_HINT_DOCK);
419 gtk_widget_realize(panel->window);
420 make_panel_window(panel->window);
421 gtk_window_stick(GTK_WINDOW(panel->window));
423 gtk_widget_show_all(align);
425 loading_panel = panel;
426 if (panel_doc)
428 panel_load_from_xml(panel, panel_doc);
429 xmlFreeDoc(panel_doc);
431 if (need_resave)
432 panel_save(panel);
434 else if (load_path)
436 parse_file(load_path, pan_from_file);
437 info_message(_("Your old panel file has been "
438 "converted to the new XML format."));
439 panel_save(panel);
441 else
443 /* Don't scare users with an empty panel... */
444 guchar *apps;
446 panel_add_item(panel, "~", "Home", FALSE, NULL, NULL);
448 apps = pathdup(make_path(app_dir, ".."));
449 if (apps)
451 panel_add_item(panel, apps, "Apps", FALSE, NULL, NULL);
452 g_free(apps);
455 loading_panel = NULL;
456 g_free(load_path);
458 current_panel[side] = panel;
460 gtk_widget_queue_resize(box);
461 g_signal_connect(panel->window, "size-request",
462 G_CALLBACK(panel_post_resize), panel);
463 g_signal_connect(panel->window, "size-allocate",
464 G_CALLBACK(reposition_panel), panel);
466 number_of_windows++;
467 gdk_window_lower(panel->window->window);
468 gtk_widget_show(panel->window);
469 /* This has no effect until after window is showing; GTK+ bug? */
470 keep_below(panel->window->window, TRUE);
473 GdkWindow *pinboard;
475 pinboard = pinboard_get_window();
476 /* (if pinboard is NULL, will go right to the back) */
477 window_put_just_above(panel->window->window, pinboard);
480 return panel;
483 /* Externally visible function to add an item to a panel */
484 gboolean panel_add(PanelSide side,
485 const gchar *path, const gchar *label, gboolean after, const gchar *shortcut, const gchar *args)
487 g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, FALSE);
489 g_return_val_if_fail(current_panel[side] != NULL, FALSE);
491 panel_add_item(current_panel[side], path, label, after, shortcut, args);
493 return TRUE;
496 /* Add the area covered by the panels to the region */
497 void panel_mark_used(GdkRegion *used)
499 int i;
501 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
503 Panel *panel = current_panel[i];
504 GdkRectangle rect;
506 if (!panel)
507 continue;
509 gdk_window_get_root_origin(panel->window->window,
510 &rect.x, &rect.y);
511 rect.width = panel->window->allocation.width;
512 rect.height = panel->window->allocation.height;
514 gdk_region_union_with_rect(used, &rect);
518 /* On xrandr screen size changes, update all panels */
519 void panel_update_size(void)
521 int i;
523 panel_check_xinerama();
525 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
527 if (current_panel[i])
529 reposition_panel(current_panel[i]->window,
530 &current_panel[i]->window->allocation,
531 current_panel[i]);
532 gtk_widget_queue_resize(current_panel[i]->window);
537 /****************************************************************
538 * INTERNAL FUNCTIONS *
539 ****************************************************************/
541 /* User has tried to close the panel via the window manager - confirm */
542 static int panel_delete(GtkWidget *widget, GdkEvent *event, Panel *panel)
544 return !confirm(_("You have tried to close a panel via the window "
545 "manager - I usually find that this is accidental... "
546 "really close?"),
547 GTK_STOCK_CLOSE, NULL);
550 static void panel_destroyed(GtkWidget *widget, Panel *panel)
552 if (panel_options_dialog)
554 PanelSide side;
555 side = GPOINTER_TO_INT(g_object_get_data(
556 G_OBJECT(panel_options_dialog),
557 "rox-panel-side"));
558 if (side == panel->side)
559 gtk_widget_destroy(panel_options_dialog);
562 if (current_panel[panel->side] == panel)
563 current_panel[panel->side] = NULL;
565 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
567 if (current_panel[PANEL_RIGHT])
568 gtk_widget_queue_resize(
569 current_panel[PANEL_RIGHT]->window);
570 if (current_panel[PANEL_LEFT])
571 gtk_widget_queue_resize(
572 current_panel[PANEL_LEFT]->window);
575 if (panel->autoscroll_speed)
576 g_source_remove(panel->autoscroll_to);
578 g_free(panel->name);
579 g_free(panel);
581 one_less_window();
584 static void panel_load_side(Panel *panel, xmlNodePtr side, gboolean after)
586 xmlNodePtr node;
587 char *label, *path, *shortcut, *args;
589 for (node = side->xmlChildrenNode; node; node = node->next)
591 if (node->type != XML_ELEMENT_NODE)
592 continue;
593 if (strcmp(node->name, "icon") != 0)
594 continue;
596 label = xmlGetProp(node, "label");
597 if (!label)
598 label = g_strdup("<missing label>");
599 path = xmlNodeGetContent(node);
600 if (!path)
601 path = g_strdup("<missing path>");
602 shortcut = xmlGetProp(node, "shortcut");
603 args = xmlGetProp(node, "args");
605 panel_add_item(panel, path, label, after, shortcut, args);
607 g_free(path);
608 g_free(label);
609 g_free(shortcut);
610 g_free(args);
614 /* Create one panel icon for each icon in the doc */
615 static void panel_load_from_xml(Panel *panel, xmlDocPtr doc)
617 xmlNodePtr root;
619 root = xmlDocGetRootElement(doc);
620 panel_load_side(panel, get_subnode(root, NULL, "start"), FALSE);
621 panel_load_side(panel, get_subnode(root, NULL, "end"), TRUE);
624 /* Called for each line in the config file while loading a new panel */
625 static const char *pan_from_file(gchar *line)
627 gchar *sep, *leaf;
629 g_return_val_if_fail(line != NULL, NULL);
630 g_return_val_if_fail(loading_panel != NULL, NULL);
632 if (*line == '\0')
633 return NULL;
635 sep = strpbrk(line, "<>");
636 if (!sep)
637 return _("Missing < or > in panel config file");
639 if (sep != line)
640 leaf = g_strndup(line, sep - line);
641 else
642 leaf = NULL;
644 panel_add_item(loading_panel, sep + 1, leaf, sep[0] == '>',
645 NULL, NULL);
647 g_free(leaf);
649 return NULL;
652 static gboolean icon_pointer_in(GtkWidget *widget,
653 GdkEventCrossing *event,
654 Icon *icon)
656 gtk_widget_set_state(widget,
657 icon->selected ? GTK_STATE_SELECTED : GTK_STATE_PRELIGHT);
659 return 0;
662 static gboolean icon_pointer_out(GtkWidget *widget,
663 GdkEventCrossing *event,
664 Icon *icon)
666 gtk_widget_set_state(widget,
667 icon->selected ? GTK_STATE_SELECTED : GTK_STATE_NORMAL);
669 return 0;
672 static void panel_icon_destroyed(PanelIcon *pi)
674 g_return_if_fail(pi->widget != NULL);
676 pi->widget = NULL;
678 g_object_unref(pi);
681 /* Set the tooltip AND hide/show the label */
682 static void panel_icon_set_tip(PanelIcon *pi)
684 XMLwrapper *ai;
685 xmlNode *node;
686 Icon *icon = (Icon *) pi;
688 g_return_if_fail(pi != NULL);
690 if (pi->label)
692 if (panel_want_show_text(pi))
693 gtk_widget_show(pi->label);
694 else
695 gtk_widget_hide(pi->label);
698 if (pi->socket)
699 ai = NULL;
700 else
701 ai = appinfo_get(icon->path, icon->item);
703 if (ai && ((node = xml_get_section(ai, NULL, "Summary"))))
705 guchar *str;
706 str = xmlNodeListGetString(node->doc,
707 node->xmlChildrenNode, 1);
708 if (str)
710 gtk_tooltips_set_tip(tooltips, pi->widget, str, NULL);
711 g_free(str);
714 else if ((!panel_want_show_text(pi)) && !pi->socket)
716 if (icon->item->leafname && icon->item->leafname[0])
717 gtk_tooltips_set_tip(tooltips, pi->widget,
718 icon->item->leafname, NULL);
720 else
721 gtk_tooltips_set_tip(tooltips, pi->widget, NULL, NULL);
723 if (ai)
724 g_object_unref(ai);
727 /* Add an icon with this path to the panel. If after is TRUE then the
728 * icon is added to the right/bottom end of the panel.
730 * If name is NULL a suitable name is taken from path.
732 static void panel_add_item(Panel *panel,
733 const gchar *path,
734 const gchar *name,
735 gboolean after,
736 const gchar *shortcut,
737 const gchar *args)
739 GtkWidget *widget;
740 PanelIcon *pi;
741 Icon *icon;
743 g_return_if_fail(panel != NULL);
744 g_return_if_fail(path != NULL);
746 widget = gtk_event_box_new();
747 gtk_widget_set_events(widget,
748 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
749 GDK_BUTTON3_MOTION_MASK |
750 GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
751 GDK_BUTTON_RELEASE_MASK);
753 gtk_box_pack_start(GTK_BOX(after ? panel->after : panel->before),
754 widget, FALSE, TRUE, 0);
755 if (after)
756 gtk_box_reorder_child(GTK_BOX(panel->after), widget, 0);
758 gtk_widget_realize(widget);
760 pi = panel_icon_new(panel, path, name);
761 icon = (Icon *) pi;
763 /* Widget takes the initial ref of Icon */
764 g_object_set_data(G_OBJECT(widget), "icon", pi);
766 pi->widget = widget;
767 g_object_ref(widget);
769 gtk_widget_set_name(pi->widget, "panel-icon");
771 g_signal_connect_swapped(widget, "destroy",
772 G_CALLBACK(panel_icon_destroyed), pi);
774 if (icon->item->base_type == TYPE_DIRECTORY)
775 run_applet(pi);
777 g_signal_connect(widget, "button_release_event",
778 G_CALLBACK(icon_button_release), pi);
779 g_signal_connect(widget, "button_press_event",
780 G_CALLBACK(icon_button_press), pi);
781 g_signal_connect(widget, "motion-notify-event",
782 G_CALLBACK(icon_motion_event), pi);
783 g_signal_connect(widget, "enter-notify-event",
784 G_CALLBACK(icon_pointer_in), pi);
785 g_signal_connect(widget, "leave-notify-event",
786 G_CALLBACK(icon_pointer_out), pi);
788 if (!pi->socket)
790 g_signal_connect(widget, "enter-notify-event",
791 G_CALLBACK(enter_icon), pi);
792 g_signal_connect_after(widget, "expose_event",
793 G_CALLBACK(expose_icon), pi);
794 g_signal_connect(widget, "drag_data_get",
795 G_CALLBACK(drag_data_get), NULL);
797 g_signal_connect(widget, "size_request",
798 G_CALLBACK(size_request), pi);
800 drag_set_panel_dest(pi);
802 pi->label = gtk_label_new(icon->item->leafname);
803 gtk_container_add(GTK_CONTAINER(pi->widget), pi->label);
804 gtk_misc_set_alignment(GTK_MISC(pi->label), 0.5, 1);
805 gtk_misc_set_padding(GTK_MISC(pi->label), 1, 2);
808 icon_set_shortcut(icon, shortcut);
809 icon_set_arguments(icon, args);
811 if (!loading_panel)
812 panel_save(panel);
814 panel_icon_set_tip(pi);
815 gtk_widget_show(widget);
818 static gboolean remove_item_from_side(GtkWidget *container, const gchar *path,
819 const gchar *label)
821 GList *kids, *next;
822 gboolean found = FALSE;
824 kids = gtk_container_get_children(GTK_CONTAINER(container));
826 for (next = kids; next; next = next->next)
828 Icon *icon;
829 icon = g_object_get_data(G_OBJECT(next->data), "icon");
830 if (!icon)
831 continue;
833 if ((!path || strcmp(path, icon->src_path) == 0) &&
834 (!label || strcmp(label, icon->item->leafname)==0))
836 icon_destroy(icon);
837 found = TRUE;
838 break;
842 g_list_free(kids);
844 return found;
847 /* Remove an item with this path. If more than one item matches, only
848 * one is removed. If label is not NULL then it must also match the item.
849 * Returns TRUE if an item was successfully removed.
851 gboolean panel_remove_item(PanelSide side, const gchar *path,
852 const gchar *label)
854 Panel *panel;
856 g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, FALSE);
858 g_return_val_if_fail(path != NULL || label != NULL, FALSE);
860 panel = current_panel[side];
861 if (!panel)
863 g_warning("No panel on this side of the screen!");
864 return FALSE;
867 if (remove_item_from_side(panel->before, path, label) ||
868 remove_item_from_side(panel->after, path, label))
870 panel_save(panel);
871 panel_update(panel);
872 return TRUE;
875 g_warning("Panel item path='%s', label='%s' not found", path, label);
876 return FALSE;
879 /* Called when Gtk+ wants to know how much space an icon needs.
880 * 'req' is already big enough for the label, if shown.
882 static void size_request(GtkWidget *widget, GtkRequisition *req, PanelIcon *pi)
884 Icon *icon = (Icon *) pi;
885 gboolean horz = (pi->panel->side == PANEL_TOP ||
886 pi->panel->side == PANEL_BOTTOM);
887 int max_width = 100;
888 int max_height = 100;
889 int image_width, image_height;
891 if (horz)
892 max_height = o_panel_width.int_value - req->height;
893 else
894 max_width = MAX(o_panel_width.int_value, req->width);
896 /* TODO: really need to recreate? */
897 if (pi->image)
898 g_object_unref(pi->image);
900 pi->image = scale_pixbuf(di_image(icon->item)->src_pixbuf,
901 MAX(20, max_width), MAX(20, max_height));
903 image_width = gdk_pixbuf_get_width(pi->image);
904 image_height = gdk_pixbuf_get_height(pi->image);
906 if (req->height > 0 && max_height < req->height)
908 pi->style = TEXT_BESIDE_ICON;
909 req->width += image_width;
910 req->height = MAX(req->height, image_height);
911 gtk_misc_set_alignment(GTK_MISC(pi->label), 1, 0.5);
913 else
915 pi->style = TEXT_UNDER_ICON;
916 req->width = MAX(req->width, image_width);
917 req->height += image_height;
918 gtk_misc_set_alignment(GTK_MISC(pi->label), 0.5, 1);
921 if (horz)
922 req->width += PANEL_ICON_SPACING;
923 else
924 req->height += PANEL_ICON_SPACING;
927 static gint expose_icon(GtkWidget *widget,
928 GdkEventExpose *event,
929 PanelIcon *pi)
931 return draw_icon(widget, &event->area, pi);
934 static gint draw_icon(GtkWidget *widget, GdkRectangle *badarea, PanelIcon *pi)
936 GdkRectangle area;
937 int width, height;
938 Icon *icon = (Icon *) pi;
939 int image_x;
940 int image_y;
941 GdkPixbuf *image;
942 int text_height = 0;
944 gdk_drawable_get_size(widget->window, &area.width, &area.height);
946 if (panel_want_show_text(pi))
947 text_height = pi->label->requisition.height;
949 g_return_val_if_fail(pi->image != NULL, FALSE);
951 image = pi->image;
953 width = gdk_pixbuf_get_width(image);
954 height = gdk_pixbuf_get_height(image);
956 if (pi->style == TEXT_UNDER_ICON)
958 image_x = (area.width - width) >> 1;
959 image_y = (area.height - height - text_height) >> 1;
961 else
963 image_x = PANEL_ICON_SPACING - 2;
964 image_y = (area.height - height) >> 1;
967 gdk_pixbuf_render_to_drawable_alpha(
968 image,
969 widget->window,
970 0, 0, /* src */
971 image_x, image_y, /* dest */
972 width, height,
973 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
974 GDK_RGB_DITHER_NORMAL, 0, 0);
976 if (icon->item->flags & ITEM_FLAG_SYMLINK)
978 gdk_pixbuf_render_to_drawable_alpha(im_symlink->pixbuf,
979 widget->window,
980 0, 0, /* src */
981 image_x, image_y + 2, /* dest */
982 -1, -1,
983 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
984 GDK_RGB_DITHER_NORMAL, 0, 0);
986 if (icon->item->flags & ITEM_FLAG_MOUNT_POINT)
988 MaskedPixmap *mp = icon->item->flags & ITEM_FLAG_MOUNTED
989 ? im_mounted
990 : im_unmounted;
992 gdk_pixbuf_render_to_drawable_alpha(mp->pixbuf,
993 widget->window,
994 0, 0, /* src */
995 image_x, image_y + 2, /* dest */
996 -1, -1,
997 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
998 GDK_RGB_DITHER_NORMAL, 0, 0);
1000 return FALSE;
1003 static void panel_icon_wink(Icon *icon)
1005 PanelIcon *pi = (PanelIcon *) icon;
1007 wink_widget(pi->widget);
1010 /* icon may be NULL if the event is on the background */
1011 static void perform_action(Panel *panel, PanelIcon *pi, GdkEventButton *event)
1013 BindAction action;
1014 Icon *icon = (Icon *) pi;
1016 action = bind_lookup_bev(icon ? BIND_PANEL_ICON : BIND_PANEL, event);
1018 if (pi && pi->socket)
1019 if (action != ACT_POPUP_MENU && action != ACT_MOVE_ICON)
1020 return;
1022 switch (action)
1024 case ACT_OPEN_ITEM:
1025 dnd_motion_ungrab();
1026 wink_widget(pi->widget);
1027 icon_run(icon);
1028 break;
1029 case ACT_EDIT_ITEM:
1030 dnd_motion_ungrab();
1031 wink_widget(pi->widget);
1032 run_diritem(icon->path, icon->item, NULL, NULL, TRUE);
1033 break;
1034 case ACT_POPUP_MENU:
1035 dnd_motion_ungrab();
1036 panel_show_menu(event, pi, panel);
1037 break;
1038 case ACT_MOVE_ICON:
1039 dnd_motion_start(MOTION_REPOSITION);
1040 break;
1041 case ACT_PRIME_AND_SELECT:
1042 if (!icon->selected)
1043 icon_select_only(icon);
1044 dnd_motion_start(MOTION_READY_FOR_DND);
1045 break;
1046 case ACT_PRIME_AND_TOGGLE:
1047 icon_set_selected(icon, !icon->selected);
1048 dnd_motion_start(MOTION_READY_FOR_DND);
1049 break;
1050 case ACT_PRIME_FOR_DND:
1051 dnd_motion_start(MOTION_READY_FOR_DND);
1052 break;
1053 case ACT_TOGGLE_SELECTED:
1054 icon_set_selected(icon, !icon->selected);
1055 break;
1056 case ACT_SELECT_EXCL:
1057 icon_set_selected(icon, TRUE);
1058 break;
1059 case ACT_IGNORE:
1060 break;
1061 case ACT_CLEAR_SELECTION:
1062 dnd_motion_ungrab();
1063 icon_select_only(NULL);
1064 break;
1065 default:
1066 g_warning("Unsupported action : %d\n", action);
1067 break;
1071 static gint panel_button_release(GtkWidget *widget,
1072 GdkEventButton *event,
1073 Panel *panel)
1075 if (dnd_motion_release(event))
1076 return TRUE;
1078 perform_action(panel, NULL, event);
1080 return TRUE;
1083 static gint panel_button_press(GtkWidget *widget,
1084 GdkEventButton *event,
1085 Panel *panel)
1087 if (dnd_motion_press(panel->window, event))
1088 perform_action(panel, NULL, event);
1090 return TRUE;
1093 static gint icon_button_release(GtkWidget *widget,
1094 GdkEventButton *event,
1095 PanelIcon *pi)
1097 if (pi->socket && event->button == 1)
1098 return FALSE; /* Restart button */
1100 if (dnd_motion_release(event))
1101 return TRUE;
1103 perform_action(pi->panel, pi, event);
1105 return TRUE;
1108 static gint icon_button_press(GtkWidget *widget,
1109 GdkEventButton *event,
1110 PanelIcon *pi)
1112 if (pi->socket && event->button == 1)
1113 return FALSE; /* Restart button */
1115 if (dnd_motion_press(widget, event))
1116 perform_action(pi->panel, pi, event);
1118 return TRUE;
1121 static void reposition_panel(GtkWidget *window,
1122 GtkAllocation *alloc, Panel *panel)
1124 int x = panel_geometry.x;
1125 int y = panel_geometry.y;
1126 int thickness;
1127 PanelSide side = panel->side;
1129 if (side == PANEL_LEFT || side == PANEL_RIGHT)
1131 if (side == PANEL_RIGHT)
1132 x += panel_geometry.width - alloc->width;
1134 if (current_panel[PANEL_TOP])
1136 GtkWidget *win = current_panel[PANEL_TOP]->window;
1137 y += win->allocation.height;
1141 if (side == PANEL_BOTTOM)
1142 y += panel_geometry.height - alloc->height;
1144 gtk_window_move(GTK_WINDOW(panel->window), x, y);
1145 gdk_window_move(panel->window->window, x, y);
1147 if (side == PANEL_BOTTOM || side == PANEL_TOP)
1149 if (current_panel[PANEL_RIGHT])
1150 gtk_widget_queue_resize(
1151 current_panel[PANEL_RIGHT]->window);
1152 if (current_panel[PANEL_LEFT])
1153 gtk_widget_queue_resize(
1154 current_panel[PANEL_LEFT]->window);
1157 /* Stop windows from maximising over all/part of us */
1159 struct {
1160 gulong left, right, top, bottom;
1161 gulong left_start_y, left_end_y;
1162 gulong right_start_y, right_end_y;
1163 gulong top_start_x, top_end_x;
1164 gulong bottom_start_x, bottom_end_x;
1165 } strut = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1167 if (o_panel_avoid.int_value == FALSE)
1168 thickness = 2;
1169 else if (panel->side == PANEL_TOP ||
1170 panel->side == PANEL_BOTTOM)
1171 thickness = alloc->height;
1172 else
1173 thickness = alloc->width;
1175 switch (panel->side)
1177 case PANEL_LEFT:
1178 if (!o_panel_xinerama.int_value ||
1179 !monitor_adjacent
1180 [o_panel_monitor.int_value].left)
1182 strut.left = panel_geometry.x +
1183 thickness;
1184 strut.left_start_y = panel_geometry.y;
1185 strut.left_end_y = panel_geometry.y +
1186 panel_geometry.height - 1;
1188 /* else there is (part of) a monitor
1189 * to the left */
1190 else
1192 thickness = 0;
1194 break;
1195 case PANEL_RIGHT:
1196 if (!o_panel_xinerama.int_value ||
1197 !monitor_adjacent
1198 [o_panel_monitor.int_value].right)
1200 /* RHS of monitor might not abut edge
1201 * of total virtual screen */
1202 strut.right = screen_width -
1203 panel_geometry.x -
1204 panel_geometry.width +
1205 thickness;
1206 strut.right_start_y = panel_geometry.y;
1207 strut.right_end_y = panel_geometry.y +
1208 panel_geometry.height - 1;
1210 /* else there is (part of) a monitor
1211 * to the right */
1212 else
1214 thickness = 0;
1216 break;
1217 case PANEL_TOP:
1218 if (!o_panel_xinerama.int_value ||
1219 !monitor_adjacent
1220 [o_panel_monitor.int_value].top)
1222 strut.top = panel_geometry.y +
1223 thickness;
1224 strut.top_start_x = panel_geometry.x;
1225 strut.top_end_x = panel_geometry.x +
1226 panel_geometry.width - 1;
1228 /* else there is (part of) a monitor above */
1229 else
1231 thickness = 0;
1233 break;
1234 default: /* PANEL_BOTTOM */
1235 if (!o_panel_xinerama.int_value ||
1236 !monitor_adjacent
1237 [o_panel_monitor.int_value].bottom)
1239 /* Bottom of monitor might not abut
1240 * edge of total virtual screen */
1241 strut.bottom = screen_height -
1242 panel_geometry.y -
1243 panel_geometry.height +
1244 thickness;
1245 strut.bottom_start_x = panel_geometry.x;
1246 strut.bottom_end_x = panel_geometry.x +
1247 panel_geometry.width - 1;
1249 /* else there is (part of) a monitor below */
1250 else
1252 thickness = 0;
1254 break;
1257 if (thickness)
1259 /* Set full-width strut as well as partial in case
1260 * partial isn't supported by wm */
1261 gdk_property_change(panel->window->window,
1262 gdk_atom_intern("_NET_WM_STRUT",
1263 FALSE),
1264 gdk_atom_intern("CARDINAL", FALSE),
1265 32, GDK_PROP_MODE_REPLACE,
1266 (gchar *) &strut, 4);
1267 gdk_property_change(panel->window->window,
1268 gdk_atom_intern("_NET_WM_STRUT_PARTIAL",
1269 FALSE),
1270 gdk_atom_intern("CARDINAL", FALSE),
1271 32, GDK_PROP_MODE_REPLACE,
1272 (gchar *) &strut, 12);
1274 else
1276 gdk_property_delete(panel->window->window,
1277 gdk_atom_intern("_NET_WM_STRUT_PARTIAL",
1278 FALSE));
1279 gdk_property_delete(panel->window->window,
1280 gdk_atom_intern("_NET_WM_STRUT",
1281 FALSE));
1287 /* Same as drag_set_dest(), but for panel icons */
1288 static void drag_set_panel_dest(PanelIcon *pi)
1290 GtkWidget *obj = pi->widget;
1292 make_drop_target(pi->widget, 0);
1294 g_signal_connect(obj, "drag_motion", G_CALLBACK(drag_motion), pi);
1295 g_signal_connect(obj, "drag_leave", G_CALLBACK(drag_leave), pi);
1296 g_signal_connect(obj, "drag_end", G_CALLBACK(drag_end), pi);
1299 static gboolean drag_motion(GtkWidget *widget,
1300 GdkDragContext *context,
1301 gint x,
1302 gint y,
1303 guint time,
1304 PanelIcon *pi)
1306 GdkDragAction action = context->suggested_action;
1307 const char *type = NULL;
1308 Icon *icon = (Icon *) pi;
1309 DirItem *item = icon->item;
1310 int panel_x, panel_y;
1312 gdk_window_get_pointer(pi->panel->window->window,
1313 &panel_x, &panel_y, NULL);
1314 motion_may_raise(pi->panel, panel_x, panel_y);
1316 /* Should we scroll the panel when dragging? */
1317 if (motion_state != MOTION_REPOSITION)
1318 if (pi->panel->autoscroll_speed == 0)
1319 may_autoscroll(pi->panel);
1321 if (icon->selected)
1322 goto out; /* Can't drag a selection to itself */
1324 type = dnd_motion_item(context, &item);
1326 if ((context->actions & GDK_ACTION_ASK) && o_dnd_left_menu.int_value
1327 && type != drop_dest_prog)
1329 guint state;
1330 gdk_window_get_pointer(NULL, NULL, NULL, &state);
1331 if (state & GDK_BUTTON1_MASK)
1332 action = GDK_ACTION_ASK;
1335 if (!item)
1336 type = NULL;
1337 out:
1338 /* We actually must pretend to accept the drop, even if the
1339 * directory isn't writeable, so that the spring-opening
1340 * thing works.
1343 /* Don't allow drops to non-writeable directories */
1344 if (o_dnd_spring_open.int_value == FALSE &&
1345 type == drop_dest_dir &&
1346 access(icon->path, W_OK) != 0)
1348 type = NULL;
1351 g_dataset_set_data(context, "drop_dest_type", (gpointer) type);
1352 if (type)
1354 gdk_drag_status(context, action, time);
1355 g_dataset_set_data_full(context, "drop_dest_path",
1356 g_strdup(icon->path), g_free);
1357 if (type == drop_dest_dir)
1358 dnd_spring_load(context, NULL);
1360 if (dnd_highlight && dnd_highlight != pi->widget)
1362 gtk_drag_unhighlight(dnd_highlight);
1363 dnd_highlight = NULL;
1366 if (dnd_highlight == NULL)
1368 gtk_drag_highlight(pi->widget);
1369 dnd_highlight = pi->widget;
1373 return type != NULL;
1377 static void add_uri_list(GtkWidget *widget,
1378 GdkDragContext *context,
1379 gint x,
1380 gint y,
1381 GtkSelectionData *selection_data,
1382 guint info,
1383 guint32 time,
1384 Panel *panel)
1386 gboolean after = FALSE;
1387 GList *uris, *next;
1389 if (!selection_data->data)
1390 return;
1392 g_return_if_fail(selection_data->data[selection_data->length] == '\0');
1394 if (g_object_get_data(G_OBJECT(widget), "after"))
1395 after = TRUE;
1397 uris = uri_list_to_glist(selection_data->data);
1399 for (next = uris; next; next = next->next)
1401 guchar *path;
1403 path = get_local_path((EscapedPath *) next->data);
1405 if (path) {
1406 panel_add_item(panel, path, NULL, after, NULL, NULL);
1407 g_free(path);
1411 g_list_free(uris);
1414 static void drag_end(GtkWidget *widget,
1415 GdkDragContext *context,
1416 Icon *icon)
1418 if (tmp_icon_selected)
1420 icon_select_only(NULL);
1421 tmp_icon_selected = FALSE;
1425 static void drag_leave(GtkWidget *widget,
1426 GdkDragContext *context,
1427 guint32 time,
1428 Icon *icon)
1430 panel_drag_leave(widget, context, time, ((PanelIcon *) icon)->panel);
1432 if (dnd_highlight && dnd_highlight == widget)
1434 gtk_drag_unhighlight(dnd_highlight);
1435 dnd_highlight = NULL;
1438 dnd_spring_abort();
1441 /* Create XML icon nodes for these widgets.
1442 * Always frees the widgets list.
1444 static void make_widgets(xmlNodePtr side, GList *widgets)
1446 GList *next;
1448 for (next = widgets; next; next = next->next)
1450 Icon *icon;
1451 xmlNodePtr tree;
1453 icon = g_object_get_data(G_OBJECT(next->data), "icon");
1455 if (!icon)
1457 g_warning("Can't find Icon from widget\n");
1458 continue;
1461 tree = xmlNewTextChild(side, NULL, "icon", icon->src_path);
1463 xmlSetProp(tree, "label", icon->item->leafname);
1464 if (icon->shortcut)
1465 xmlSetProp(tree, "shortcut", icon->shortcut);
1466 if (icon->args)
1467 xmlSetProp(tree, "args", icon->args);
1470 if (widgets)
1471 g_list_free(widgets);
1474 void panel_save(Panel *panel)
1476 xmlDocPtr doc;
1477 xmlNodePtr root;
1478 guchar *save = NULL;
1479 guchar *save_new = NULL;
1481 g_return_if_fail(panel != NULL);
1483 if (strchr(panel->name, '/'))
1484 save = g_strdup(panel->name);
1485 else
1487 guchar *leaf;
1489 leaf = g_strconcat("pan_", panel->name, NULL);
1490 save = choices_find_xdg_path_save(leaf, PROJECT, SITE, TRUE);
1491 g_free(leaf);
1494 if (!save)
1495 return;
1497 doc = xmlNewDoc("1.0");
1498 xmlDocSetRootElement(doc, xmlNewDocNode(doc, NULL, "panel", NULL));
1500 root = xmlDocGetRootElement(doc);
1502 xmlSetProp(root, "side",
1503 panel->side == PANEL_TOP ? "Top" :
1504 panel->side == PANEL_BOTTOM ? "Bottom" :
1505 panel->side == PANEL_LEFT ? "Left" :
1506 "Right");
1508 make_widgets(xmlNewChild(root, NULL, "start", NULL),
1509 gtk_container_get_children(GTK_CONTAINER(panel->before)));
1511 make_widgets(xmlNewChild(root, NULL, "end", NULL),
1512 g_list_reverse(gtk_container_get_children(
1513 GTK_CONTAINER(panel->after))));
1515 save_new = g_strconcat(save, ".new", NULL);
1516 if (save_xml_file(doc, save_new) || rename(save_new, save))
1517 delayed_error(_("Error saving panel %s: %s"),
1518 save, g_strerror(errno));
1519 g_free(save_new);
1521 g_free(save);
1522 if (doc)
1523 xmlFreeDoc(doc);
1526 /* Create a frame widget which can be used to add icons to the panel */
1527 static GtkWidget *make_insert_frame(Panel *panel)
1529 GtkWidget *frame;
1530 GtkTargetEntry target_table[] = {
1531 {"text/uri-list", 0, TARGET_URI_LIST},
1534 frame = gtk_frame_new(NULL);
1535 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
1536 gtk_widget_set_size_request(frame, 16, 16);
1538 g_signal_connect(frame, "drag-motion",
1539 G_CALLBACK(insert_drag_motion), panel);
1540 g_signal_connect(frame, "drag-leave",
1541 G_CALLBACK(panel_drag_leave), panel);
1543 g_signal_connect(frame, "drag-data-received",
1544 G_CALLBACK(add_uri_list), panel);
1545 gtk_drag_dest_set(frame,
1546 GTK_DEST_DEFAULT_ALL,
1547 target_table,
1548 sizeof(target_table) / sizeof(*target_table),
1549 GDK_ACTION_COPY);
1551 return frame;
1554 static gboolean enter_icon(GtkWidget *widget,
1555 GdkEventCrossing *event,
1556 Icon *icon)
1558 icon_may_update(icon);
1559 panel_icon_set_tip((PanelIcon *) icon);
1561 return FALSE;
1564 static gint panel_leave_event(GtkWidget *widget,
1565 GdkEventCrossing *event,
1566 Panel *panel)
1568 GdkWindow *pinboard;
1570 if (event->mode != GDK_CROSSING_NORMAL)
1571 return FALSE; /* Grab for menu, DnD, etc */
1573 keep_below(panel->window->window, TRUE);
1575 /* Shouldn't need this as well as keep_below but some WMs don't
1576 * automatically lower as soon as the hint is set */
1577 pinboard = pinboard_get_window();
1578 window_put_just_above(panel->window->window, pinboard);
1580 return FALSE;
1583 /* If (x, y) is at the edge of the panel then raise */
1584 static void motion_may_raise(Panel *panel, int x, int y)
1586 gboolean raise;
1588 if (panel->side == PANEL_TOP)
1589 raise = y == 0;
1590 else if (panel->side == PANEL_BOTTOM)
1591 raise = y == panel->window->allocation.height - 1;
1592 else if (panel->side == PANEL_LEFT)
1593 raise = x == 0;
1594 else
1595 raise = x == panel->window->allocation.width - 1;
1597 if (raise)
1599 keep_below(panel->window->window, FALSE);
1601 /* Shouldn't need this as well as keep_below but some WMs don't
1602 * automatically raise as soon as the hint is set */
1603 gdk_window_raise(panel->window->window);
1607 static gboolean may_autoscroll(Panel *panel)
1609 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1610 gint max, panel_x, panel_y, delta, new;
1612 if (panel->adj->upper <= panel->adj->page_size)
1613 goto stop_scrolling; /* Can see everything already */
1615 gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
1617 if (horz)
1619 delta = panel_x;
1620 max = panel->window->allocation.width;
1621 if (panel_y < 0 || panel_y > panel->window->allocation.height)
1622 goto stop_scrolling; /* Not over the panel */
1624 else
1626 delta = panel_y;
1627 max = panel->window->allocation.height;
1628 if (panel_x < 0 || panel_x > panel->window->allocation.width)
1629 goto stop_scrolling; /* Not over the panel */
1632 if (delta >= 20 && delta <= max - 20)
1633 goto stop_scrolling; /* Not at either end */
1635 panel->autoscroll_speed = MIN(panel->autoscroll_speed + 2, 200);
1637 new = panel->adj->value - ((delta < 20) ? panel->autoscroll_speed
1638 : -panel->autoscroll_speed);
1639 new = CLAMP(new, 0, panel->adj->upper - panel->adj->page_size);
1640 gtk_adjustment_set_value(panel->adj, new);
1642 panel->autoscroll_to = g_timeout_add(40,
1643 (GSourceFunc) may_autoscroll, panel);
1645 return FALSE;
1647 stop_scrolling:
1648 panel->autoscroll_speed = 0;
1649 return FALSE;
1652 static gint panel_motion_event(GtkWidget *widget,
1653 GdkEventMotion *event,
1654 Panel *panel)
1656 motion_may_raise(panel, event->x, event->y);
1658 if (motion_state != MOTION_REPOSITION)
1659 if (panel->autoscroll_speed == 0)
1660 may_autoscroll(panel);
1662 return FALSE;
1665 static gint icon_motion_event(GtkWidget *widget,
1666 GdkEventMotion *event,
1667 PanelIcon *pi)
1669 Panel *panel = pi->panel;
1670 GList *list, *me;
1671 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1672 int val;
1673 int dir = 0;
1675 if (motion_state == MOTION_READY_FOR_DND)
1677 if (dnd_motion_moved(event))
1678 start_drag(pi, event);
1679 return TRUE;
1681 else if (motion_state != MOTION_REPOSITION)
1682 return FALSE;
1684 list = gtk_container_get_children(GTK_CONTAINER(panel->before));
1685 list = g_list_append(list, NULL); /* The gap in the middle */
1686 list = g_list_concat(list,
1687 gtk_container_get_children(GTK_CONTAINER(panel->after)));
1688 me = g_list_find(list, widget);
1690 g_return_val_if_fail(me != NULL, TRUE);
1692 val = horz ? event->x_root : event->y_root;
1694 if (me->prev)
1696 GtkWidget *prev;
1697 int x, y;
1699 if (me->prev->data)
1700 prev = GTK_WIDGET(me->prev->data);
1701 else
1702 prev = panel->gap;
1704 gdk_window_get_origin(prev->window, &x, &y);
1706 if (val <= (horz ? x : y))
1707 dir = -1;
1710 if (dir == 0 && me->next)
1712 GtkWidget *next;
1713 int x, y, w, h;
1715 if (me->next->data)
1716 next = GTK_WIDGET(me->next->data);
1717 else
1718 next = panel->gap;
1720 gdk_window_get_origin(next->window, &x, &y);
1722 gdk_drawable_get_size(next->window, &w, &h);
1724 x += w;
1725 y += h;
1727 if (val >= (horz ? x : y)-1)
1729 if (next == panel->gap)
1730 dir = +2;
1731 else
1732 dir = +1;
1736 if (dir)
1737 reposition_icon(pi, g_list_index(list, widget) + dir);
1739 return TRUE;
1742 static void reposition_icon_on_side(GtkWidget *side, GtkWidget *widget,
1743 int index)
1745 GList *list;
1747 list = gtk_container_get_children(GTK_CONTAINER(side));
1749 /* Want to move icon to the list in the given 'side'. Is it there
1750 * already?
1753 if (!g_list_find(list, widget))
1755 /* No, reparent */
1756 gtk_grab_remove(widget);
1757 gtk_widget_reparent(widget, side);
1758 dnd_motion_grab_pointer();
1759 gtk_grab_add(widget);
1762 gtk_box_reorder_child(GTK_BOX(side), widget, index);
1764 g_list_free(list);
1767 /* Move icon to this index in the complete widget list.
1768 * 0 makes the icon the left-most icon. The gap in the middle has
1769 * an index number, which allows you to specify that the icon should
1770 * go on the left or right side.
1772 static void reposition_icon(PanelIcon *pi, int index)
1774 Panel *panel = pi->panel;
1775 GtkWidget *widget = pi->widget;
1776 GList *list;
1777 int before_len;
1779 list = gtk_container_get_children(GTK_CONTAINER(panel->before));
1780 before_len = g_list_length(list);
1781 g_list_free(list);
1783 if (index <= before_len)
1784 reposition_icon_on_side(panel->before, widget, index);
1785 else
1786 reposition_icon_on_side(panel->after, widget,
1787 index - (before_len + 1));
1789 panel_save(panel);
1792 static void start_drag(PanelIcon *pi, GdkEventMotion *event)
1794 GtkWidget *widget = pi->widget;
1795 Icon *icon = (Icon *) pi;
1797 if (!icon->selected)
1799 if (event->state & GDK_BUTTON1_MASK)
1801 /* Select just this one */
1802 icon_select_only(icon);
1803 tmp_icon_selected = TRUE;
1805 else
1806 icon_set_selected(icon, TRUE);
1809 g_return_if_fail(icon_selection != NULL);
1811 if (icon_selection->next == NULL)
1812 drag_one_item(widget, event, icon->path, icon->item, NULL);
1813 else
1815 guchar *uri_list;
1817 uri_list = icon_create_uri_list();
1818 drag_selection(widget, event, uri_list);
1819 g_free(uri_list);
1823 static void applet_died(GtkWidget *socket)
1825 gboolean never_plugged;
1827 never_plugged = (!g_object_get_data(G_OBJECT(socket), "lost_plug"))
1828 && !GTK_SOCKET(socket)->plug_window;
1830 if (never_plugged)
1832 report_error(
1833 _("Applet quit without ever creating a widget!"));
1834 gtk_widget_destroy(socket);
1837 gtk_widget_unref(socket);
1840 static void socket_destroyed(GtkWidget *socket, GtkWidget *widget)
1842 g_object_set_data(G_OBJECT(socket), "lost_plug", "yes");
1844 gtk_widget_unref(socket);
1846 gtk_widget_destroy(widget); /* Remove from panel */
1848 if (!closing_panel)
1849 panel_save(g_object_get_data(G_OBJECT(socket), "panel"));
1852 /* Try to run this applet.
1853 * Cases:
1855 * - No executable AppletRun:
1856 * icon->socket == NULL (unchanged) on return.
1858 * Otherwise, create socket (setting icon->socket) and ref it twice.
1860 * - AppletRun quits without connecting a plug:
1861 * On child death lost_plug is unset and socket is empty.
1862 * Unref socket.
1863 * Report error and destroy widget (to 'socket destroyed').
1865 * - AppletRun quits while plug is in socket:
1866 * Unref socket once. Socket will be destroyed later.
1868 * - Socket is destroyed.
1869 * Set lost_plug = "yes" and remove widget from panel.
1870 * Unref socket.
1872 static void run_applet(PanelIcon *pi)
1874 GError *error = NULL;
1875 char *argv[3];
1876 gint pid;
1877 Icon *icon = (Icon *) pi;
1879 argv[0] = (char *) make_path(icon->path, "AppletRun");
1881 if (access(argv[0], X_OK) != 0)
1882 return;
1884 pi->socket = gtk_socket_new();
1886 gtk_container_add(GTK_CONTAINER(pi->widget), pi->socket);
1887 gtk_widget_show_all(pi->socket);
1888 gtk_widget_realize(pi->socket);
1890 /* Always get button-2 events so we can drag */
1891 XGrabButton(gdk_display, Button2, AnyModifier,
1892 GDK_WINDOW_XWINDOW(pi->socket->window),
1893 False,
1894 ButtonPressMask | ButtonReleaseMask | Button2MotionMask,
1895 GrabModeAsync, /* Pointer */
1896 GrabModeAsync, /* Keyboard */
1897 None, None);
1900 gchar *pos;
1901 PanelSide side = pi->panel->side;
1903 /* Set a hint to let applets position their menus correctly */
1904 pos = g_strdup_printf("%s,%d",
1905 side == PANEL_TOP ? "Top" :
1906 side == PANEL_BOTTOM ? "Bottom" :
1907 side == PANEL_LEFT ? "Left" :
1908 "Right", MENU_MARGIN(side));
1909 gdk_property_change(pi->socket->window,
1910 gdk_atom_intern("_ROX_PANEL_MENU_POS", FALSE),
1911 gdk_atom_intern("STRING", FALSE),
1912 8, GDK_PROP_MODE_REPLACE,
1913 pos, strlen(pos));
1914 g_free(pos);
1916 /* Ensure that the properties are set before starting the
1917 * applet.
1919 gdk_flush();
1922 g_object_set_data(G_OBJECT(pi->widget), "icon", pi);
1923 g_object_set_data(G_OBJECT(pi->socket), "panel", pi->panel);
1925 argv[1] = g_strdup_printf("%ld",
1926 GDK_WINDOW_XWINDOW(pi->socket->window));
1927 argv[2] = NULL;
1929 if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
1930 NULL, NULL, &pid, &error))
1932 delayed_error(_("Error running applet:\n%s"), error->message);
1933 g_error_free(error);
1934 gtk_widget_destroy(pi->socket);
1935 pi->socket = NULL;
1937 else
1939 gtk_widget_ref(pi->socket);
1940 on_child_death(pid, (CallbackFn) applet_died, pi->socket);
1942 gtk_widget_ref(pi->socket);
1943 g_signal_connect(pi->socket, "destroy",
1944 G_CALLBACK(socket_destroyed), pi->widget);
1947 g_free(argv[1]);
1950 static void panel_post_resize(GtkWidget *win, GtkRequisition *req, Panel *panel)
1952 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
1954 req->width = panel_geometry.width;
1955 req->height += EDGE_WIDTH;
1957 else
1959 int h = panel_geometry.height;
1961 if (current_panel[PANEL_TOP])
1963 GtkWidget *win = current_panel[PANEL_TOP]->window;
1964 h -= win->allocation.height;
1967 if (current_panel[PANEL_BOTTOM])
1969 GtkWidget *win = current_panel[PANEL_BOTTOM]->window;
1970 h -= win->allocation.height;
1973 req->height = h;
1974 req->width += EDGE_WIDTH;
1978 static void update_side(GtkWidget *side)
1980 GList *kids, *next;
1982 kids = gtk_container_get_children(GTK_CONTAINER(side));
1983 for (next = kids; next; next = next->next)
1985 PanelIcon *pi;
1986 pi = g_object_get_data(next->data, "icon");
1987 panel_icon_set_tip(pi);
1989 g_list_free(kids);
1992 /* Tips or style has changed -- update everything on this panel */
1993 static void panel_set_style(Panel *panel)
1995 update_side(panel->before);
1996 update_side(panel->after);
1997 gtk_widget_queue_resize(panel->window);
2000 static gboolean recreate_panels(char **names)
2002 int i;
2004 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
2006 if (names[i])
2008 panel_new(names[i], i);
2009 g_free(names[i]);
2013 g_free(names);
2015 return FALSE;
2018 static void update_side_size(GtkWidget *side)
2020 GList *kids, *next;
2022 kids = gtk_container_get_children(GTK_CONTAINER(side));
2023 for (next = kids; next; next = next->next)
2025 PanelIcon *pi;
2026 pi = g_object_get_data(next->data, "icon");
2027 gtk_widget_queue_resize(pi->widget);
2029 g_list_free(kids);
2032 /* Update panel size and redraw */
2033 static void panel_update(Panel *panel)
2035 update_side_size(panel->before);
2036 update_side_size(panel->after);
2037 gtk_widget_queue_resize(panel->window);
2038 gtk_widget_queue_draw(panel->window);
2041 static void panel_style_changed(void)
2043 int i;
2045 if (o_override_redirect.has_changed)
2047 gchar **names;
2049 names = g_new(char *, PANEL_NUMBER_OF_SIDES);
2051 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
2053 Panel *panel = current_panel[i];
2054 names[i] = panel ? g_strdup(panel->name) : NULL;
2055 panel_new(NULL, i);
2058 g_idle_add((GtkFunction) recreate_panels, names);
2061 if (o_panel_style.has_changed)
2063 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
2065 if (current_panel[i])
2066 panel_set_style(current_panel[i]);
2069 if (o_panel_width.has_changed)
2071 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
2073 if (current_panel[i])
2074 panel_update(current_panel[i]);
2078 if (o_panel_xinerama.has_changed || o_panel_monitor.has_changed ||
2079 o_panel_avoid.has_changed)
2081 if (panel_check_xinerama() || o_panel_avoid.has_changed)
2083 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
2085 if (current_panel[i])
2087 reposition_panel(
2088 current_panel[i]->window,
2089 &current_panel[i]->
2090 window->allocation,
2091 current_panel[i]);
2092 gtk_widget_queue_resize(
2093 current_panel[i]->window);
2100 static gboolean draw_panel_edge(GtkWidget *widget, GdkEventExpose *event,
2101 Panel *panel)
2103 int x, y, width, height;
2105 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
2107 width = panel_geometry.width;
2108 height = EDGE_WIDTH;
2110 x = 0;
2111 if (panel->side == PANEL_BOTTOM)
2112 y = 0;
2113 else
2114 y = widget->allocation.height - EDGE_WIDTH;
2116 else
2118 width = EDGE_WIDTH;
2119 height = panel_geometry.height;
2121 y = 0;
2122 if (panel->side == PANEL_RIGHT)
2123 x = 0;
2124 else
2125 x = widget->allocation.width - EDGE_WIDTH;
2128 gdk_draw_rectangle(widget->window,
2129 widget->style->fg_gc[GTK_STATE_NORMAL], TRUE,
2130 x, y, width, height);
2132 return FALSE;
2135 static gpointer parent_class;
2137 static void panel_icon_destroy(Icon *icon)
2139 PanelIcon *pi = (PanelIcon *) icon;
2141 g_return_if_fail(pi != NULL);
2143 if (pi->image)
2144 g_object_unref(pi->image);
2146 g_return_if_fail(pi->widget != NULL);
2148 gtk_widget_destroy(pi->widget);
2151 static void panel_remove_items(void)
2153 Panel *panel;
2155 g_return_if_fail(icon_selection != NULL);
2157 panel = ((PanelIcon *) icon_selection->data)->panel;
2159 while (icon_selection)
2160 icon_destroy((Icon *) icon_selection->data);
2162 panel_save(panel);
2165 static void panel_icon_redraw(Icon *icon)
2167 gtk_widget_set_state(((PanelIcon *) icon)->widget,
2168 icon->selected ? GTK_STATE_SELECTED
2169 : GTK_STATE_NORMAL);
2170 gtk_widget_queue_draw(PANEL_ICON(icon)->widget);
2171 panel_icon_set_tip((PanelIcon *) icon);
2174 static void panel_icon_update(Icon *icon)
2176 PanelIcon *pi = (PanelIcon *) icon;
2178 gtk_widget_queue_draw(pi->widget);
2179 gtk_label_set_text(GTK_LABEL(pi->label), icon->item->leafname);
2180 panel_save(pi->panel);
2183 /* The point of this is to clear the selection if the existing icons
2184 * aren't from the same panel...
2186 static gboolean panel_icon_same_group(Icon *icon, Icon *other)
2188 if (IS_PANEL_ICON(other))
2190 PanelIcon *a = (PanelIcon *) icon;
2191 PanelIcon *b = (PanelIcon *) other;
2193 return a->panel == b->panel;
2195 else
2196 return FALSE;
2199 static void panel_icon_class_init(gpointer gclass, gpointer data)
2201 IconClass *icon = (IconClass *) gclass;
2203 parent_class = g_type_class_peek_parent(gclass);
2205 icon->destroy = panel_icon_destroy;
2206 icon->redraw = panel_icon_redraw;
2207 icon->update = panel_icon_update;
2208 icon->remove_items = panel_remove_items;
2209 icon->same_group = panel_icon_same_group;
2210 icon->wink = panel_icon_wink;
2213 static void panel_icon_init(GTypeInstance *object, gpointer gclass)
2215 PanelIcon *pi = (PanelIcon *) object;
2217 pi->widget = NULL;
2218 pi->image = NULL;
2219 pi->label = NULL;
2220 pi->socket = NULL;
2221 pi->style = TEXT_UNDER_ICON;
2224 static GType panel_icon_get_type(void)
2226 static GType type = 0;
2228 if (!type)
2230 static const GTypeInfo info =
2232 sizeof (PanelIconClass),
2233 NULL, /* base_init */
2234 NULL, /* base_finalise */
2235 panel_icon_class_init,
2236 NULL, /* class_finalise */
2237 NULL, /* class_data */
2238 sizeof(PanelIcon),
2239 0, /* n_preallocs */
2240 panel_icon_init
2243 type = g_type_register_static(icon_get_type(),
2244 "PanelIcon", &info, 0);
2247 return type;
2250 static PanelIcon *panel_icon_new(Panel *panel,
2251 const char *pathname,
2252 const char *name)
2254 PanelIcon *pi;
2255 Icon *icon;
2257 pi = g_object_new(panel_icon_get_type(), NULL);
2258 icon = (Icon *) pi;
2260 icon_set_path(icon, pathname, name);
2261 pi->panel = panel;
2263 return pi;
2266 static gboolean panel_want_show_text(PanelIcon *pi)
2268 Icon *icon = (Icon *) pi;
2270 if (!icon->item->leafname[0])
2271 return FALSE;
2273 if (o_panel_style.int_value == SHOW_BOTH)
2274 return TRUE;
2275 if (o_panel_style.int_value == SHOW_ICON)
2276 return FALSE;
2278 if (icon->item->flags & ITEM_FLAG_APPDIR)
2279 return FALSE;
2281 if (EXECUTABLE_FILE(icon->item))
2282 return FALSE;
2284 return TRUE;
2287 static void panel_position_menu(GtkMenu *menu, gint *x, gint *y,
2288 gboolean *push_in, gpointer data)
2290 int *pos = (int *) data;
2291 GtkRequisition requisition;
2292 int margin = pos[2];
2293 int mon = pos[3];
2294 int mon_right = monitor_geom[mon].x +
2295 monitor_geom[mon].width;
2296 int mon_bottom = monitor_geom[mon].y +
2297 monitor_geom[mon].height;
2299 gtk_widget_size_request(GTK_WIDGET(menu), &requisition);
2301 if (pos[0] == -1)
2302 *x = mon_right - margin - requisition.width;
2303 else if (pos[0] == -2)
2304 *x = monitor_geom[mon].x + margin;
2305 else
2306 *x = pos[0] - (requisition.width >> 2);
2308 if (pos[1] == -1)
2309 *y = mon_bottom - margin - requisition.height;
2310 else if (pos[1] == -2)
2311 *y = monitor_geom[mon].y + margin;
2312 else
2313 *y = pos[1] - (requisition.height >> 2);
2315 *x = CLAMP(*x, 0, mon_right - requisition.width);
2316 *y = CLAMP(*y, 0, mon_bottom - requisition.height);
2318 *push_in = FALSE;
2321 static void panel_show_menu(GdkEventButton *event, PanelIcon *pi, Panel *panel)
2323 GtkWidget *option_item;
2324 PanelSide side = panel->side;
2325 int pos[4];
2327 pos[0] = event->x_root;
2328 pos[1] = event->y_root;
2329 pos[2] = MENU_MARGIN(side);
2330 /* FIXME: Should we read screen from event's window rather than
2331 * using default? */
2332 pos[3] = gdk_screen_get_monitor_at_point(
2333 gdk_screen_get_default(),
2334 event->x_root, event->y_root);
2336 option_item = gtk_image_menu_item_new_with_label(_("Panel Options..."));
2337 g_signal_connect_swapped(option_item, "activate",
2338 G_CALLBACK(panel_show_options),
2339 GINT_TO_POINTER(panel->side));
2341 icon_prepare_menu((Icon *) pi, option_item);
2343 if (side == PANEL_LEFT)
2344 pos[0] = -2;
2345 else if (side == PANEL_RIGHT)
2346 pos[0] = -1;
2348 if (side == PANEL_TOP)
2349 pos[1] = -2;
2350 else if (side == PANEL_BOTTOM)
2351 pos[1] = -1;
2353 gtk_menu_popup(GTK_MENU(icon_menu), NULL, NULL,
2354 panel_position_menu,
2355 (gpointer) pos, event->button, event->time);
2358 /* Note: also called from icon handler */
2359 static gboolean panel_drag_motion(GtkWidget *widget,
2360 GdkDragContext *context,
2361 gint x,
2362 gint y,
2363 guint time,
2364 Panel *panel)
2366 int panel_x, panel_y;
2368 gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
2370 motion_may_raise(panel, panel_x, panel_y);
2371 gdk_drag_status(context, 0, time);
2373 return TRUE;
2376 static gboolean insert_drag_motion(GtkWidget *widget,
2377 GdkDragContext *context,
2378 gint x,
2379 gint y,
2380 guint time,
2381 Panel *panel)
2383 int panel_x, panel_y;
2385 gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
2386 motion_may_raise(panel, panel_x, panel_y);
2388 return FALSE;
2391 /* Note: also called from icon handler */
2392 static void panel_drag_leave(GtkWidget *widget,
2393 GdkDragContext *context,
2394 guint32 time,
2395 Panel *panel)
2397 GdkWindow *pinboard, *window;
2398 GtkAllocation *alloc = &panel->window->allocation;
2399 int x, y;
2401 window = panel->window->window;
2402 gdk_window_get_pointer(window, &x, &y, NULL);
2403 if (x < 0 || y < 0 || x > alloc->width || y > alloc->height)
2405 keep_below(panel->window->window, TRUE);
2407 /* Shouldn't need this as well as keep_below but some WMs don't
2408 * automatically lower as soon as the hint is set */
2409 pinboard = pinboard_get_window();
2410 window_put_just_above(panel->window->window, pinboard);
2414 static gboolean panel_check_xinerama(void)
2416 gint old_monitor = panel_monitor;
2418 panel_monitor = -1;
2420 if (o_panel_xinerama.int_value)
2422 if (o_panel_monitor.int_value < n_monitors)
2424 panel_monitor = o_panel_monitor.int_value;
2426 else
2428 g_warning(_("Xinerama monitor %d unavailable"),
2429 o_panel_monitor.int_value);
2433 if (panel_monitor == -1)
2435 panel_geometry.x = panel_geometry.y = 0;
2436 panel_geometry.width = screen_width;
2437 panel_geometry.height = screen_height;
2439 else
2441 panel_geometry = monitor_geom[panel_monitor];
2444 return old_monitor != panel_monitor;
2447 static GList *build_monitor_number(Option *option, xmlNode *node, guchar *label)
2449 GtkObject *adj;
2451 adj = gtk_adjustment_new(MAX(0, panel_monitor),
2452 0, n_monitors - 1, 1, 10, 1);
2453 return build_numentry_base(option, node, label, GTK_ADJUSTMENT(adj));
2456 static void side_changed(Radios *radios, gpointer data)
2458 Panel *panel;
2459 PanelSide side, new_side;
2460 GObject *dialog = G_OBJECT(data);
2461 char *name, *other_side_name;
2463 side = GPOINTER_TO_INT(g_object_get_data(dialog, "rox-panel-side"));
2464 g_return_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES);
2465 panel = current_panel[side];
2466 g_return_if_fail(panel != NULL);
2468 new_side = radios_get_value(radios);
2470 if (new_side == side)
2471 return;
2473 name = g_strdup(panel->name);
2474 other_side_name = current_panel[new_side]
2475 ? g_strdup(current_panel[new_side]->name)
2476 : NULL;
2478 panel_new(name, new_side);
2479 g_object_set_data(G_OBJECT(dialog), "rox-panel-side",
2480 GINT_TO_POINTER(new_side));
2481 panel_new(other_side_name, side);
2483 g_free(name);
2484 g_free(other_side_name);
2487 static void panel_show_options(gpointer data)
2489 GtkWidget *dialog;
2490 GtkWidget *vbox, *label;
2491 Radios *radios;
2492 Panel *panel;
2493 PanelSide side = GPOINTER_TO_INT(data);
2494 char *tmp;
2496 g_return_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES);
2497 panel = current_panel[side];
2498 g_return_if_fail(panel != NULL);
2500 if (panel_options_dialog)
2501 gtk_widget_destroy(panel_options_dialog);
2503 dialog = gtk_dialog_new_with_buttons(_("Panel Options"), NULL,
2504 GTK_DIALOG_NO_SEPARATOR,
2505 GTK_STOCK_CLOSE, GTK_RESPONSE_OK,
2506 NULL);
2507 panel_options_dialog = dialog;
2509 g_object_set_data(G_OBJECT(dialog), "rox-panel-side",
2510 GINT_TO_POINTER(panel->side));
2512 vbox = gtk_vbox_new(FALSE, 0);
2513 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
2514 vbox, TRUE, TRUE, 0);
2516 gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
2518 tmp = g_strdup_printf(_("Panel: %s"), panel->name);
2519 label = gtk_label_new(tmp);
2520 make_heading(label, PANGO_SCALE_LARGE);
2521 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
2522 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
2523 g_free(tmp);
2525 gtk_box_pack_start(GTK_BOX(vbox),
2526 gtk_label_new(_("Select the panel's position:")),
2527 FALSE, TRUE, 4);
2529 /* Radio buttons to set the side */
2530 radios = radios_new(side_changed, dialog);
2532 radios_add(radios, _("Top-edge panel"),
2533 PANEL_TOP, _("Top edge"));
2534 radios_add(radios, _("Bottom edge panel"),
2535 PANEL_BOTTOM, _("Bottom edge"));
2536 radios_add(radios, _("Left edge panel"),
2537 PANEL_LEFT, _("Left edge"));
2538 radios_add(radios, _("Right-edge panel"),
2539 PANEL_RIGHT, _("Right edge"));
2541 radios_set_value(radios, panel->side);
2543 radios_pack(radios, GTK_BOX(vbox));
2545 g_signal_connect(dialog, "destroy",
2546 G_CALLBACK(gtk_widget_destroyed),
2547 &panel_options_dialog);
2549 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
2551 g_signal_connect(dialog, "response",
2552 G_CALLBACK(gtk_widget_destroy), NULL);
2553 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
2555 gtk_widget_show_all(dialog);
2558 /* Returns PANEL_NUMBER_OF_SIDES if name is invalid */
2559 PanelSide panel_name_to_side(gchar *side)
2561 if (strcmp(side, "Top") == 0)
2562 return PANEL_TOP;
2563 else if (strcmp(side, "Bottom") == 0)
2564 return PANEL_BOTTOM;
2565 else if (strcmp(side, "Left") == 0)
2566 return PANEL_LEFT;
2567 else if (strcmp(side, "Right") == 0)
2568 return PANEL_RIGHT;
2569 else
2570 g_warning("Unknown panel side '%s'", side);
2571 return PANEL_NUMBER_OF_SIDES;