Add panel menu items are now functional.
[rox-filer.git] / ROX-Filer / src / panel.c
blob7b244ecd289a7fa9879c97326eaf832903cab9d5
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 <stdlib.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <ctype.h>
29 #include <unistd.h>
30 #include <libxml/parser.h>
32 #include <gtk/gtk.h>
33 #include <gdk/gdkx.h>
35 #include "global.h"
37 #include "panel.h"
38 #include "options.h"
39 #include "choices.h"
40 #include "main.h"
41 #include "type.h"
42 #include "gui_support.h"
43 #include "diritem.h"
44 #include "pixmaps.h"
45 #include "filer.h"
46 #include "display.h"
47 #include "bind.h"
48 #include "dnd.h"
49 #include "support.h"
50 #include "icon.h"
51 #include "run.h"
52 #include "appinfo.h"
53 #include "pixmaps.h"
54 #include "xml.h"
55 #include "pinboard.h" /* For pinboard_get_window() */
57 /* The width of the separator at the inner edge of the panel */
58 #define EDGE_WIDTH 1
60 /* The gap between panel icons */
61 #define PANEL_ICON_SPACING 8
63 enum {TEXT_BESIDE_ICON, TEXT_UNDER_ICON};
65 static gboolean tmp_icon_selected = FALSE; /* When dragging */
67 typedef struct _PanelIconClass PanelIconClass;
68 typedef struct _PanelIcon PanelIcon;
70 struct _PanelIconClass {
71 IconClass parent;
74 struct _PanelIcon {
75 Icon icon;
76 GdkPixbuf *image;
78 Panel *panel;
79 GtkWidget *widget; /* The drawing area for the icon */
80 GtkWidget *label;
81 GtkWidget *socket; /* For applets */
83 int style;
86 #define PANEL_ICON(obj) GTK_CHECK_CAST((obj), panel_icon_get_type(), PanelIcon)
87 #define IS_PANEL_ICON(obj) \
88 G_TYPE_CHECK_INSTANCE_TYPE((obj), panel_icon_get_type())
90 Panel *current_panel[PANEL_NUMBER_OF_SIDES];
92 /* NULL => Not loading a panel */
93 static Panel *loading_panel = NULL;
95 static GtkWidget *panel_options_dialog = NULL;
97 /* Static prototypes */
98 static int panel_delete(GtkWidget *widget, GdkEvent *event, Panel *panel);
99 static void panel_destroyed(GtkWidget *widget, Panel *panel);
100 static const char *pan_from_file(gchar *line);
101 static gint icon_button_release(GtkWidget *widget,
102 GdkEventButton *event,
103 PanelIcon *pi);
104 static gint icon_button_press(GtkWidget *widget,
105 GdkEventButton *event,
106 PanelIcon *pi);
107 static void reposition_panel(GtkWidget *window,
108 GtkAllocation *alloc, Panel *panel);
109 static gint expose_icon(GtkWidget *widget,
110 GdkEventExpose *event,
111 PanelIcon *pi);
112 static gint draw_icon(GtkWidget *widget,
113 GdkRectangle *badarea,
114 PanelIcon *pi);
115 static gint panel_button_release(GtkWidget *widget,
116 GdkEventButton *event,
117 Panel *panel);
118 static gint panel_button_press(GtkWidget *widget,
119 GdkEventButton *event,
120 Panel *panel);
121 static void panel_post_resize(GtkWidget *box,
122 GtkRequisition *req, Panel *panel);
123 static void drag_set_panel_dest(PanelIcon *pi);
124 static void add_uri_list(GtkWidget *widget,
125 GdkDragContext *context,
126 gint x,
127 gint y,
128 GtkSelectionData *selection_data,
129 guint info,
130 guint32 time,
131 Panel *panel);
132 static void panel_add_item(Panel *panel,
133 const gchar *path,
134 const gchar *name,
135 gboolean after,
136 const gchar *shortcut,
137 const gchar *args,
138 gboolean locked);
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 GList *build_monitor_number(Option *option,
201 xmlNode *node, guchar *label);
202 static gboolean may_autoscroll(Panel *panel);
203 static void panel_update_geometry(Panel *panel);
206 static GtkWidget *dnd_highlight = NULL; /* (stops flickering) */
208 #define SHOW_BOTH 0
209 #define SHOW_APPS_SMALL 1
210 #define SHOW_ICON 2
211 static Option o_panel_style;
212 static Option o_panel_width;
213 static Option o_panel_xinerama;
214 static Option o_panel_monitor;
215 static Option o_panel_avoid;
216 static Option o_panel_is_dock;
218 static gint panel_monitor = -1;
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);
242 /* Return a free edge for a new panel.
243 * If no edge is free, returns PANEL_BOTTOM.
245 static PanelSide find_free_side()
247 if (!current_panel[PANEL_BOTTOM])
248 return PANEL_BOTTOM;
250 if (!current_panel[PANEL_TOP])
251 return PANEL_TOP;
253 if (!current_panel[PANEL_LEFT])
254 return PANEL_LEFT;
256 if (!current_panel[PANEL_RIGHT])
257 return PANEL_RIGHT;
259 return PANEL_BOTTOM;
262 /* Returns TRUE and sets *target if the property exists, otherwise returns
263 * FALSE and leaves *target unchanged */
264 static gboolean get_int_prop(xmlNodePtr node, const char *name, int *target)
266 char *prop = xmlGetProp(node, name);
268 if (prop)
270 *target = atoi(prop);
271 g_free(prop);
272 return TRUE;
274 return FALSE;
277 static void set_int_prop(xmlNodePtr node, const char *name, int value)
279 char prop[16];
281 sprintf(prop, "%d", value);
282 xmlSetProp(node, name, prop);
285 static void panel_load_options_from_xml(Panel *panel, xmlDocPtr doc)
287 xmlNodePtr root;
288 xmlNodePtr options;
290 root = xmlDocGetRootElement(doc);
291 options = get_subnode(root, NULL, "options");
292 if (!options)
293 return;
294 get_int_prop(options, "style", &panel->style);
295 get_int_prop(options, "width", &panel->width);
296 get_int_prop(options, "avoid", &panel->avoid);
297 get_int_prop(options, "xinerama", &panel->xinerama);
298 get_int_prop(options, "monitor", &panel->monitor);
301 /* 'name' may be NULL or "" to remove the panel */
302 Panel *panel_new(const gchar *name, PanelSide side)
304 guchar *load_path;
305 Panel *panel;
306 GtkWidget *vp, *box, *frame, *align;
307 xmlDocPtr panel_doc = NULL;
308 gboolean need_resave = FALSE;
310 g_return_val_if_fail(side == PANEL_DEFAULT_SIDE ||
311 (side >= 0 && side < PANEL_NUMBER_OF_SIDES), NULL);
312 g_return_val_if_fail(loading_panel == NULL, NULL);
314 if (name && *name == '\0')
315 name = NULL;
317 if (!name)
318 load_path = NULL;
319 else if (strchr(name, '/'))
320 load_path = g_strdup(name);
321 else
323 guchar *leaf;
325 leaf = g_strconcat("pan_", name, NULL);
326 load_path = choices_find_xdg_path_load(leaf, PROJECT, SITE);
327 g_free(leaf);
330 if (load_path && access(load_path, F_OK) == 0)
332 char *saved_side;
333 xmlNodePtr root;
335 panel_doc = xmlParseFile(load_path);
336 root = xmlDocGetRootElement(panel_doc);
338 saved_side = xmlGetProp(root, "side");
339 if (saved_side)
341 PanelSide old_side;
342 old_side = panel_name_to_side(saved_side);
343 g_free(saved_side);
345 if (side == PANEL_DEFAULT_SIDE)
346 side = old_side;
347 else if (side != old_side)
348 need_resave = TRUE;
350 else
351 need_resave = TRUE;
354 if (side == PANEL_DEFAULT_SIDE)
355 side = find_free_side();
357 if (current_panel[side])
359 if (name)
360 number_of_windows++;
361 closing_panel++;
362 gtk_widget_destroy(current_panel[side]->window);
363 closing_panel--;
364 if (name)
365 number_of_windows--;
368 if (name == NULL || *name == '\0')
369 return NULL;
371 panel = g_new(Panel, 1);
372 panel->name = g_strdup(name);
373 panel->side = side;
374 panel->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
375 panel->autoscroll_speed = 0;
377 /* These are fallbacks from legacy global options */
378 panel->style = o_panel_style.int_value;
379 panel->width = o_panel_width.int_value;
380 panel->xinerama = o_panel_xinerama.int_value;
381 panel->monitor = o_panel_monitor.int_value;
382 panel->avoid = o_panel_avoid.int_value;
384 /* Now try to load options from this panel's XML */
385 if (panel_doc)
387 panel_load_options_from_xml(panel, panel_doc);
389 else
391 /* Otherwise ensure old settings are migrated */
392 need_resave = TRUE;
395 panel_update_geometry(panel);
397 gtk_window_set_resizable(GTK_WINDOW(panel->window), FALSE);
398 gtk_window_set_wmclass(GTK_WINDOW(panel->window), "ROX-Panel", PROJECT);
399 gtk_widget_set_name(panel->window, "rox-panel");
400 gtk_widget_set_events(panel->window,
401 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
402 GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
404 /* We make the panel a drop target only so that we can auto-raise! */
405 gtk_drag_dest_set(panel->window, 0, NULL, 0, GDK_ACTION_PRIVATE);
406 g_signal_connect(panel->window, "drag_leave",
407 G_CALLBACK(panel_drag_leave), panel);
408 g_signal_connect(panel->window, "drag_motion",
409 G_CALLBACK(panel_drag_motion), panel);
411 g_signal_connect(panel->window, "delete-event",
412 G_CALLBACK(panel_delete), panel);
413 g_signal_connect(panel->window, "destroy",
414 G_CALLBACK(panel_destroyed), panel);
415 g_signal_connect(panel->window, "button_press_event",
416 G_CALLBACK(panel_button_press), panel);
417 g_signal_connect(panel->window, "button_release_event",
418 G_CALLBACK(panel_button_release), panel);
419 g_signal_connect(panel->window, "motion-notify-event",
420 G_CALLBACK(panel_motion_event), panel);
421 g_signal_connect(panel->window, "leave-notify-event",
422 G_CALLBACK(panel_leave_event), panel);
424 if (panel->side == PANEL_RIGHT)
425 align = gtk_alignment_new(1.0, 0.0, 0.0, 1.0);
426 else if (panel->side == PANEL_BOTTOM)
427 align = gtk_alignment_new(0.0, 1.0, 1.0, 0.0);
428 else if (panel->side == PANEL_TOP)
429 align = gtk_alignment_new(0.0, 0.0, 1.0, 0.0);
430 else
431 align = gtk_alignment_new(0.0, 0.0, 0.0, 1.0);
433 gtk_container_add(GTK_CONTAINER(panel->window), align);
435 vp = gtk_viewport_new(NULL, NULL);
436 gtk_container_set_resize_mode(GTK_CONTAINER(vp), GTK_RESIZE_PARENT);
437 gtk_viewport_set_shadow_type(GTK_VIEWPORT(vp), GTK_SHADOW_NONE);
438 gtk_container_add(GTK_CONTAINER(align), vp);
440 g_signal_connect(align, "expose-event",
441 G_CALLBACK(draw_panel_edge), panel);
443 if (side == PANEL_TOP || side == PANEL_BOTTOM)
445 panel->adj = gtk_viewport_get_hadjustment(GTK_VIEWPORT(vp));
446 box = gtk_hbox_new(FALSE, 0);
447 panel->before = gtk_hbox_new(FALSE, 0);
448 panel->after = gtk_hbox_new(FALSE, 0);
450 else
452 panel->adj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(vp));
453 box = gtk_vbox_new(FALSE, 0);
454 panel->before = gtk_vbox_new(FALSE, 0);
455 panel->after = gtk_vbox_new(FALSE, 0);
458 gtk_container_add(GTK_CONTAINER(vp), box);
459 gtk_box_pack_start(GTK_BOX(box), panel->before, FALSE, TRUE, 0);
460 gtk_box_pack_end(GTK_BOX(box), panel->after, FALSE, TRUE, 0);
462 frame = make_insert_frame(panel);
463 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
465 /* This is used so that we can find the middle easily! */
466 panel->gap = gtk_event_box_new();
467 gtk_box_pack_start(GTK_BOX(box), panel->gap, FALSE, FALSE, 0);
469 frame = make_insert_frame(panel);
470 g_object_set_data(G_OBJECT(frame), "after", "yes");
471 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
473 if (o_panel_is_dock.int_value)
474 gtk_window_set_type_hint(GTK_WINDOW(panel->window),
475 GDK_WINDOW_TYPE_HINT_DOCK);
477 gtk_widget_realize(panel->window);
478 make_panel_window(panel->window);
479 gtk_window_stick(GTK_WINDOW(panel->window));
481 gtk_widget_show_all(align);
483 loading_panel = panel;
484 if (panel_doc)
486 panel_load_from_xml(panel, panel_doc);
487 xmlFreeDoc(panel_doc);
489 if (need_resave)
490 panel_save(panel);
492 else if (load_path)
494 parse_file(load_path, pan_from_file);
495 info_message(_("Your old panel file has been "
496 "converted to the new XML format."));
497 panel_save(panel);
499 else
501 /* Don't scare users with an empty panel... */
502 guchar *apps;
504 panel_add_item(panel, "~", "Home", FALSE, NULL, NULL, FALSE);
506 apps = pathdup(make_path(app_dir, ".."));
507 if (apps)
509 panel_add_item(panel, apps, "Apps", FALSE, NULL, NULL, FALSE);
510 g_free(apps);
513 loading_panel = NULL;
514 g_free(load_path);
516 current_panel[side] = panel;
518 gtk_widget_queue_resize(box);
519 g_signal_connect(panel->window, "size-request",
520 G_CALLBACK(panel_post_resize), panel);
521 g_signal_connect(panel->window, "size-allocate",
522 G_CALLBACK(reposition_panel), panel);
524 number_of_windows++;
525 gdk_window_lower(panel->window->window);
526 gtk_widget_show(panel->window);
527 /* This has no effect until after window is showing; GTK+ bug? */
528 keep_below(panel->window->window, TRUE);
531 GdkWindow *pinboard;
533 pinboard = pinboard_get_window();
534 /* (if pinboard is NULL, will go right to the back) */
535 window_put_just_above(panel->window->window, pinboard);
538 return panel;
541 /* Externally visible function to add an item to a panel */
542 gboolean panel_add(PanelSide side,
543 const gchar *path, const gchar *label, gboolean after, const gchar *shortcut, const gchar *args,
544 gboolean locked)
546 g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, FALSE);
548 g_return_val_if_fail(current_panel[side] != NULL, FALSE);
550 panel_add_item(current_panel[side], path, label, after, shortcut, args, locked);
552 return TRUE;
555 /* Add the area covered by the panels to the region */
556 void panel_mark_used(GdkRegion *used)
558 int i;
560 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
562 Panel *panel = current_panel[i];
563 GdkRectangle rect;
565 if (!panel)
566 continue;
568 gdk_window_get_root_origin(panel->window->window,
569 &rect.x, &rect.y);
570 rect.width = panel->window->allocation.width;
571 rect.height = panel->window->allocation.height;
573 gdk_region_union_with_rect(used, &rect);
577 /* On xrandr screen size changes, update all panels */
578 void panel_update_size(void)
580 int i;
582 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
584 if (current_panel[i])
586 reposition_panel(current_panel[i]->window,
587 &current_panel[i]->window->allocation,
588 current_panel[i]);
589 gtk_widget_queue_resize(current_panel[i]->window);
594 /****************************************************************
595 * INTERNAL FUNCTIONS *
596 ****************************************************************/
598 /* User has tried to close the panel via the window manager - confirm */
599 static int panel_delete(GtkWidget *widget, GdkEvent *event, Panel *panel)
601 return !confirm(_("You have tried to close a panel via the window "
602 "manager - I usually find that this is accidental... "
603 "really close?"),
604 GTK_STOCK_CLOSE, NULL);
607 static void panel_destroyed(GtkWidget *widget, Panel *panel)
609 if (panel_options_dialog)
611 Panel *dlg_panel = g_object_get_data(G_OBJECT(panel_options_dialog),
612 "rox-panel");
614 if (dlg_panel == panel)
615 gtk_widget_destroy(panel_options_dialog);
618 if (current_panel[panel->side] == panel)
619 current_panel[panel->side] = NULL;
621 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
623 if (current_panel[PANEL_RIGHT])
624 gtk_widget_queue_resize(
625 current_panel[PANEL_RIGHT]->window);
626 if (current_panel[PANEL_LEFT])
627 gtk_widget_queue_resize(
628 current_panel[PANEL_LEFT]->window);
631 if (panel->autoscroll_speed)
632 g_source_remove(panel->autoscroll_to);
634 g_free(panel->name);
635 g_free(panel);
637 one_less_window();
640 static void panel_load_side(Panel *panel, xmlNodePtr side, gboolean after)
642 xmlNodePtr node;
643 char *label, *path, *shortcut, *args, *tmp;
644 gboolean locked;
646 for (node = side->xmlChildrenNode; node; node = node->next)
648 if (node->type != XML_ELEMENT_NODE)
649 continue;
650 if (strcmp(node->name, "icon") != 0)
651 continue;
653 label = xmlGetProp(node, "label");
654 if (!label)
655 label = g_strdup("<missing label>");
656 path = xmlNodeGetContent(node);
657 if (!path)
658 path = g_strdup("<missing path>");
659 shortcut = xmlGetProp(node, "shortcut");
660 args = xmlGetProp(node, "args");
661 tmp = xmlGetProp(node, "locked");
662 if (tmp)
664 locked = text_to_boolean(tmp, FALSE);
665 g_free(tmp);
667 else
668 locked = FALSE;
670 panel_add_item(panel, path, label, after, shortcut, args, locked);
672 g_free(path);
673 g_free(label);
674 g_free(shortcut);
675 g_free(args);
679 /* Create one panel icon for each icon in the doc */
680 static void panel_load_from_xml(Panel *panel, xmlDocPtr doc)
682 xmlNodePtr root;
684 root = xmlDocGetRootElement(doc);
685 panel_load_side(panel, get_subnode(root, NULL, "start"), FALSE);
686 panel_load_side(panel, get_subnode(root, NULL, "end"), TRUE);
689 /* Called for each line in the config file while loading a new panel */
690 static const char *pan_from_file(gchar *line)
692 gchar *sep, *leaf;
694 g_return_val_if_fail(line != NULL, NULL);
695 g_return_val_if_fail(loading_panel != NULL, NULL);
697 if (*line == '\0')
698 return NULL;
700 sep = strpbrk(line, "<>");
701 if (!sep)
702 return _("Missing < or > in panel config file");
704 if (sep != line)
705 leaf = g_strndup(line, sep - line);
706 else
707 leaf = NULL;
709 panel_add_item(loading_panel, sep + 1, leaf, sep[0] == '>',
710 NULL, NULL, FALSE);
712 g_free(leaf);
714 return NULL;
717 static gboolean icon_pointer_in(GtkWidget *widget,
718 GdkEventCrossing *event,
719 Icon *icon)
721 gtk_widget_set_state(widget,
722 icon->selected ? GTK_STATE_SELECTED : GTK_STATE_PRELIGHT);
724 return 0;
727 static gboolean icon_pointer_out(GtkWidget *widget,
728 GdkEventCrossing *event,
729 Icon *icon)
731 gtk_widget_set_state(widget,
732 icon->selected ? GTK_STATE_SELECTED : GTK_STATE_NORMAL);
734 return 0;
737 static void panel_icon_destroyed(PanelIcon *pi)
739 g_return_if_fail(pi->widget != NULL);
741 pi->widget = NULL;
743 g_object_unref(pi);
746 /* Set the tooltip AND hide/show the label */
747 static void panel_icon_set_tip(PanelIcon *pi)
749 XMLwrapper *ai;
750 xmlNode *node;
751 Icon *icon = (Icon *) pi;
753 g_return_if_fail(pi != NULL);
755 if (pi->label)
757 if (panel_want_show_text(pi))
758 gtk_widget_show(pi->label);
759 else
760 gtk_widget_hide(pi->label);
763 if (pi->socket)
764 ai = NULL;
765 else
766 ai = appinfo_get(icon->path, icon->item);
768 if (ai && ((node = xml_get_section(ai, NULL, "Summary"))))
770 guchar *str;
771 str = xmlNodeListGetString(node->doc,
772 node->xmlChildrenNode, 1);
773 if (str)
775 gtk_tooltips_set_tip(tooltips, pi->widget, str, NULL);
776 g_free(str);
779 else if ((!panel_want_show_text(pi)) && !pi->socket)
781 if (icon->item->leafname && icon->item->leafname[0])
782 gtk_tooltips_set_tip(tooltips, pi->widget,
783 icon->item->leafname, NULL);
785 else
786 gtk_tooltips_set_tip(tooltips, pi->widget, NULL, NULL);
788 if (ai)
789 g_object_unref(ai);
792 /* Add an icon with this path to the panel. If after is TRUE then the
793 * icon is added to the right/bottom end of the panel.
795 * If name is NULL a suitable name is taken from path.
797 static void panel_add_item(Panel *panel,
798 const gchar *path,
799 const gchar *name,
800 gboolean after,
801 const gchar *shortcut,
802 const gchar *args,
803 gboolean locked)
805 GtkWidget *widget;
806 PanelIcon *pi;
807 Icon *icon;
809 g_return_if_fail(panel != NULL);
810 g_return_if_fail(path != NULL);
812 widget = gtk_event_box_new();
813 gtk_widget_set_events(widget,
814 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
815 GDK_BUTTON3_MOTION_MASK |
816 GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
817 GDK_BUTTON_RELEASE_MASK);
819 gtk_box_pack_start(GTK_BOX(after ? panel->after : panel->before),
820 widget, FALSE, TRUE, 0);
821 if (after)
822 gtk_box_reorder_child(GTK_BOX(panel->after), widget, 0);
824 gtk_widget_realize(widget);
826 pi = panel_icon_new(panel, path, name);
827 icon = (Icon *) pi;
829 /* Widget takes the initial ref of Icon */
830 g_object_set_data(G_OBJECT(widget), "icon", pi);
832 pi->widget = widget;
833 g_object_ref(widget);
835 gtk_widget_set_name(pi->widget, "panel-icon");
837 g_signal_connect_swapped(widget, "destroy",
838 G_CALLBACK(panel_icon_destroyed), pi);
840 if (icon->item->base_type == TYPE_DIRECTORY)
841 run_applet(pi);
843 g_signal_connect(widget, "button_release_event",
844 G_CALLBACK(icon_button_release), pi);
845 g_signal_connect(widget, "button_press_event",
846 G_CALLBACK(icon_button_press), pi);
847 g_signal_connect(widget, "motion-notify-event",
848 G_CALLBACK(icon_motion_event), pi);
849 g_signal_connect(widget, "enter-notify-event",
850 G_CALLBACK(icon_pointer_in), pi);
851 g_signal_connect(widget, "leave-notify-event",
852 G_CALLBACK(icon_pointer_out), pi);
854 if (!pi->socket)
856 g_signal_connect(widget, "enter-notify-event",
857 G_CALLBACK(enter_icon), pi);
858 g_signal_connect_after(widget, "expose_event",
859 G_CALLBACK(expose_icon), pi);
860 g_signal_connect(widget, "drag_data_get",
861 G_CALLBACK(drag_data_get), NULL);
863 g_signal_connect(widget, "size_request",
864 G_CALLBACK(size_request), pi);
866 drag_set_panel_dest(pi);
868 pi->label = gtk_label_new(icon->item->leafname);
869 gtk_container_add(GTK_CONTAINER(pi->widget), pi->label);
870 gtk_misc_set_alignment(GTK_MISC(pi->label), 0.5, 1);
871 gtk_misc_set_padding(GTK_MISC(pi->label), 1, 2);
874 icon_set_shortcut(icon, shortcut);
875 icon_set_arguments(icon, args);
876 icon->locked = locked;
878 if (!loading_panel)
879 panel_save(panel);
881 panel_icon_set_tip(pi);
882 gtk_widget_show(widget);
885 static gboolean remove_item_from_side(GtkWidget *container, const gchar *path,
886 const gchar *label)
888 GList *kids, *next;
889 gboolean found = FALSE;
891 kids = gtk_container_get_children(GTK_CONTAINER(container));
893 for (next = kids; next; next = next->next)
895 Icon *icon;
896 icon = g_object_get_data(G_OBJECT(next->data), "icon");
897 if (!icon)
898 continue;
900 if ((!path || strcmp(path, icon->src_path) == 0) &&
901 (!label || strcmp(label, icon->item->leafname)==0))
903 icon->locked = FALSE;
904 icon_destroy(icon);
905 found = TRUE;
906 break;
910 g_list_free(kids);
912 return found;
915 /* Remove an item with this path. If more than one item matches, only
916 * one is removed. If label is not NULL then it must also match the item.
917 * Returns TRUE if an item was successfully removed.
919 gboolean panel_remove_item(PanelSide side, const gchar *path,
920 const gchar *label)
922 Panel *panel;
924 g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, FALSE);
926 g_return_val_if_fail(path != NULL || label != NULL, FALSE);
928 panel = current_panel[side];
929 if (!panel)
931 g_warning("No panel on this side of the screen!");
932 return FALSE;
935 if (remove_item_from_side(panel->before, path, label) ||
936 remove_item_from_side(panel->after, path, label))
938 panel_save(panel);
939 panel_update(panel);
940 return TRUE;
943 g_warning("Panel item path='%s', label='%s' not found", path, label);
944 return FALSE;
947 /* Called when Gtk+ wants to know how much space an icon needs.
948 * 'req' is already big enough for the label, if shown.
950 static void size_request(GtkWidget *widget, GtkRequisition *req, PanelIcon *pi)
952 Icon *icon = (Icon *) pi;
953 gboolean horz = (pi->panel->side == PANEL_TOP ||
954 pi->panel->side == PANEL_BOTTOM);
955 int max_width = 100;
956 int max_height = 100;
957 int image_width, image_height;
958 Panel *panel = pi->panel;
960 if (horz)
961 max_height = panel->width - req->height;
962 else
963 max_width = MAX(panel->width, req->width);
965 /* TODO: really need to recreate? */
966 if (pi->image)
967 g_object_unref(pi->image);
969 pi->image = scale_pixbuf(di_image(icon->item)->src_pixbuf,
970 MAX(20, max_width), MAX(20, max_height));
972 image_width = gdk_pixbuf_get_width(pi->image);
973 image_height = gdk_pixbuf_get_height(pi->image);
975 if (req->height > 0 && max_height < req->height)
977 pi->style = TEXT_BESIDE_ICON;
978 req->width += image_width;
979 req->height = MAX(req->height, image_height);
980 gtk_misc_set_alignment(GTK_MISC(pi->label), 1, 0.5);
982 else
984 pi->style = TEXT_UNDER_ICON;
985 req->width = MAX(req->width, image_width);
986 req->height += image_height;
987 gtk_misc_set_alignment(GTK_MISC(pi->label), 0.5, 1);
990 if (horz)
991 req->width += PANEL_ICON_SPACING;
992 else
993 req->height += PANEL_ICON_SPACING;
996 static gint expose_icon(GtkWidget *widget,
997 GdkEventExpose *event,
998 PanelIcon *pi)
1000 return draw_icon(widget, &event->area, pi);
1003 static gint draw_icon(GtkWidget *widget, GdkRectangle *badarea, PanelIcon *pi)
1005 GdkRectangle area;
1006 int width, height;
1007 Icon *icon = (Icon *) pi;
1008 int image_x;
1009 int image_y;
1010 GdkPixbuf *image;
1011 int text_height = 0;
1013 gdk_drawable_get_size(widget->window, &area.width, &area.height);
1015 if (panel_want_show_text(pi))
1016 text_height = pi->label->requisition.height;
1018 g_return_val_if_fail(pi->image != NULL, FALSE);
1020 image = pi->image;
1022 width = gdk_pixbuf_get_width(image);
1023 height = gdk_pixbuf_get_height(image);
1025 if (pi->style == TEXT_UNDER_ICON)
1027 image_x = (area.width - width) >> 1;
1028 image_y = (area.height - height - text_height) >> 1;
1030 else
1032 image_x = PANEL_ICON_SPACING - 2;
1033 image_y = (area.height - height) >> 1;
1036 gdk_pixbuf_render_to_drawable_alpha(
1037 image,
1038 widget->window,
1039 0, 0, /* src */
1040 image_x, image_y, /* dest */
1041 width, height,
1042 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
1043 GDK_RGB_DITHER_NORMAL, 0, 0);
1045 if (icon->item->flags & ITEM_FLAG_SYMLINK)
1047 gdk_pixbuf_render_to_drawable_alpha(im_symlink->pixbuf,
1048 widget->window,
1049 0, 0, /* src */
1050 image_x, image_y + 2, /* dest */
1051 -1, -1,
1052 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
1053 GDK_RGB_DITHER_NORMAL, 0, 0);
1055 if (icon->item->flags & ITEM_FLAG_MOUNT_POINT)
1057 MaskedPixmap *mp = icon->item->flags & ITEM_FLAG_MOUNTED
1058 ? im_mounted
1059 : im_unmounted;
1061 gdk_pixbuf_render_to_drawable_alpha(mp->pixbuf,
1062 widget->window,
1063 0, 0, /* src */
1064 image_x, image_y + 2, /* dest */
1065 -1, -1,
1066 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
1067 GDK_RGB_DITHER_NORMAL, 0, 0);
1069 return FALSE;
1072 static void panel_icon_wink(Icon *icon)
1074 PanelIcon *pi = (PanelIcon *) icon;
1076 wink_widget(pi->widget);
1079 /* icon may be NULL if the event is on the background */
1080 static void perform_action(Panel *panel, PanelIcon *pi, GdkEventButton *event)
1082 BindAction action;
1083 Icon *icon = (Icon *) pi;
1085 action = bind_lookup_bev(icon ? BIND_PANEL_ICON : BIND_PANEL, event);
1087 if (pi && pi->socket)
1088 if (action != ACT_POPUP_MENU && action != ACT_MOVE_ICON)
1089 return;
1091 switch (action)
1093 case ACT_OPEN_ITEM:
1094 dnd_motion_ungrab();
1095 wink_widget(pi->widget);
1096 icon_run(icon);
1097 break;
1098 case ACT_EDIT_ITEM:
1099 dnd_motion_ungrab();
1100 wink_widget(pi->widget);
1101 run_diritem(icon->path, icon->item, NULL, NULL, TRUE);
1102 break;
1103 case ACT_POPUP_MENU:
1104 dnd_motion_ungrab();
1105 panel_show_menu(event, pi, panel);
1106 break;
1107 case ACT_MOVE_ICON:
1108 dnd_motion_start(MOTION_REPOSITION);
1109 break;
1110 case ACT_PRIME_AND_SELECT:
1111 if (!icon->selected)
1112 icon_select_only(icon);
1113 dnd_motion_start(MOTION_READY_FOR_DND);
1114 break;
1115 case ACT_PRIME_AND_TOGGLE:
1116 icon_set_selected(icon, !icon->selected);
1117 dnd_motion_start(MOTION_READY_FOR_DND);
1118 break;
1119 case ACT_PRIME_FOR_DND:
1120 dnd_motion_start(MOTION_READY_FOR_DND);
1121 break;
1122 case ACT_TOGGLE_SELECTED:
1123 icon_set_selected(icon, !icon->selected);
1124 break;
1125 case ACT_SELECT_EXCL:
1126 icon_set_selected(icon, TRUE);
1127 break;
1128 case ACT_IGNORE:
1129 break;
1130 case ACT_CLEAR_SELECTION:
1131 dnd_motion_ungrab();
1132 icon_select_only(NULL);
1133 break;
1134 default:
1135 g_warning("Unsupported action : %d\n", action);
1136 break;
1140 static gint panel_button_release(GtkWidget *widget,
1141 GdkEventButton *event,
1142 Panel *panel)
1144 if (dnd_motion_release(event))
1145 return TRUE;
1147 perform_action(panel, NULL, event);
1149 return TRUE;
1152 static gint panel_button_press(GtkWidget *widget,
1153 GdkEventButton *event,
1154 Panel *panel)
1156 if (dnd_motion_press(panel->window, event))
1157 perform_action(panel, NULL, event);
1159 return TRUE;
1162 static gint icon_button_release(GtkWidget *widget,
1163 GdkEventButton *event,
1164 PanelIcon *pi)
1166 if (pi->socket && event->button == 1)
1167 return FALSE; /* Restart button */
1169 if (dnd_motion_release(event))
1170 return TRUE;
1172 perform_action(pi->panel, pi, event);
1174 return TRUE;
1177 static gint icon_button_press(GtkWidget *widget,
1178 GdkEventButton *event,
1179 PanelIcon *pi)
1181 if (pi->socket && event->button == 1)
1182 return FALSE; /* Restart button */
1184 if (dnd_motion_press(widget, event))
1185 perform_action(pi->panel, pi, event);
1187 return TRUE;
1190 /* Stop windows from maximising over all/part of us */
1191 static void panel_setup_struts(Panel *panel, GtkAllocation *alloc)
1193 int thickness;
1194 struct {
1195 gulong left, right, top, bottom;
1196 gulong left_start_y, left_end_y;
1197 gulong right_start_y, right_end_y;
1198 gulong top_start_x, top_end_x;
1199 gulong bottom_start_x, bottom_end_x;
1200 } strut = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1202 if (panel->avoid == FALSE)
1203 thickness = 2;
1204 else if (panel->side == PANEL_TOP ||
1205 panel->side == PANEL_BOTTOM)
1206 thickness = alloc->height;
1207 else
1208 thickness = alloc->width;
1210 switch (panel->side)
1212 case PANEL_LEFT:
1213 if (!panel->xinerama || !monitor_adjacent[panel->monitor].left)
1215 strut.left = panel->geometry.x + thickness;
1216 strut.left_start_y = panel->geometry.y;
1217 strut.left_end_y = panel->geometry.y +
1218 panel->geometry.height - 1;
1220 /* else there is (part of) a monitor
1221 * to the left */
1222 else
1224 thickness = 0;
1226 break;
1227 case PANEL_RIGHT:
1228 if (!panel->xinerama || !monitor_adjacent[panel->monitor].right)
1230 /* RHS of monitor might not abut edge
1231 * of total virtual screen */
1232 strut.right = screen_width -
1233 panel->geometry.x -
1234 panel->geometry.width +
1235 thickness;
1236 strut.right_start_y = panel->geometry.y;
1237 strut.right_end_y = panel->geometry.y +
1238 panel->geometry.height - 1;
1240 /* else there is (part of) a monitor
1241 * to the right */
1242 else
1244 thickness = 0;
1246 break;
1247 case PANEL_TOP:
1248 if (!panel->xinerama || !monitor_adjacent[panel->monitor].top)
1250 strut.top = panel->geometry.y + thickness;
1251 strut.top_start_x = panel->geometry.x;
1252 strut.top_end_x = panel->geometry.x +
1253 panel->geometry.width - 1;
1255 /* else there is (part of) a monitor above */
1256 else
1258 thickness = 0;
1260 break;
1261 default: /* PANEL_BOTTOM */
1262 if (!panel->xinerama ||
1263 !monitor_adjacent[panel->monitor].bottom)
1265 /* Bottom of monitor might not abut
1266 * edge of total virtual screen */
1267 strut.bottom = screen_height -
1268 panel->geometry.y -
1269 panel->geometry.height +
1270 thickness;
1271 strut.bottom_start_x = panel->geometry.x;
1272 strut.bottom_end_x = panel->geometry.x +
1273 panel->geometry.width - 1;
1275 /* else there is (part of) a monitor below */
1276 else
1278 thickness = 0;
1280 break;
1283 if (thickness)
1285 /* Set full-width strut as well as partial in case
1286 * partial isn't supported by wm */
1287 gdk_property_change(panel->window->window,
1288 gdk_atom_intern("_NET_WM_STRUT",
1289 FALSE),
1290 gdk_atom_intern("CARDINAL", FALSE),
1291 32, GDK_PROP_MODE_REPLACE,
1292 (gchar *) &strut, 4);
1293 gdk_property_change(panel->window->window,
1294 gdk_atom_intern("_NET_WM_STRUT_PARTIAL",
1295 FALSE),
1296 gdk_atom_intern("CARDINAL", FALSE),
1297 32, GDK_PROP_MODE_REPLACE,
1298 (gchar *) &strut, 12);
1300 else
1302 gdk_property_delete(panel->window->window,
1303 gdk_atom_intern("_NET_WM_STRUT_PARTIAL",
1304 FALSE));
1305 gdk_property_delete(panel->window->window,
1306 gdk_atom_intern("_NET_WM_STRUT",
1307 FALSE));
1311 static void reposition_panel(GtkWidget *window,
1312 GtkAllocation *alloc, Panel *panel)
1314 int x = panel->geometry.x;
1315 int y = panel->geometry.y;
1316 PanelSide side = panel->side;
1318 if (side == PANEL_LEFT || side == PANEL_RIGHT)
1320 if (side == PANEL_RIGHT)
1321 x += panel->geometry.width - alloc->width;
1323 if (current_panel[PANEL_TOP])
1325 GtkWidget *win = current_panel[PANEL_TOP]->window;
1326 y += win->allocation.height;
1330 if (side == PANEL_BOTTOM)
1331 y += panel->geometry.height - alloc->height;
1333 gtk_window_move(GTK_WINDOW(panel->window), x, y);
1334 gdk_window_move(panel->window->window, x, y);
1336 if (side == PANEL_BOTTOM || side == PANEL_TOP)
1338 if (current_panel[PANEL_RIGHT])
1339 gtk_widget_queue_resize(
1340 current_panel[PANEL_RIGHT]->window);
1341 if (current_panel[PANEL_LEFT])
1342 gtk_widget_queue_resize(
1343 current_panel[PANEL_LEFT]->window);
1346 panel_setup_struts(panel, alloc);
1349 /* Same as drag_set_dest(), but for panel icons */
1350 static void drag_set_panel_dest(PanelIcon *pi)
1352 GtkWidget *obj = pi->widget;
1354 make_drop_target(pi->widget, 0);
1356 g_signal_connect(obj, "drag_motion", G_CALLBACK(drag_motion), pi);
1357 g_signal_connect(obj, "drag_leave", G_CALLBACK(drag_leave), pi);
1358 g_signal_connect(obj, "drag_end", G_CALLBACK(drag_end), pi);
1361 static gboolean drag_motion(GtkWidget *widget,
1362 GdkDragContext *context,
1363 gint x,
1364 gint y,
1365 guint time,
1366 PanelIcon *pi)
1368 GdkDragAction action = context->suggested_action;
1369 const char *type = NULL;
1370 Icon *icon = (Icon *) pi;
1371 DirItem *item = icon->item;
1372 int panel_x, panel_y;
1374 gdk_window_get_pointer(pi->panel->window->window,
1375 &panel_x, &panel_y, NULL);
1376 motion_may_raise(pi->panel, panel_x, panel_y);
1378 /* Should we scroll the panel when dragging? */
1379 if (motion_state != MOTION_REPOSITION)
1380 if (pi->panel->autoscroll_speed == 0)
1381 may_autoscroll(pi->panel);
1383 if (icon->selected)
1384 goto out; /* Can't drag a selection to itself */
1386 type = dnd_motion_item(context, &item);
1388 if ((context->actions & GDK_ACTION_ASK) && o_dnd_left_menu.int_value
1389 && type != drop_dest_prog)
1391 guint state;
1392 gdk_window_get_pointer(NULL, NULL, NULL, &state);
1393 if (state & GDK_BUTTON1_MASK)
1394 action = GDK_ACTION_ASK;
1397 if (!item)
1398 type = NULL;
1399 out:
1400 /* We actually must pretend to accept the drop, even if the
1401 * directory isn't writeable, so that the spring-opening
1402 * thing works.
1405 /* Don't allow drops to non-writeable directories */
1406 if (o_dnd_spring_open.int_value == FALSE &&
1407 type == drop_dest_dir &&
1408 access(icon->path, W_OK) != 0)
1410 type = NULL;
1413 g_dataset_set_data(context, "drop_dest_type", (gpointer) type);
1414 if (type)
1416 gdk_drag_status(context, action, time);
1417 g_dataset_set_data_full(context, "drop_dest_path",
1418 g_strdup(icon->path), g_free);
1419 if (type == drop_dest_dir)
1420 dnd_spring_load(context, NULL);
1422 if (dnd_highlight && dnd_highlight != pi->widget)
1424 gtk_drag_unhighlight(dnd_highlight);
1425 dnd_highlight = NULL;
1428 if (dnd_highlight == NULL)
1430 gtk_drag_highlight(pi->widget);
1431 dnd_highlight = pi->widget;
1435 return type != NULL;
1439 static void add_uri_list(GtkWidget *widget,
1440 GdkDragContext *context,
1441 gint x,
1442 gint y,
1443 GtkSelectionData *selection_data,
1444 guint info,
1445 guint32 time,
1446 Panel *panel)
1448 gboolean after = FALSE;
1449 GList *uris, *next;
1451 if (!selection_data->data)
1452 return;
1454 g_return_if_fail(selection_data->data[selection_data->length] == '\0');
1456 if (g_object_get_data(G_OBJECT(widget), "after"))
1457 after = TRUE;
1459 uris = uri_list_to_glist(selection_data->data);
1461 for (next = uris; next; next = next->next)
1463 guchar *path;
1465 path = get_local_path((EscapedPath *) next->data);
1467 if (path) {
1468 panel_add_item(panel, path, NULL, after, NULL, NULL, FALSE);
1469 g_free(path);
1473 g_list_free(uris);
1476 static void drag_end(GtkWidget *widget,
1477 GdkDragContext *context,
1478 Icon *icon)
1480 if (tmp_icon_selected)
1482 icon_select_only(NULL);
1483 tmp_icon_selected = FALSE;
1487 static void drag_leave(GtkWidget *widget,
1488 GdkDragContext *context,
1489 guint32 time,
1490 Icon *icon)
1492 panel_drag_leave(widget, context, time, ((PanelIcon *) icon)->panel);
1494 if (dnd_highlight && dnd_highlight == widget)
1496 gtk_drag_unhighlight(dnd_highlight);
1497 dnd_highlight = NULL;
1500 dnd_spring_abort();
1503 /* Create XML icon nodes for these widgets.
1504 * Always frees the widgets list.
1506 static void make_widgets(xmlNodePtr side, GList *widgets)
1508 GList *next;
1510 for (next = widgets; next; next = next->next)
1512 Icon *icon;
1513 xmlNodePtr tree;
1515 icon = g_object_get_data(G_OBJECT(next->data), "icon");
1517 if (!icon)
1519 g_warning("Can't find Icon from widget\n");
1520 continue;
1523 tree = xmlNewTextChild(side, NULL, "icon", icon->src_path);
1525 xmlSetProp(tree, "label", icon->item->leafname);
1526 if (icon->shortcut)
1527 xmlSetProp(tree, "shortcut", icon->shortcut);
1528 if (icon->args)
1529 xmlSetProp(tree, "args", icon->args);
1530 if (icon->locked)
1531 xmlSetProp(tree, "locked", "true");
1534 if (widgets)
1535 g_list_free(widgets);
1538 void panel_save(Panel *panel)
1540 xmlDocPtr doc;
1541 xmlNodePtr root;
1542 xmlNodePtr options;
1543 guchar *save = NULL;
1544 guchar *save_new = NULL;
1546 g_return_if_fail(panel != NULL);
1548 if (strchr(panel->name, '/'))
1549 save = g_strdup(panel->name);
1550 else
1552 guchar *leaf;
1554 leaf = g_strconcat("pan_", panel->name, NULL);
1555 save = choices_find_xdg_path_save(leaf, PROJECT, SITE, TRUE);
1556 g_free(leaf);
1559 if (!save)
1560 return;
1562 doc = xmlNewDoc("1.0");
1563 xmlDocSetRootElement(doc, xmlNewDocNode(doc, NULL, "panel", NULL));
1565 root = xmlDocGetRootElement(doc);
1567 xmlSetProp(root, "side", panel_side_to_name(panel->side));
1569 options = xmlNewChild(root, NULL, "options", NULL);
1570 set_int_prop(options, "style", panel->style);
1571 set_int_prop(options, "width", panel->width);
1572 set_int_prop(options, "avoid", panel->avoid);
1573 set_int_prop(options, "xinerama", panel->xinerama);
1574 set_int_prop(options, "monitor", panel->monitor);
1576 make_widgets(xmlNewChild(root, NULL, "start", NULL),
1577 gtk_container_get_children(GTK_CONTAINER(panel->before)));
1579 make_widgets(xmlNewChild(root, NULL, "end", NULL),
1580 g_list_reverse(gtk_container_get_children(
1581 GTK_CONTAINER(panel->after))));
1583 save_new = g_strconcat(save, ".new", NULL);
1584 if (save_xml_file(doc, save_new) || rename(save_new, save))
1585 delayed_error(_("Error saving panel %s: %s"),
1586 save, g_strerror(errno));
1587 g_free(save_new);
1589 g_free(save);
1590 if (doc)
1591 xmlFreeDoc(doc);
1594 /* Create a frame widget which can be used to add icons to the panel */
1595 static GtkWidget *make_insert_frame(Panel *panel)
1597 GtkWidget *frame;
1598 GtkTargetEntry target_table[] = {
1599 {"text/uri-list", 0, TARGET_URI_LIST},
1602 frame = gtk_frame_new(NULL);
1603 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
1604 gtk_widget_set_size_request(frame, 16, 16);
1606 g_signal_connect(frame, "drag-motion",
1607 G_CALLBACK(insert_drag_motion), panel);
1608 g_signal_connect(frame, "drag-leave",
1609 G_CALLBACK(panel_drag_leave), panel);
1611 g_signal_connect(frame, "drag-data-received",
1612 G_CALLBACK(add_uri_list), panel);
1613 gtk_drag_dest_set(frame,
1614 GTK_DEST_DEFAULT_ALL,
1615 target_table,
1616 sizeof(target_table) / sizeof(*target_table),
1617 GDK_ACTION_COPY);
1619 return frame;
1622 static gboolean enter_icon(GtkWidget *widget,
1623 GdkEventCrossing *event,
1624 Icon *icon)
1626 icon_may_update(icon);
1627 panel_icon_set_tip((PanelIcon *) icon);
1629 return FALSE;
1632 static gint panel_leave_event(GtkWidget *widget,
1633 GdkEventCrossing *event,
1634 Panel *panel)
1636 GdkWindow *pinboard;
1638 if (event->mode != GDK_CROSSING_NORMAL)
1639 return FALSE; /* Grab for menu, DnD, etc */
1641 keep_below(panel->window->window, TRUE);
1643 /* Shouldn't need this as well as keep_below but some WMs don't
1644 * automatically lower as soon as the hint is set */
1645 pinboard = pinboard_get_window();
1646 window_put_just_above(panel->window->window, pinboard);
1648 return FALSE;
1651 /* If (x, y) is at the edge of the panel then raise */
1652 static void motion_may_raise(Panel *panel, int x, int y)
1654 gboolean raise;
1656 if (panel->side == PANEL_TOP)
1657 raise = y == 0;
1658 else if (panel->side == PANEL_BOTTOM)
1659 raise = y == panel->window->allocation.height - 1;
1660 else if (panel->side == PANEL_LEFT)
1661 raise = x == 0;
1662 else
1663 raise = x == panel->window->allocation.width - 1;
1665 if (raise)
1667 keep_below(panel->window->window, FALSE);
1669 /* Shouldn't need this as well as keep_below but some WMs don't
1670 * automatically raise as soon as the hint is set */
1671 gdk_window_raise(panel->window->window);
1675 static gboolean may_autoscroll(Panel *panel)
1677 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1678 gint max, panel_x, panel_y, delta, new;
1680 if (panel->adj->upper <= panel->adj->page_size)
1681 goto stop_scrolling; /* Can see everything already */
1683 gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
1685 if (horz)
1687 delta = panel_x;
1688 max = panel->window->allocation.width;
1689 if (panel_y < 0 || panel_y > panel->window->allocation.height)
1690 goto stop_scrolling; /* Not over the panel */
1692 else
1694 delta = panel_y;
1695 max = panel->window->allocation.height;
1696 if (panel_x < 0 || panel_x > panel->window->allocation.width)
1697 goto stop_scrolling; /* Not over the panel */
1700 if (delta >= 20 && delta <= max - 20)
1701 goto stop_scrolling; /* Not at either end */
1703 panel->autoscroll_speed = MIN(panel->autoscroll_speed + 2, 200);
1705 new = panel->adj->value - ((delta < 20) ? panel->autoscroll_speed
1706 : -panel->autoscroll_speed);
1707 new = CLAMP(new, 0, panel->adj->upper - panel->adj->page_size);
1708 gtk_adjustment_set_value(panel->adj, new);
1710 panel->autoscroll_to = g_timeout_add(40,
1711 (GSourceFunc) may_autoscroll, panel);
1713 return FALSE;
1715 stop_scrolling:
1716 panel->autoscroll_speed = 0;
1717 return FALSE;
1720 static gint panel_motion_event(GtkWidget *widget,
1721 GdkEventMotion *event,
1722 Panel *panel)
1724 motion_may_raise(panel, event->x, event->y);
1726 if (motion_state != MOTION_REPOSITION)
1727 if (panel->autoscroll_speed == 0)
1728 may_autoscroll(panel);
1730 return FALSE;
1733 static gint icon_motion_event(GtkWidget *widget,
1734 GdkEventMotion *event,
1735 PanelIcon *pi)
1737 Panel *panel = pi->panel;
1738 GList *list, *me;
1739 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1740 int val;
1741 int dir = 0;
1743 if (motion_state == MOTION_READY_FOR_DND)
1745 if (dnd_motion_moved(event))
1746 start_drag(pi, event);
1747 return TRUE;
1749 else if (motion_state != MOTION_REPOSITION)
1750 return FALSE;
1752 list = gtk_container_get_children(GTK_CONTAINER(panel->before));
1753 list = g_list_append(list, NULL); /* The gap in the middle */
1754 list = g_list_concat(list,
1755 gtk_container_get_children(GTK_CONTAINER(panel->after)));
1756 me = g_list_find(list, widget);
1758 g_return_val_if_fail(me != NULL, TRUE);
1760 val = horz ? event->x_root : event->y_root;
1762 if (me->prev)
1764 GtkWidget *prev;
1765 int x, y;
1767 if (me->prev->data)
1768 prev = GTK_WIDGET(me->prev->data);
1769 else
1770 prev = panel->gap;
1772 gdk_window_get_origin(prev->window, &x, &y);
1774 if (val <= (horz ? x : y))
1775 dir = -1;
1778 if (dir == 0 && me->next)
1780 GtkWidget *next;
1781 int x, y, w, h;
1783 if (me->next->data)
1784 next = GTK_WIDGET(me->next->data);
1785 else
1786 next = panel->gap;
1788 gdk_window_get_origin(next->window, &x, &y);
1790 gdk_drawable_get_size(next->window, &w, &h);
1792 x += w;
1793 y += h;
1795 if (val >= (horz ? x : y)-1)
1797 if (next == panel->gap)
1798 dir = +2;
1799 else
1800 dir = +1;
1804 if (dir)
1805 reposition_icon(pi, g_list_index(list, widget) + dir);
1807 return TRUE;
1810 static void reposition_icon_on_side(GtkWidget *side, GtkWidget *widget,
1811 int index)
1813 GList *list;
1815 list = gtk_container_get_children(GTK_CONTAINER(side));
1817 /* Want to move icon to the list in the given 'side'. Is it there
1818 * already?
1821 if (!g_list_find(list, widget))
1823 /* No, reparent */
1824 gtk_grab_remove(widget);
1825 gtk_widget_reparent(widget, side);
1826 dnd_motion_grab_pointer();
1827 gtk_grab_add(widget);
1830 gtk_box_reorder_child(GTK_BOX(side), widget, index);
1832 g_list_free(list);
1835 /* Move icon to this index in the complete widget list.
1836 * 0 makes the icon the left-most icon. The gap in the middle has
1837 * an index number, which allows you to specify that the icon should
1838 * go on the left or right side.
1840 static void reposition_icon(PanelIcon *pi, int index)
1842 Panel *panel = pi->panel;
1843 GtkWidget *widget = pi->widget;
1844 GList *list;
1845 int before_len;
1847 list = gtk_container_get_children(GTK_CONTAINER(panel->before));
1848 before_len = g_list_length(list);
1849 g_list_free(list);
1851 if (index <= before_len)
1852 reposition_icon_on_side(panel->before, widget, index);
1853 else
1854 reposition_icon_on_side(panel->after, widget,
1855 index - (before_len + 1));
1857 panel_save(panel);
1860 static void start_drag(PanelIcon *pi, GdkEventMotion *event)
1862 GtkWidget *widget = pi->widget;
1863 Icon *icon = (Icon *) pi;
1865 if (!icon->selected)
1867 if (event->state & GDK_BUTTON1_MASK)
1869 /* Select just this one */
1870 icon_select_only(icon);
1871 tmp_icon_selected = TRUE;
1873 else
1874 icon_set_selected(icon, TRUE);
1877 g_return_if_fail(icon_selection != NULL);
1879 if (icon_selection->next == NULL)
1880 drag_one_item(widget, event, icon->path, icon->item, NULL);
1881 else
1883 guchar *uri_list;
1885 uri_list = icon_create_uri_list();
1886 drag_selection(widget, event, uri_list);
1887 g_free(uri_list);
1891 static void applet_died(GtkWidget *socket)
1893 gboolean never_plugged;
1895 never_plugged = (!g_object_get_data(G_OBJECT(socket), "lost_plug"))
1896 && !GTK_SOCKET(socket)->plug_window;
1898 if (never_plugged)
1900 report_error(
1901 _("Applet quit without ever creating a widget!"));
1902 gtk_widget_destroy(socket);
1905 gtk_widget_unref(socket);
1908 static void socket_destroyed(GtkWidget *socket, GtkWidget *widget)
1910 g_object_set_data(G_OBJECT(socket), "lost_plug", "yes");
1912 gtk_widget_unref(socket);
1914 gtk_widget_destroy(widget); /* Remove from panel */
1916 if (!closing_panel)
1917 panel_save(g_object_get_data(G_OBJECT(socket), "panel"));
1920 /* Try to run this applet.
1921 * Cases:
1923 * - No executable AppletRun:
1924 * icon->socket == NULL (unchanged) on return.
1926 * Otherwise, create socket (setting icon->socket) and ref it twice.
1928 * - AppletRun quits without connecting a plug:
1929 * On child death lost_plug is unset and socket is empty.
1930 * Unref socket.
1931 * Report error and destroy widget (to 'socket destroyed').
1933 * - AppletRun quits while plug is in socket:
1934 * Unref socket once. Socket will be destroyed later.
1936 * - Socket is destroyed.
1937 * Set lost_plug = "yes" and remove widget from panel.
1938 * Unref socket.
1940 static void run_applet(PanelIcon *pi)
1942 GError *error = NULL;
1943 char *argv[3];
1944 gint pid;
1945 Icon *icon = (Icon *) pi;
1947 argv[0] = (char *) make_path(icon->path, "AppletRun");
1949 if (access(argv[0], X_OK) != 0)
1950 return;
1952 pi->socket = gtk_socket_new();
1954 gtk_container_add(GTK_CONTAINER(pi->widget), pi->socket);
1955 gtk_widget_show_all(pi->socket);
1956 gtk_widget_realize(pi->socket);
1958 /* Always get button-2 events so we can drag */
1959 XGrabButton(gdk_display, Button2, AnyModifier,
1960 GDK_WINDOW_XWINDOW(pi->socket->window),
1961 False,
1962 ButtonPressMask | ButtonReleaseMask | Button2MotionMask,
1963 GrabModeAsync, /* Pointer */
1964 GrabModeAsync, /* Keyboard */
1965 None, None);
1968 gchar *pos;
1969 PanelSide side = pi->panel->side;
1971 /* Set a hint to let applets position their menus correctly */
1972 pos = g_strdup_printf("%s,%d",
1973 panel_side_to_name(side), MENU_MARGIN(side));
1974 gdk_property_change(pi->socket->window,
1975 gdk_atom_intern("_ROX_PANEL_MENU_POS", FALSE),
1976 gdk_atom_intern("STRING", FALSE),
1977 8, GDK_PROP_MODE_REPLACE,
1978 pos, strlen(pos));
1979 g_free(pos);
1981 /* Ensure that the properties are set before starting the
1982 * applet.
1984 gdk_flush();
1987 g_object_set_data(G_OBJECT(pi->widget), "icon", pi);
1988 g_object_set_data(G_OBJECT(pi->socket), "panel", pi->panel);
1990 argv[1] = g_strdup_printf("%ld",
1991 GDK_WINDOW_XWINDOW(pi->socket->window));
1992 argv[2] = NULL;
1994 if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
1995 NULL, NULL, &pid, &error))
1997 delayed_error(_("Error running applet:\n%s"), error->message);
1998 g_error_free(error);
1999 gtk_widget_destroy(pi->socket);
2000 pi->socket = NULL;
2002 else
2004 gtk_widget_ref(pi->socket);
2005 on_child_death(pid, (CallbackFn) applet_died, pi->socket);
2007 gtk_widget_ref(pi->socket);
2008 g_signal_connect(pi->socket, "destroy",
2009 G_CALLBACK(socket_destroyed), pi->widget);
2012 g_free(argv[1]);
2015 static void panel_post_resize(GtkWidget *win, GtkRequisition *req, Panel *panel)
2017 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
2019 req->width = panel->geometry.width;
2020 req->height += EDGE_WIDTH;
2022 else
2024 int h = panel->geometry.height;
2026 if (current_panel[PANEL_TOP])
2028 GtkWidget *win = current_panel[PANEL_TOP]->window;
2029 h -= win->allocation.height;
2032 if (current_panel[PANEL_BOTTOM])
2034 GtkWidget *win = current_panel[PANEL_BOTTOM]->window;
2035 h -= win->allocation.height;
2038 req->height = h;
2039 req->width += EDGE_WIDTH;
2043 static void update_side(GtkWidget *side)
2045 GList *kids, *next;
2047 kids = gtk_container_get_children(GTK_CONTAINER(side));
2048 for (next = kids; next; next = next->next)
2050 PanelIcon *pi;
2051 pi = g_object_get_data(next->data, "icon");
2052 panel_icon_set_tip(pi);
2054 g_list_free(kids);
2057 /* Tips or style has changed -- update everything on this panel */
2058 static void panel_set_style(Panel *panel)
2060 update_side(panel->before);
2061 update_side(panel->after);
2062 gtk_widget_queue_resize(panel->window);
2065 static gboolean recreate_panels(char **names)
2067 int i;
2069 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
2071 if (names[i])
2073 panel_new(names[i], i);
2074 g_free(names[i]);
2078 g_free(names);
2080 return FALSE;
2083 static void update_side_size(GtkWidget *side)
2085 GList *kids, *next;
2087 kids = gtk_container_get_children(GTK_CONTAINER(side));
2088 for (next = kids; next; next = next->next)
2090 PanelIcon *pi;
2091 pi = g_object_get_data(next->data, "icon");
2092 gtk_widget_queue_resize(pi->widget);
2094 g_list_free(kids);
2097 /* Update panel size and redraw */
2098 static void panel_update(Panel *panel)
2100 update_side_size(panel->before);
2101 update_side_size(panel->after);
2102 gtk_widget_queue_resize(panel->window);
2103 gtk_widget_queue_draw(panel->window);
2106 static void panel_style_changed(void)
2108 int i;
2110 if (o_override_redirect.has_changed)
2112 gchar **names;
2114 names = g_new(char *, PANEL_NUMBER_OF_SIDES);
2116 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
2118 Panel *panel = current_panel[i];
2119 names[i] = panel ? g_strdup(panel->name) : NULL;
2120 panel_new(NULL, i);
2123 g_idle_add((GtkFunction) recreate_panels, names);
2127 static gboolean draw_panel_edge(GtkWidget *widget, GdkEventExpose *event,
2128 Panel *panel)
2130 int x, y, width, height;
2132 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
2134 width = panel->geometry.width;
2135 height = EDGE_WIDTH;
2137 x = 0;
2138 if (panel->side == PANEL_BOTTOM)
2139 y = 0;
2140 else
2141 y = widget->allocation.height - EDGE_WIDTH;
2143 else
2145 width = EDGE_WIDTH;
2146 height = panel->geometry.height;
2148 y = 0;
2149 if (panel->side == PANEL_RIGHT)
2150 x = 0;
2151 else
2152 x = widget->allocation.width - EDGE_WIDTH;
2155 gdk_draw_rectangle(widget->window,
2156 widget->style->fg_gc[GTK_STATE_NORMAL], TRUE,
2157 x, y, width, height);
2159 return FALSE;
2162 static gpointer parent_class;
2164 static void panel_icon_destroy(Icon *icon)
2166 PanelIcon *pi = (PanelIcon *) icon;
2168 g_return_if_fail(pi != NULL);
2170 if (pi->image)
2171 g_object_unref(pi->image);
2173 g_return_if_fail(pi->widget != NULL);
2175 gtk_widget_destroy(pi->widget);
2178 static void panel_remove_items(void)
2180 Panel *panel;
2182 g_return_if_fail(icon_selection != NULL);
2184 panel = ((PanelIcon *) icon_selection->data)->panel;
2186 while (icon_selection)
2187 icon_destroy((Icon *) icon_selection->data);
2189 panel_save(panel);
2192 /* Icon's size, shape or appearance has changed - update the display */
2193 static void panel_icon_redraw(Icon *icon)
2195 PanelIcon *pi = (PanelIcon *) icon;
2197 gtk_widget_set_state(pi->widget,
2198 icon->selected ? GTK_STATE_SELECTED
2199 : GTK_STATE_NORMAL);
2201 /* Will regenerate the scaled icon from the new image */
2202 gtk_widget_queue_resize(pi->widget);
2204 panel_icon_set_tip((PanelIcon *) icon);
2207 static void panel_icon_update(Icon *icon)
2209 PanelIcon *pi = (PanelIcon *) icon;
2211 gtk_widget_queue_draw(pi->widget);
2212 gtk_label_set_text(GTK_LABEL(pi->label), icon->item->leafname);
2213 panel_save(pi->panel);
2216 /* The point of this is to clear the selection if the existing icons
2217 * aren't from the same panel...
2219 static gboolean panel_icon_same_group(Icon *icon, Icon *other)
2221 if (IS_PANEL_ICON(other))
2223 PanelIcon *a = (PanelIcon *) icon;
2224 PanelIcon *b = (PanelIcon *) other;
2226 return a->panel == b->panel;
2228 else
2229 return FALSE;
2232 static void panel_icon_class_init(gpointer gclass, gpointer data)
2234 IconClass *icon = (IconClass *) gclass;
2236 parent_class = g_type_class_peek_parent(gclass);
2238 icon->destroy = panel_icon_destroy;
2239 icon->redraw = panel_icon_redraw;
2240 icon->update = panel_icon_update;
2241 icon->remove_items = panel_remove_items;
2242 icon->same_group = panel_icon_same_group;
2243 icon->wink = panel_icon_wink;
2246 static void panel_icon_init(GTypeInstance *object, gpointer gclass)
2248 PanelIcon *pi = (PanelIcon *) object;
2250 pi->widget = NULL;
2251 pi->image = NULL;
2252 pi->label = NULL;
2253 pi->socket = NULL;
2254 pi->style = TEXT_UNDER_ICON;
2257 static GType panel_icon_get_type(void)
2259 static GType type = 0;
2261 if (!type)
2263 static const GTypeInfo info =
2265 sizeof (PanelIconClass),
2266 NULL, /* base_init */
2267 NULL, /* base_finalise */
2268 panel_icon_class_init,
2269 NULL, /* class_finalise */
2270 NULL, /* class_data */
2271 sizeof(PanelIcon),
2272 0, /* n_preallocs */
2273 panel_icon_init
2276 type = g_type_register_static(icon_get_type(),
2277 "PanelIcon", &info, 0);
2280 return type;
2283 static PanelIcon *panel_icon_new(Panel *panel,
2284 const char *pathname,
2285 const char *name)
2287 PanelIcon *pi;
2288 Icon *icon;
2290 pi = g_object_new(panel_icon_get_type(), NULL);
2291 icon = (Icon *) pi;
2293 icon_set_path(icon, pathname, name);
2294 pi->panel = panel;
2296 return pi;
2299 static gboolean panel_want_show_text(PanelIcon *pi)
2301 Icon *icon = (Icon *) pi;
2302 Panel *panel = pi->panel;
2304 if (!icon->item->leafname[0])
2305 return FALSE;
2307 if (panel->style == SHOW_BOTH)
2308 return TRUE;
2309 if (panel->style == SHOW_ICON)
2310 return FALSE;
2312 if (icon->item->flags & ITEM_FLAG_APPDIR)
2313 return FALSE;
2315 if (EXECUTABLE_FILE(icon->item))
2316 return FALSE;
2318 return TRUE;
2321 static void xinerama_sensitive(GladeXML *glade, gboolean sensitive)
2323 gtk_widget_set_sensitive(
2324 glade_xml_get_widget(glade, "panel_xinerama_monitor"),
2325 sensitive);
2328 inline static Panel *panel_from_opts_widget(GtkWidget *widget)
2330 return g_object_get_data(G_OBJECT(gtk_widget_get_toplevel(widget)),
2331 "rox-panel");
2334 static void panel_style_radio_toggled(GtkToggleButton *widget, int style)
2336 Panel *panel;
2338 if (!gtk_toggle_button_get_active(widget))
2339 return;
2340 panel = panel_from_opts_widget(GTK_WIDGET(widget));
2341 if (style != panel->style)
2343 panel->style = style;
2344 panel_set_style(panel);
2345 panel_save(panel);
2349 static void panel_xinerama_changed(Panel *panel)
2351 panel_update_geometry(panel);
2352 reposition_panel(panel->window, &panel->window->allocation, panel);
2353 gtk_widget_queue_resize(panel->window);
2354 panel_save(panel);
2357 static void panel_side_radio_toggled(GtkWidget *widget, PanelSide new_side)
2359 Panel *panel;
2360 PanelSide old_side;
2361 char *name, *other_side_name;
2363 if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
2364 return;
2366 panel = panel_from_opts_widget(widget);
2367 old_side = panel->side;
2368 if (new_side == old_side)
2369 return;
2371 name = g_strdup(panel->name);
2372 other_side_name = current_panel[new_side]
2373 ? g_strdup(current_panel[new_side]->name)
2374 : NULL;
2376 panel_new(name, new_side);
2377 g_object_set_data(G_OBJECT(gtk_widget_get_toplevel(widget)),
2378 "rox-panel", current_panel[new_side]);
2379 panel_new(other_side_name, old_side);
2381 g_free(name);
2382 g_free(other_side_name);
2385 static void panel_style_radio_0_toggled_cb(GtkToggleButton *widget)
2387 panel_style_radio_toggled(widget, 0);
2390 static void panel_style_radio_1_toggled_cb(GtkToggleButton *widget)
2392 panel_style_radio_toggled(widget, 1);
2395 static void panel_style_radio_2_toggled_cb(GtkToggleButton *widget)
2397 panel_style_radio_toggled(widget, 2);
2400 static void panel_width_changed_cb(GtkSpinButton *widget)
2402 Panel *panel = panel_from_opts_widget(GTK_WIDGET(widget));
2403 int width = gtk_spin_button_get_value_as_int(widget);
2405 if (width != panel->width)
2407 panel->width = width;
2408 panel_update(panel);
2409 panel_save(panel);
2413 static void panel_avoid_toggled_cb(GtkToggleButton *widget)
2415 Panel *panel = panel_from_opts_widget(GTK_WIDGET(widget));
2416 gboolean avoid = gtk_toggle_button_get_active(widget);
2418 if (avoid != panel->avoid)
2420 panel->avoid = avoid;
2421 panel_setup_struts(panel, &panel->window->allocation);
2422 panel_save(panel);
2426 static void panel_xinerama_confine_toggled_cb(GtkWidget *widget)
2428 Panel *panel = panel_from_opts_widget(widget);
2429 gboolean xinerama = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
2431 xinerama_sensitive(glade_get_widget_tree(gtk_widget_get_toplevel(widget)),
2432 xinerama);
2433 if (xinerama != panel->xinerama)
2435 panel->xinerama = xinerama;
2436 panel_xinerama_changed(panel);
2440 static void panel_xinerama_monitor_changed_cb(GtkSpinButton *widget)
2442 Panel *panel = panel_from_opts_widget(GTK_WIDGET(widget));
2443 int monitor = gtk_spin_button_get_value_as_int(widget);
2445 if (monitor != panel->monitor)
2447 panel->monitor = monitor;
2448 panel_xinerama_changed(panel);
2452 static void panel_pos_top_toggled_cb(GtkWidget *widget)
2454 panel_side_radio_toggled(widget, PANEL_TOP);
2457 static void panel_pos_bottom_toggled_cb(GtkWidget *widget)
2459 panel_side_radio_toggled(widget, PANEL_BOTTOM);
2462 static void panel_pos_left_toggled_cb(GtkWidget *widget)
2464 panel_side_radio_toggled(widget, PANEL_LEFT);
2467 static void panel_pos_right_toggled_cb(GtkWidget *widget)
2469 panel_side_radio_toggled(widget, PANEL_RIGHT);
2472 #define CONNECT_GLADE_CB(glade, handler) \
2473 glade_xml_signal_connect(glade, #handler, G_CALLBACK(handler))
2475 static void panel_connect_dialog_signal_handlers(GladeXML *glade)
2477 CONNECT_GLADE_CB(glade, gtk_widget_destroy);
2478 CONNECT_GLADE_CB(glade, panel_style_radio_0_toggled_cb);
2479 CONNECT_GLADE_CB(glade, panel_style_radio_1_toggled_cb);
2480 CONNECT_GLADE_CB(glade, panel_style_radio_2_toggled_cb);
2481 CONNECT_GLADE_CB(glade, panel_width_changed_cb);
2482 CONNECT_GLADE_CB(glade, panel_avoid_toggled_cb);
2483 CONNECT_GLADE_CB(glade, panel_xinerama_confine_toggled_cb);
2484 CONNECT_GLADE_CB(glade, panel_xinerama_monitor_changed_cb);
2485 CONNECT_GLADE_CB(glade, panel_pos_top_toggled_cb);
2486 CONNECT_GLADE_CB(glade, panel_pos_bottom_toggled_cb);
2487 CONNECT_GLADE_CB(glade, panel_pos_left_toggled_cb);
2488 CONNECT_GLADE_CB(glade, panel_pos_right_toggled_cb);
2491 static void panel_setup_options_dialog(GladeXML *glade, Panel *panel)
2493 char *wnm;
2494 const char *pos_radio;
2496 wnm = g_strdup_printf("panel_style_radio_%d", panel->style);
2497 gtk_toggle_button_set_active(
2498 GTK_TOGGLE_BUTTON(glade_xml_get_widget(glade, wnm)),
2499 TRUE);
2500 g_free(wnm);
2501 gtk_spin_button_set_value(
2502 GTK_SPIN_BUTTON(glade_xml_get_widget(glade, "panel_width")),
2503 panel->width);
2504 gtk_toggle_button_set_active(
2505 GTK_TOGGLE_BUTTON(glade_xml_get_widget(glade, "panel_avoid")),
2506 panel->avoid);
2507 gtk_toggle_button_set_active(
2508 GTK_TOGGLE_BUTTON(glade_xml_get_widget(glade,
2509 "panel_xinerama_confine")),
2510 panel->xinerama);
2511 gtk_spin_button_set_adjustment(
2512 GTK_SPIN_BUTTON(glade_xml_get_widget(glade, "panel_xinerama_monitor")),
2513 GTK_ADJUSTMENT(gtk_adjustment_new(MAX(0, panel->monitor),
2514 0, n_monitors - 1, 1, 10, 1)));
2515 xinerama_sensitive(glade, panel->xinerama);
2516 switch (panel->side)
2518 case PANEL_TOP:
2519 pos_radio = "panel_pos_top";
2520 break;
2521 case PANEL_BOTTOM:
2522 pos_radio = "panel_pos_bottom";
2523 break;
2524 case PANEL_LEFT:
2525 pos_radio = "panel_pos_left";
2526 break;
2527 case PANEL_RIGHT:
2528 pos_radio = "panel_pos_right";
2529 break;
2530 default:
2531 pos_radio = NULL;
2532 break;
2534 g_return_if_fail(pos_radio != NULL);
2535 gtk_toggle_button_set_active(
2536 GTK_TOGGLE_BUTTON(glade_xml_get_widget(glade, pos_radio)),
2537 TRUE);
2540 static void panel_show_options(Panel *panel)
2542 GtkWidget *dialog;
2543 gboolean already_showing = FALSE;
2544 GladeXML *glade = NULL;
2546 if (panel_options_dialog)
2548 dialog = panel_options_dialog;
2549 already_showing = TRUE;
2550 glade = glade_get_widget_tree(dialog);
2552 else
2554 glade = get_glade_xml("Panel Options");
2555 dialog = glade_xml_get_widget(glade, "Panel Options");
2556 panel_options_dialog = dialog;
2557 g_signal_connect(dialog, "destroy",
2558 G_CALLBACK(gtk_widget_destroyed),
2559 &panel_options_dialog);
2560 panel_connect_dialog_signal_handlers(glade);
2562 g_object_set_data(G_OBJECT(panel_options_dialog), "rox-panel", panel);
2564 panel_setup_options_dialog(glade, panel);
2566 if (already_showing)
2568 GtkWindow *win = GTK_WINDOW(dialog);
2570 gtk_widget_hide(dialog);
2571 /* This extra set_position() should ensure it moves to new position
2572 * under pointer */
2573 gtk_window_set_position(win, GTK_WIN_POS_CENTER_ALWAYS);
2574 gtk_window_set_position(win, GTK_WIN_POS_MOUSE);
2575 gtk_window_present(win);
2577 else
2579 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
2580 gtk_widget_show_all(dialog);
2584 static void panel_position_menu(GtkMenu *menu, gint *x, gint *y,
2585 gboolean *push_in, gpointer data)
2587 int *pos = (int *) data;
2588 GtkRequisition requisition;
2589 int margin = pos[2];
2590 int mon = pos[3];
2591 int mon_right = monitor_geom[mon].x +
2592 monitor_geom[mon].width;
2593 int mon_bottom = monitor_geom[mon].y +
2594 monitor_geom[mon].height;
2596 gtk_widget_size_request(GTK_WIDGET(menu), &requisition);
2598 if (pos[0] == -1)
2599 *x = mon_right - margin - requisition.width;
2600 else if (pos[0] == -2)
2601 *x = monitor_geom[mon].x + margin;
2602 else
2603 *x = pos[0] - (requisition.width >> 2);
2605 if (pos[1] == -1)
2606 *y = mon_bottom - margin - requisition.height;
2607 else if (pos[1] == -2)
2608 *y = monitor_geom[mon].y + margin;
2609 else
2610 *y = pos[1] - (requisition.height >> 2);
2612 *x = CLAMP(*x, 0, mon_right - requisition.width);
2613 *y = CLAMP(*y, 0, mon_bottom - requisition.height);
2615 *push_in = FALSE;
2618 static void panel_show_menu(GdkEventButton *event, PanelIcon *pi, Panel *panel)
2620 GtkWidget *option_item;
2621 PanelSide side = panel->side;
2622 int pos[4];
2624 pos[0] = event->x_root;
2625 pos[1] = event->y_root;
2626 pos[2] = MENU_MARGIN(side);
2627 /* FIXME: Should we read screen from event's window rather than
2628 * using default? */
2629 pos[3] = gdk_screen_get_monitor_at_point(
2630 gdk_screen_get_default(),
2631 event->x_root, event->y_root);
2633 option_item = gtk_image_menu_item_new_with_label(_("Panel Options..."));
2634 g_signal_connect_swapped(option_item, "activate",
2635 G_CALLBACK(panel_show_options), panel);
2637 icon_prepare_menu((Icon *) pi, option_item, NULL);
2639 if (side == PANEL_LEFT)
2640 pos[0] = -2;
2641 else if (side == PANEL_RIGHT)
2642 pos[0] = -1;
2644 if (side == PANEL_TOP)
2645 pos[1] = -2;
2646 else if (side == PANEL_BOTTOM)
2647 pos[1] = -1;
2649 gtk_menu_popup(GTK_MENU(icon_menu), NULL, NULL,
2650 panel_position_menu,
2651 (gpointer) pos, event->button, event->time);
2654 /* Note: also called from icon handler */
2655 static gboolean panel_drag_motion(GtkWidget *widget,
2656 GdkDragContext *context,
2657 gint x,
2658 gint y,
2659 guint time,
2660 Panel *panel)
2662 int panel_x, panel_y;
2664 gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
2666 motion_may_raise(panel, panel_x, panel_y);
2667 gdk_drag_status(context, 0, time);
2669 return TRUE;
2672 static gboolean insert_drag_motion(GtkWidget *widget,
2673 GdkDragContext *context,
2674 gint x,
2675 gint y,
2676 guint time,
2677 Panel *panel)
2679 int panel_x, panel_y;
2681 gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
2682 motion_may_raise(panel, panel_x, panel_y);
2684 return FALSE;
2687 /* Note: also called from icon handler */
2688 static void panel_drag_leave(GtkWidget *widget,
2689 GdkDragContext *context,
2690 guint32 time,
2691 Panel *panel)
2693 GdkWindow *pinboard, *window;
2694 GtkAllocation *alloc = &panel->window->allocation;
2695 int x, y;
2697 window = panel->window->window;
2698 gdk_window_get_pointer(window, &x, &y, NULL);
2699 if (x < 0 || y < 0 || x > alloc->width || y > alloc->height)
2701 keep_below(panel->window->window, TRUE);
2703 /* Shouldn't need this as well as keep_below but some WMs don't
2704 * automatically lower as soon as the hint is set */
2705 pinboard = pinboard_get_window();
2706 window_put_just_above(panel->window->window, pinboard);
2710 static void panel_update_geometry(Panel *panel)
2712 if (panel->xinerama && panel->monitor >= n_monitors)
2714 g_warning(_("Xinerama monitor %d unavailable"), panel->monitor);
2715 panel->xinerama = FALSE;
2718 if (panel->xinerama)
2720 panel->geometry = monitor_geom[panel->monitor];
2722 else
2724 panel->geometry.x = panel->geometry.y = 0;
2725 panel->geometry.width = screen_width;
2726 panel->geometry.height = screen_height;
2730 static GList *build_monitor_number(Option *option, xmlNode *node, guchar *label)
2732 GtkObject *adj;
2734 adj = gtk_adjustment_new(MAX(0, panel_monitor),
2735 0, n_monitors - 1, 1, 10, 1);
2736 return build_numentry_base(option, node, label, GTK_ADJUSTMENT(adj));
2739 static const char *panel_side_to_translated_name(PanelSide side)
2741 switch (side)
2743 case PANEL_TOP:
2744 return _("Top");
2745 case PANEL_BOTTOM:
2746 return _("Bottom");
2747 case PANEL_LEFT:
2748 return _("Left");
2749 case PANEL_RIGHT:
2750 return _("Right");
2751 case PANEL_DEFAULT_SIDE:
2752 return _("Default");
2753 default:
2754 break;
2756 return _("Unknown side");
2759 const char *panel_side_to_name(PanelSide side)
2761 switch (side)
2763 case PANEL_TOP:
2764 return "Top";
2765 case PANEL_BOTTOM:
2766 return "Bottom";
2767 case PANEL_LEFT:
2768 return "Left";
2769 case PANEL_RIGHT:
2770 return "Right";
2771 case PANEL_DEFAULT_SIDE:
2772 return "Default";
2773 default:
2774 break;
2776 return "UnknownSide";
2779 /* Returns PANEL_NUMBER_OF_SIDES if name is invalid */
2780 PanelSide panel_name_to_side(gchar *side)
2782 if (strcmp(side, "Top") == 0)
2783 return PANEL_TOP;
2784 else if (strcmp(side, "Bottom") == 0)
2785 return PANEL_BOTTOM;
2786 else if (strcmp(side, "Left") == 0)
2787 return PANEL_LEFT;
2788 else if (strcmp(side, "Right") == 0)
2789 return PANEL_RIGHT;
2790 else
2791 g_warning("Unknown panel side '%s'", side);
2792 return PANEL_NUMBER_OF_SIDES;
2795 static void panel_add_callback(PanelSide side)
2797 g_return_if_fail(current_panel[side] == NULL);
2798 panel_new(panel_side_to_name(side), side);
2801 GtkWidget *panel_new_panel_submenu(void)
2803 GtkWidget *menu = gtk_menu_new();
2804 PanelSide side;
2806 for (side = 0; side < PANEL_NUMBER_OF_SIDES; ++side)
2808 GtkWidget *item = gtk_menu_item_new_with_label(
2809 panel_side_to_translated_name(side));
2811 g_signal_connect_swapped(item, "activate",
2812 G_CALLBACK(panel_add_callback), GINT_TO_POINTER(side));
2813 gtk_widget_set_sensitive(item, current_panel[side] == NULL);
2814 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2815 gtk_widget_show(item);
2817 return menu;