Add a "keep panel on top" option
[rox-filer.git] / ROX-Filer / src / panel.c
blob6766f8ccdfe4399c27d29989903f7a13f30d3217
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);
204 static gboolean panel_keep_below(Panel *panel, gboolean setting);
207 static GtkWidget *dnd_highlight = NULL; /* (stops flickering) */
209 #define SHOW_BOTH 0
210 #define SHOW_APPS_SMALL 1
211 #define SHOW_ICON 2
212 static Option o_panel_style;
213 static Option o_panel_width;
214 static Option o_panel_xinerama;
215 static Option o_panel_monitor;
216 static Option o_panel_avoid;
217 static Option o_panel_is_dock;
218 static Option o_panel_on_top;
220 static gint panel_monitor = -1;
222 static int closing_panel = 0; /* Don't panel_save; destroying! */
224 /****************************************************************
225 * EXTERNAL INTERFACE *
226 ****************************************************************/
228 void panel_init(void)
230 option_add_int(&o_panel_style, "panel_style", SHOW_APPS_SMALL);
231 option_add_int(&o_panel_width, "panel_width", 52);
233 option_add_int(&o_panel_xinerama, "panel_xinerama", 0);
234 option_add_int(&o_panel_monitor, "panel_monitor", 0);
236 option_add_int(&o_panel_avoid, "panel_avoid", TRUE);
237 option_add_int(&o_panel_is_dock, "panel_is_dock", TRUE);
238 option_add_int(&o_panel_on_top, "panel_on_top", FALSE);
240 option_add_notify(panel_style_changed);
242 option_register_widget("monitor-number", build_monitor_number);
245 /* Return a free edge for a new panel.
246 * If no edge is free, returns PANEL_BOTTOM.
248 static PanelSide find_free_side()
250 if (!current_panel[PANEL_BOTTOM])
251 return PANEL_BOTTOM;
253 if (!current_panel[PANEL_TOP])
254 return PANEL_TOP;
256 if (!current_panel[PANEL_LEFT])
257 return PANEL_LEFT;
259 if (!current_panel[PANEL_RIGHT])
260 return PANEL_RIGHT;
262 return PANEL_BOTTOM;
265 /* Returns TRUE and sets *target if the property exists, otherwise returns
266 * FALSE and leaves *target unchanged */
267 static gboolean get_int_prop(xmlNodePtr node, const char *name, int *target)
269 char *prop = xmlGetProp(node, name);
271 if (prop)
273 *target = atoi(prop);
274 g_free(prop);
275 return TRUE;
277 return FALSE;
280 static void set_int_prop(xmlNodePtr node, const char *name, int value)
282 char prop[16];
284 sprintf(prop, "%d", value);
285 xmlSetProp(node, name, prop);
288 static void panel_load_options_from_xml(Panel *panel, xmlDocPtr doc)
290 xmlNodePtr root;
291 xmlNodePtr options;
293 root = xmlDocGetRootElement(doc);
294 options = get_subnode(root, NULL, "options");
295 if (!options)
296 return;
297 get_int_prop(options, "style", &panel->style);
298 get_int_prop(options, "width", &panel->width);
299 get_int_prop(options, "avoid", &panel->avoid);
300 get_int_prop(options, "xinerama", &panel->xinerama);
301 get_int_prop(options, "monitor", &panel->monitor);
304 static void save_panels(void)
306 char *filename = choices_find_xdg_path_save("panels",
307 "ROX-Filer", "rox.sourceforge.net", TRUE);
308 char *tmp = g_strconcat(filename, ".new", NULL);
309 FILE *fp = fopen(tmp, "w");
311 if (fp)
313 PanelSide n;
315 for (n = 0; n < PANEL_NUMBER_OF_SIDES; ++n)
317 if (current_panel[n])
318 fprintf(fp, "%s\n", current_panel[n]->name);
320 fclose(fp);
321 if (rename(tmp, filename))
322 g_critical(_("Unable to replace '%s'"), filename);
324 else
326 g_critical(_("Unable to save '%s'"), tmp);
328 g_free(tmp);
329 g_free(filename);
332 /* 'name' may be NULL or "" to remove the panel */
333 Panel *panel_new(const gchar *name, PanelSide side)
335 guchar *load_path;
336 Panel *panel;
337 GtkWidget *vp, *box, *frame, *align;
338 xmlDocPtr panel_doc = NULL;
339 gboolean need_resave = FALSE;
341 g_return_val_if_fail(side == PANEL_DEFAULT_SIDE ||
342 (side >= 0 && side < PANEL_NUMBER_OF_SIDES), NULL);
343 g_return_val_if_fail(loading_panel == NULL, NULL);
345 if (name && *name == '\0')
346 name = NULL;
348 if (!name)
349 load_path = NULL;
350 else if (strchr(name, '/'))
351 load_path = g_strdup(name);
352 else
354 guchar *leaf;
356 leaf = g_strconcat("pan_", name, NULL);
357 load_path = choices_find_xdg_path_load(leaf, PROJECT, SITE);
358 g_free(leaf);
361 if (load_path && access(load_path, F_OK) == 0)
363 char *saved_side;
364 xmlNodePtr root;
366 panel_doc = xmlParseFile(load_path);
367 root = xmlDocGetRootElement(panel_doc);
369 saved_side = xmlGetProp(root, "side");
370 if (saved_side)
372 PanelSide old_side;
373 old_side = panel_name_to_side(saved_side);
374 g_free(saved_side);
376 if (side == PANEL_DEFAULT_SIDE)
377 side = old_side;
378 else if (side != old_side)
379 need_resave = TRUE;
381 else
382 need_resave = TRUE;
385 if (side == PANEL_DEFAULT_SIDE)
386 side = find_free_side();
388 if (current_panel[side])
390 if (name)
391 number_of_windows++;
392 closing_panel++;
393 gtk_widget_destroy(current_panel[side]->window);
394 closing_panel--;
395 if (name)
396 number_of_windows--;
399 if (name == NULL || *name == '\0')
401 save_panels();
402 return NULL;
405 panel = g_new(Panel, 1);
406 panel->name = g_strdup(name);
407 panel->side = side;
408 panel->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
409 panel->autoscroll_speed = 0;
411 /* These are fallbacks from legacy global options */
412 panel->style = o_panel_style.int_value;
413 panel->width = o_panel_width.int_value;
414 panel->xinerama = o_panel_xinerama.int_value;
415 panel->monitor = o_panel_monitor.int_value;
416 panel->avoid = o_panel_avoid.int_value;
418 /* Now try to load options from this panel's XML */
419 if (panel_doc)
421 panel_load_options_from_xml(panel, panel_doc);
423 else
425 /* Otherwise ensure old settings are migrated */
426 need_resave = TRUE;
429 panel_update_geometry(panel);
431 gtk_window_set_resizable(GTK_WINDOW(panel->window), FALSE);
432 gtk_window_set_wmclass(GTK_WINDOW(panel->window), "ROX-Panel", PROJECT);
433 gtk_widget_set_name(panel->window, "rox-panel");
434 gtk_widget_set_events(panel->window,
435 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
436 GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
438 /* We make the panel a drop target only so that we can auto-raise! */
439 gtk_drag_dest_set(panel->window, 0, NULL, 0, GDK_ACTION_PRIVATE);
440 g_signal_connect(panel->window, "drag_leave",
441 G_CALLBACK(panel_drag_leave), panel);
442 g_signal_connect(panel->window, "drag_motion",
443 G_CALLBACK(panel_drag_motion), panel);
445 g_signal_connect(panel->window, "delete-event",
446 G_CALLBACK(panel_delete), panel);
447 g_signal_connect(panel->window, "destroy",
448 G_CALLBACK(panel_destroyed), panel);
449 g_signal_connect(panel->window, "button_press_event",
450 G_CALLBACK(panel_button_press), panel);
451 g_signal_connect(panel->window, "button_release_event",
452 G_CALLBACK(panel_button_release), panel);
453 g_signal_connect(panel->window, "motion-notify-event",
454 G_CALLBACK(panel_motion_event), panel);
455 g_signal_connect(panel->window, "leave-notify-event",
456 G_CALLBACK(panel_leave_event), panel);
458 if (panel->side == PANEL_RIGHT)
459 align = gtk_alignment_new(1.0, 0.0, 0.0, 1.0);
460 else if (panel->side == PANEL_BOTTOM)
461 align = gtk_alignment_new(0.0, 1.0, 1.0, 0.0);
462 else if (panel->side == PANEL_TOP)
463 align = gtk_alignment_new(0.0, 0.0, 1.0, 0.0);
464 else
465 align = gtk_alignment_new(0.0, 0.0, 0.0, 1.0);
467 gtk_container_add(GTK_CONTAINER(panel->window), align);
469 vp = gtk_viewport_new(NULL, NULL);
470 gtk_container_set_resize_mode(GTK_CONTAINER(vp), GTK_RESIZE_PARENT);
471 gtk_viewport_set_shadow_type(GTK_VIEWPORT(vp), GTK_SHADOW_NONE);
472 gtk_container_add(GTK_CONTAINER(align), vp);
474 g_signal_connect(align, "expose-event",
475 G_CALLBACK(draw_panel_edge), panel);
477 if (side == PANEL_TOP || side == PANEL_BOTTOM)
479 panel->adj = gtk_viewport_get_hadjustment(GTK_VIEWPORT(vp));
480 box = gtk_hbox_new(FALSE, 0);
481 panel->before = gtk_hbox_new(FALSE, 0);
482 panel->after = gtk_hbox_new(FALSE, 0);
484 else
486 panel->adj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(vp));
487 box = gtk_vbox_new(FALSE, 0);
488 panel->before = gtk_vbox_new(FALSE, 0);
489 panel->after = gtk_vbox_new(FALSE, 0);
492 gtk_container_add(GTK_CONTAINER(vp), box);
493 gtk_box_pack_start(GTK_BOX(box), panel->before, FALSE, TRUE, 0);
494 gtk_box_pack_end(GTK_BOX(box), panel->after, FALSE, TRUE, 0);
496 frame = make_insert_frame(panel);
497 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
499 /* This is used so that we can find the middle easily! */
500 panel->gap = gtk_event_box_new();
501 gtk_box_pack_start(GTK_BOX(box), panel->gap, FALSE, FALSE, 0);
503 frame = make_insert_frame(panel);
504 g_object_set_data(G_OBJECT(frame), "after", "yes");
505 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
507 if (o_panel_is_dock.int_value)
508 gtk_window_set_type_hint(GTK_WINDOW(panel->window),
509 GDK_WINDOW_TYPE_HINT_DOCK);
511 gtk_widget_realize(panel->window);
512 make_panel_window(panel->window);
513 gtk_window_stick(GTK_WINDOW(panel->window));
515 gtk_widget_show_all(align);
517 loading_panel = panel;
518 if (panel_doc)
520 panel_load_from_xml(panel, panel_doc);
521 xmlFreeDoc(panel_doc);
523 if (need_resave)
524 panel_save(panel);
526 else if (load_path)
528 parse_file(load_path, pan_from_file);
529 info_message(_("Your old panel file has been "
530 "converted to the new XML format."));
531 panel_save(panel);
533 else
535 /* Don't scare users with an empty panel... */
536 guchar *apps;
538 panel_add_item(panel, "~", "Home", FALSE, NULL, NULL, FALSE);
540 apps = pathdup(make_path(app_dir, ".."));
541 if (apps)
543 panel_add_item(panel, apps, "Apps", FALSE, NULL, NULL, FALSE);
544 g_free(apps);
547 loading_panel = NULL;
548 g_free(load_path);
550 current_panel[side] = panel;
552 gtk_widget_queue_resize(box);
553 g_signal_connect(panel->window, "size-request",
554 G_CALLBACK(panel_post_resize), panel);
555 g_signal_connect(panel->window, "size-allocate",
556 G_CALLBACK(reposition_panel), panel);
558 number_of_windows++;
559 gdk_window_lower(panel->window->window);
560 gtk_widget_show(panel->window);
561 /* This has no effect until after window is showing; GTK+ bug? */
562 if (panel_keep_below(panel, TRUE))
564 GdkWindow *pinboard;
566 pinboard = pinboard_get_window();
567 /* (if pinboard is NULL, will go right to the back) */
568 window_put_just_above(panel->window->window, pinboard);
571 save_panels();
573 return panel;
576 /* Externally visible function to add an item to a panel */
577 gboolean panel_add(PanelSide side,
578 const gchar *path, const gchar *label, gboolean after, const gchar *shortcut, const gchar *args,
579 gboolean locked)
581 g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, FALSE);
583 g_return_val_if_fail(current_panel[side] != NULL, FALSE);
585 panel_add_item(current_panel[side], path, label, after, shortcut, args, locked);
587 return TRUE;
590 /* Add the area covered by the panels to the region */
591 void panel_mark_used(GdkRegion *used)
593 int i;
595 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
597 Panel *panel = current_panel[i];
598 GdkRectangle rect;
600 if (!panel)
601 continue;
603 gdk_window_get_root_origin(panel->window->window,
604 &rect.x, &rect.y);
605 rect.width = panel->window->allocation.width;
606 rect.height = panel->window->allocation.height;
608 gdk_region_union_with_rect(used, &rect);
612 /* On xrandr screen size changes, update all panels */
613 void panel_update_size(void)
615 int i;
617 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
619 if (current_panel[i])
621 reposition_panel(current_panel[i]->window,
622 &current_panel[i]->window->allocation,
623 current_panel[i]);
624 gtk_widget_queue_resize(current_panel[i]->window);
629 /****************************************************************
630 * INTERNAL FUNCTIONS *
631 ****************************************************************/
633 /* User has tried to close the panel via the window manager - confirm */
634 static int panel_delete(GtkWidget *widget, GdkEvent *event, Panel *panel)
636 return !confirm(_("You have tried to close a panel via the window "
637 "manager - I usually find that this is accidental... "
638 "really close?"),
639 GTK_STOCK_CLOSE, NULL);
642 static void panel_destroyed(GtkWidget *widget, Panel *panel)
644 if (panel_options_dialog)
646 Panel *dlg_panel = g_object_get_data(G_OBJECT(panel_options_dialog),
647 "rox-panel");
649 if (dlg_panel == panel)
650 gtk_widget_destroy(panel_options_dialog);
653 if (current_panel[panel->side] == panel)
654 current_panel[panel->side] = NULL;
656 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
658 if (current_panel[PANEL_RIGHT])
659 gtk_widget_queue_resize(
660 current_panel[PANEL_RIGHT]->window);
661 if (current_panel[PANEL_LEFT])
662 gtk_widget_queue_resize(
663 current_panel[PANEL_LEFT]->window);
666 if (panel->autoscroll_speed)
667 g_source_remove(panel->autoscroll_to);
669 g_free(panel->name);
670 g_free(panel);
672 one_less_window();
675 static void panel_load_side(Panel *panel, xmlNodePtr side, gboolean after)
677 xmlNodePtr node;
678 char *label, *path, *shortcut, *args, *tmp;
679 gboolean locked;
681 for (node = side->xmlChildrenNode; node; node = node->next)
683 if (node->type != XML_ELEMENT_NODE)
684 continue;
685 if (strcmp(node->name, "icon") != 0)
686 continue;
688 label = xmlGetProp(node, "label");
689 if (!label)
690 label = g_strdup("<missing label>");
691 path = xmlNodeGetContent(node);
692 if (!path)
693 path = g_strdup("<missing path>");
694 shortcut = xmlGetProp(node, "shortcut");
695 args = xmlGetProp(node, "args");
696 tmp = xmlGetProp(node, "locked");
697 if (tmp)
699 locked = text_to_boolean(tmp, FALSE);
700 g_free(tmp);
702 else
703 locked = FALSE;
705 panel_add_item(panel, path, label, after, shortcut, args, locked);
707 g_free(path);
708 g_free(label);
709 g_free(shortcut);
710 g_free(args);
714 /* Create one panel icon for each icon in the doc */
715 static void panel_load_from_xml(Panel *panel, xmlDocPtr doc)
717 xmlNodePtr root;
719 root = xmlDocGetRootElement(doc);
720 panel_load_side(panel, get_subnode(root, NULL, "start"), FALSE);
721 panel_load_side(panel, get_subnode(root, NULL, "end"), TRUE);
724 /* Called for each line in the config file while loading a new panel */
725 static const char *pan_from_file(gchar *line)
727 gchar *sep, *leaf;
729 g_return_val_if_fail(line != NULL, NULL);
730 g_return_val_if_fail(loading_panel != NULL, NULL);
732 if (*line == '\0')
733 return NULL;
735 sep = strpbrk(line, "<>");
736 if (!sep)
737 return _("Missing < or > in panel config file");
739 if (sep != line)
740 leaf = g_strndup(line, sep - line);
741 else
742 leaf = NULL;
744 panel_add_item(loading_panel, sep + 1, leaf, sep[0] == '>',
745 NULL, NULL, FALSE);
747 g_free(leaf);
749 return NULL;
752 static gboolean icon_pointer_in(GtkWidget *widget,
753 GdkEventCrossing *event,
754 Icon *icon)
756 gtk_widget_set_state(widget,
757 icon->selected ? GTK_STATE_SELECTED : GTK_STATE_PRELIGHT);
759 return 0;
762 static gboolean icon_pointer_out(GtkWidget *widget,
763 GdkEventCrossing *event,
764 Icon *icon)
766 gtk_widget_set_state(widget,
767 icon->selected ? GTK_STATE_SELECTED : GTK_STATE_NORMAL);
769 return 0;
772 static void panel_icon_destroyed(PanelIcon *pi)
774 g_return_if_fail(pi->widget != NULL);
776 pi->widget = NULL;
778 g_object_unref(pi);
781 /* Set the tooltip AND hide/show the label */
782 static void panel_icon_set_tip(PanelIcon *pi)
784 XMLwrapper *ai;
785 xmlNode *node;
786 Icon *icon = (Icon *) pi;
788 g_return_if_fail(pi != NULL);
790 if (pi->label)
792 if (panel_want_show_text(pi))
793 gtk_widget_show(pi->label);
794 else
795 gtk_widget_hide(pi->label);
798 if (pi->socket)
799 ai = NULL;
800 else
801 ai = appinfo_get(icon->path, icon->item);
803 if (ai && ((node = xml_get_section(ai, NULL, "Summary"))))
805 guchar *str;
806 str = xmlNodeListGetString(node->doc,
807 node->xmlChildrenNode, 1);
808 if (str)
810 gtk_tooltips_set_tip(tooltips, pi->widget, str, NULL);
811 g_free(str);
814 else if ((!panel_want_show_text(pi)) && !pi->socket)
816 if (icon->item->leafname && icon->item->leafname[0])
817 gtk_tooltips_set_tip(tooltips, pi->widget,
818 icon->item->leafname, NULL);
820 else
821 gtk_tooltips_set_tip(tooltips, pi->widget, NULL, NULL);
823 if (ai)
824 g_object_unref(ai);
827 /* Add an icon with this path to the panel. If after is TRUE then the
828 * icon is added to the right/bottom end of the panel.
830 * If name is NULL a suitable name is taken from path.
832 static void panel_add_item(Panel *panel,
833 const gchar *path,
834 const gchar *name,
835 gboolean after,
836 const gchar *shortcut,
837 const gchar *args,
838 gboolean locked)
840 GtkWidget *widget;
841 PanelIcon *pi;
842 Icon *icon;
844 g_return_if_fail(panel != NULL);
845 g_return_if_fail(path != NULL);
847 widget = gtk_event_box_new();
848 gtk_widget_set_events(widget,
849 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
850 GDK_BUTTON3_MOTION_MASK |
851 GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
852 GDK_BUTTON_RELEASE_MASK);
854 gtk_box_pack_start(GTK_BOX(after ? panel->after : panel->before),
855 widget, FALSE, TRUE, 0);
856 if (after)
857 gtk_box_reorder_child(GTK_BOX(panel->after), widget, 0);
859 gtk_widget_realize(widget);
861 pi = panel_icon_new(panel, path, name);
862 icon = (Icon *) pi;
864 /* Widget takes the initial ref of Icon */
865 g_object_set_data(G_OBJECT(widget), "icon", pi);
867 pi->widget = widget;
868 g_object_ref(widget);
870 gtk_widget_set_name(pi->widget, "panel-icon");
872 g_signal_connect_swapped(widget, "destroy",
873 G_CALLBACK(panel_icon_destroyed), pi);
875 if (icon->item->base_type == TYPE_DIRECTORY)
876 run_applet(pi);
878 g_signal_connect(widget, "button_release_event",
879 G_CALLBACK(icon_button_release), pi);
880 g_signal_connect(widget, "button_press_event",
881 G_CALLBACK(icon_button_press), pi);
882 g_signal_connect(widget, "motion-notify-event",
883 G_CALLBACK(icon_motion_event), pi);
884 g_signal_connect(widget, "enter-notify-event",
885 G_CALLBACK(icon_pointer_in), pi);
886 g_signal_connect(widget, "leave-notify-event",
887 G_CALLBACK(icon_pointer_out), pi);
889 if (!pi->socket)
891 g_signal_connect(widget, "enter-notify-event",
892 G_CALLBACK(enter_icon), pi);
893 g_signal_connect_after(widget, "expose_event",
894 G_CALLBACK(expose_icon), pi);
895 g_signal_connect(widget, "drag_data_get",
896 G_CALLBACK(drag_data_get), NULL);
898 g_signal_connect(widget, "size_request",
899 G_CALLBACK(size_request), pi);
901 drag_set_panel_dest(pi);
903 pi->label = gtk_label_new(icon->item->leafname);
904 gtk_container_add(GTK_CONTAINER(pi->widget), pi->label);
905 gtk_misc_set_alignment(GTK_MISC(pi->label), 0.5, 1);
906 gtk_misc_set_padding(GTK_MISC(pi->label), 1, 2);
909 icon_set_shortcut(icon, shortcut);
910 icon_set_arguments(icon, args);
911 icon->locked = locked;
913 if (!loading_panel)
914 panel_save(panel);
916 panel_icon_set_tip(pi);
917 gtk_widget_show(widget);
920 static gboolean remove_item_from_side(GtkWidget *container, const gchar *path,
921 const gchar *label)
923 GList *kids, *next;
924 gboolean found = FALSE;
926 kids = gtk_container_get_children(GTK_CONTAINER(container));
928 for (next = kids; next; next = next->next)
930 Icon *icon;
931 icon = g_object_get_data(G_OBJECT(next->data), "icon");
932 if (!icon)
933 continue;
935 if ((!path || strcmp(path, icon->src_path) == 0) &&
936 (!label || strcmp(label, icon->item->leafname)==0))
938 icon->locked = FALSE;
939 icon_destroy(icon);
940 found = TRUE;
941 break;
945 g_list_free(kids);
947 return found;
950 /* Remove an item with this path. If more than one item matches, only
951 * one is removed. If label is not NULL then it must also match the item.
952 * Returns TRUE if an item was successfully removed.
954 gboolean panel_remove_item(PanelSide side, const gchar *path,
955 const gchar *label)
957 Panel *panel;
959 g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, FALSE);
961 g_return_val_if_fail(path != NULL || label != NULL, FALSE);
963 panel = current_panel[side];
964 if (!panel)
966 g_warning("No panel on this side of the screen!");
967 return FALSE;
970 if (remove_item_from_side(panel->before, path, label) ||
971 remove_item_from_side(panel->after, path, label))
973 panel_save(panel);
974 panel_update(panel);
975 return TRUE;
978 g_warning("Panel item path='%s', label='%s' not found", path, label);
979 return FALSE;
982 /* Called when Gtk+ wants to know how much space an icon needs.
983 * 'req' is already big enough for the label, if shown.
985 static void size_request(GtkWidget *widget, GtkRequisition *req, PanelIcon *pi)
987 Icon *icon = (Icon *) pi;
988 gboolean horz = (pi->panel->side == PANEL_TOP ||
989 pi->panel->side == PANEL_BOTTOM);
990 int max_width = 100;
991 int max_height = 100;
992 int image_width, image_height;
993 Panel *panel = pi->panel;
995 if (horz)
996 max_height = panel->width - req->height;
997 else
998 max_width = MAX(panel->width, req->width);
1000 /* TODO: really need to recreate? */
1001 if (pi->image)
1002 g_object_unref(pi->image);
1004 pi->image = scale_pixbuf(di_image(icon->item)->src_pixbuf,
1005 MAX(20, max_width), MAX(20, max_height));
1007 image_width = gdk_pixbuf_get_width(pi->image);
1008 image_height = gdk_pixbuf_get_height(pi->image);
1010 if (req->height > 0 && max_height < req->height)
1012 pi->style = TEXT_BESIDE_ICON;
1013 req->width += image_width;
1014 req->height = MAX(req->height, image_height);
1015 gtk_misc_set_alignment(GTK_MISC(pi->label), 1, 0.5);
1017 else
1019 pi->style = TEXT_UNDER_ICON;
1020 req->width = MAX(req->width, image_width);
1021 req->height += image_height;
1022 gtk_misc_set_alignment(GTK_MISC(pi->label), 0.5, 1);
1025 if (horz)
1026 req->width += PANEL_ICON_SPACING;
1027 else
1028 req->height += PANEL_ICON_SPACING;
1031 static gint expose_icon(GtkWidget *widget,
1032 GdkEventExpose *event,
1033 PanelIcon *pi)
1035 return draw_icon(widget, &event->area, pi);
1038 static gint draw_icon(GtkWidget *widget, GdkRectangle *badarea, PanelIcon *pi)
1040 GdkRectangle area;
1041 int width, height;
1042 Icon *icon = (Icon *) pi;
1043 int image_x;
1044 int image_y;
1045 GdkPixbuf *image;
1046 int text_height = 0;
1048 gdk_drawable_get_size(widget->window, &area.width, &area.height);
1050 if (panel_want_show_text(pi))
1051 text_height = pi->label->requisition.height;
1053 g_return_val_if_fail(pi->image != NULL, FALSE);
1055 image = pi->image;
1057 width = gdk_pixbuf_get_width(image);
1058 height = gdk_pixbuf_get_height(image);
1060 if (pi->style == TEXT_UNDER_ICON)
1062 image_x = (area.width - width) >> 1;
1063 image_y = (area.height - height - text_height) >> 1;
1065 else
1067 image_x = PANEL_ICON_SPACING - 2;
1068 image_y = (area.height - height) >> 1;
1071 gdk_pixbuf_render_to_drawable_alpha(
1072 image,
1073 widget->window,
1074 0, 0, /* src */
1075 image_x, image_y, /* dest */
1076 width, height,
1077 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
1078 GDK_RGB_DITHER_NORMAL, 0, 0);
1080 if (icon->item->flags & ITEM_FLAG_SYMLINK)
1082 gdk_pixbuf_render_to_drawable_alpha(im_symlink->pixbuf,
1083 widget->window,
1084 0, 0, /* src */
1085 image_x, image_y + 2, /* dest */
1086 -1, -1,
1087 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
1088 GDK_RGB_DITHER_NORMAL, 0, 0);
1090 if (icon->item->flags & ITEM_FLAG_MOUNT_POINT)
1092 MaskedPixmap *mp = icon->item->flags & ITEM_FLAG_MOUNTED
1093 ? im_mounted
1094 : im_unmounted;
1096 gdk_pixbuf_render_to_drawable_alpha(mp->pixbuf,
1097 widget->window,
1098 0, 0, /* src */
1099 image_x, image_y + 2, /* dest */
1100 -1, -1,
1101 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
1102 GDK_RGB_DITHER_NORMAL, 0, 0);
1104 return FALSE;
1107 static void panel_icon_wink(Icon *icon)
1109 PanelIcon *pi = (PanelIcon *) icon;
1111 wink_widget(pi->widget);
1114 /* icon may be NULL if the event is on the background */
1115 static void perform_action(Panel *panel, PanelIcon *pi, GdkEventButton *event)
1117 BindAction action;
1118 Icon *icon = (Icon *) pi;
1120 action = bind_lookup_bev(icon ? BIND_PANEL_ICON : BIND_PANEL, event);
1122 if (pi && pi->socket)
1123 if (action != ACT_POPUP_MENU && action != ACT_MOVE_ICON)
1124 return;
1126 switch (action)
1128 case ACT_OPEN_ITEM:
1129 dnd_motion_ungrab();
1130 wink_widget(pi->widget);
1131 icon_run(icon);
1132 break;
1133 case ACT_EDIT_ITEM:
1134 dnd_motion_ungrab();
1135 wink_widget(pi->widget);
1136 run_diritem(icon->path, icon->item, NULL, NULL, TRUE);
1137 break;
1138 case ACT_POPUP_MENU:
1139 dnd_motion_ungrab();
1140 panel_show_menu(event, pi, panel);
1141 break;
1142 case ACT_MOVE_ICON:
1143 dnd_motion_start(MOTION_REPOSITION);
1144 break;
1145 case ACT_PRIME_AND_SELECT:
1146 if (!icon->selected)
1147 icon_select_only(icon);
1148 dnd_motion_start(MOTION_READY_FOR_DND);
1149 break;
1150 case ACT_PRIME_AND_TOGGLE:
1151 icon_set_selected(icon, !icon->selected);
1152 dnd_motion_start(MOTION_READY_FOR_DND);
1153 break;
1154 case ACT_PRIME_FOR_DND:
1155 dnd_motion_start(MOTION_READY_FOR_DND);
1156 break;
1157 case ACT_TOGGLE_SELECTED:
1158 icon_set_selected(icon, !icon->selected);
1159 break;
1160 case ACT_SELECT_EXCL:
1161 icon_set_selected(icon, TRUE);
1162 break;
1163 case ACT_IGNORE:
1164 break;
1165 case ACT_CLEAR_SELECTION:
1166 dnd_motion_ungrab();
1167 icon_select_only(NULL);
1168 break;
1169 default:
1170 g_warning("Unsupported action : %d\n", action);
1171 break;
1175 static gint panel_button_release(GtkWidget *widget,
1176 GdkEventButton *event,
1177 Panel *panel)
1179 if (dnd_motion_release(event))
1180 return TRUE;
1182 perform_action(panel, NULL, event);
1184 return TRUE;
1187 static gint panel_button_press(GtkWidget *widget,
1188 GdkEventButton *event,
1189 Panel *panel)
1191 if (dnd_motion_press(panel->window, event))
1192 perform_action(panel, NULL, event);
1194 return TRUE;
1197 static gint icon_button_release(GtkWidget *widget,
1198 GdkEventButton *event,
1199 PanelIcon *pi)
1201 if (pi->socket && event->button == 1)
1202 return FALSE; /* Restart button */
1204 if (dnd_motion_release(event))
1205 return TRUE;
1207 perform_action(pi->panel, pi, event);
1209 return TRUE;
1212 static gint icon_button_press(GtkWidget *widget,
1213 GdkEventButton *event,
1214 PanelIcon *pi)
1216 if (pi->socket && event->button == 1)
1217 return FALSE; /* Restart button */
1219 if (dnd_motion_press(widget, event))
1220 perform_action(pi->panel, pi, event);
1222 return TRUE;
1225 /* Stop windows from maximising over all/part of us */
1226 static void panel_setup_struts(Panel *panel, GtkAllocation *alloc)
1228 int thickness;
1229 struct {
1230 gulong left, right, top, bottom;
1231 gulong left_start_y, left_end_y;
1232 gulong right_start_y, right_end_y;
1233 gulong top_start_x, top_end_x;
1234 gulong bottom_start_x, bottom_end_x;
1235 } strut = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1237 if (panel->avoid == FALSE)
1238 thickness = 2;
1239 else if (panel->side == PANEL_TOP ||
1240 panel->side == PANEL_BOTTOM)
1241 thickness = alloc->height;
1242 else
1243 thickness = alloc->width;
1245 switch (panel->side)
1247 case PANEL_LEFT:
1248 if (!panel->xinerama || !monitor_adjacent[panel->monitor].left)
1250 strut.left = panel->geometry.x + thickness;
1251 strut.left_start_y = panel->geometry.y;
1252 strut.left_end_y = panel->geometry.y +
1253 panel->geometry.height - 1;
1255 /* else there is (part of) a monitor
1256 * to the left */
1257 else
1259 thickness = 0;
1261 break;
1262 case PANEL_RIGHT:
1263 if (!panel->xinerama || !monitor_adjacent[panel->monitor].right)
1265 /* RHS of monitor might not abut edge
1266 * of total virtual screen */
1267 strut.right = screen_width -
1268 panel->geometry.x -
1269 panel->geometry.width +
1270 thickness;
1271 strut.right_start_y = panel->geometry.y;
1272 strut.right_end_y = panel->geometry.y +
1273 panel->geometry.height - 1;
1275 /* else there is (part of) a monitor
1276 * to the right */
1277 else
1279 thickness = 0;
1281 break;
1282 case PANEL_TOP:
1283 if (!panel->xinerama || !monitor_adjacent[panel->monitor].top)
1285 strut.top = panel->geometry.y + thickness;
1286 strut.top_start_x = panel->geometry.x;
1287 strut.top_end_x = panel->geometry.x +
1288 panel->geometry.width - 1;
1290 /* else there is (part of) a monitor above */
1291 else
1293 thickness = 0;
1295 break;
1296 default: /* PANEL_BOTTOM */
1297 if (!panel->xinerama ||
1298 !monitor_adjacent[panel->monitor].bottom)
1300 /* Bottom of monitor might not abut
1301 * edge of total virtual screen */
1302 strut.bottom = screen_height -
1303 panel->geometry.y -
1304 panel->geometry.height +
1305 thickness;
1306 strut.bottom_start_x = panel->geometry.x;
1307 strut.bottom_end_x = panel->geometry.x +
1308 panel->geometry.width - 1;
1310 /* else there is (part of) a monitor below */
1311 else
1313 thickness = 0;
1315 break;
1318 if (thickness)
1320 /* Set full-width strut as well as partial in case
1321 * partial isn't supported by wm */
1322 gdk_property_change(panel->window->window,
1323 gdk_atom_intern("_NET_WM_STRUT",
1324 FALSE),
1325 gdk_atom_intern("CARDINAL", FALSE),
1326 32, GDK_PROP_MODE_REPLACE,
1327 (gchar *) &strut, 4);
1328 gdk_property_change(panel->window->window,
1329 gdk_atom_intern("_NET_WM_STRUT_PARTIAL",
1330 FALSE),
1331 gdk_atom_intern("CARDINAL", FALSE),
1332 32, GDK_PROP_MODE_REPLACE,
1333 (gchar *) &strut, 12);
1335 else
1337 gdk_property_delete(panel->window->window,
1338 gdk_atom_intern("_NET_WM_STRUT_PARTIAL",
1339 FALSE));
1340 gdk_property_delete(panel->window->window,
1341 gdk_atom_intern("_NET_WM_STRUT",
1342 FALSE));
1346 static void reposition_panel(GtkWidget *window,
1347 GtkAllocation *alloc, Panel *panel)
1349 int x = panel->geometry.x;
1350 int y = panel->geometry.y;
1351 PanelSide side = panel->side;
1353 if (side == PANEL_LEFT || side == PANEL_RIGHT)
1355 if (side == PANEL_RIGHT)
1356 x += panel->geometry.width - alloc->width;
1358 if (current_panel[PANEL_TOP])
1360 GtkWidget *win = current_panel[PANEL_TOP]->window;
1361 y += win->allocation.height;
1365 if (side == PANEL_BOTTOM)
1366 y += panel->geometry.height - alloc->height;
1368 gtk_window_move(GTK_WINDOW(panel->window), x, y);
1369 gdk_window_move(panel->window->window, x, y);
1371 if (side == PANEL_BOTTOM || side == PANEL_TOP)
1373 if (current_panel[PANEL_RIGHT])
1374 gtk_widget_queue_resize(
1375 current_panel[PANEL_RIGHT]->window);
1376 if (current_panel[PANEL_LEFT])
1377 gtk_widget_queue_resize(
1378 current_panel[PANEL_LEFT]->window);
1381 panel_setup_struts(panel, alloc);
1384 /* Same as drag_set_dest(), but for panel icons */
1385 static void drag_set_panel_dest(PanelIcon *pi)
1387 GtkWidget *obj = pi->widget;
1389 make_drop_target(pi->widget, 0);
1391 g_signal_connect(obj, "drag_motion", G_CALLBACK(drag_motion), pi);
1392 g_signal_connect(obj, "drag_leave", G_CALLBACK(drag_leave), pi);
1393 g_signal_connect(obj, "drag_end", G_CALLBACK(drag_end), pi);
1396 static gboolean drag_motion(GtkWidget *widget,
1397 GdkDragContext *context,
1398 gint x,
1399 gint y,
1400 guint time,
1401 PanelIcon *pi)
1403 GdkDragAction action = context->suggested_action;
1404 const char *type = NULL;
1405 Icon *icon = (Icon *) pi;
1406 DirItem *item = icon->item;
1407 int panel_x, panel_y;
1409 gdk_window_get_pointer(pi->panel->window->window,
1410 &panel_x, &panel_y, NULL);
1411 motion_may_raise(pi->panel, panel_x, panel_y);
1413 /* Should we scroll the panel when dragging? */
1414 if (motion_state != MOTION_REPOSITION)
1415 if (pi->panel->autoscroll_speed == 0)
1416 may_autoscroll(pi->panel);
1418 if (icon->selected)
1419 goto out; /* Can't drag a selection to itself */
1421 type = dnd_motion_item(context, &item);
1423 if ((context->actions & GDK_ACTION_ASK) && o_dnd_left_menu.int_value
1424 && type != drop_dest_prog)
1426 guint state;
1427 gdk_window_get_pointer(NULL, NULL, NULL, &state);
1428 if (state & GDK_BUTTON1_MASK)
1429 action = GDK_ACTION_ASK;
1432 if (!item)
1433 type = NULL;
1434 out:
1435 /* We actually must pretend to accept the drop, even if the
1436 * directory isn't writeable, so that the spring-opening
1437 * thing works.
1440 /* Don't allow drops to non-writeable directories */
1441 if (o_dnd_spring_open.int_value == FALSE &&
1442 type == drop_dest_dir &&
1443 access(icon->path, W_OK) != 0)
1445 type = NULL;
1448 g_dataset_set_data(context, "drop_dest_type", (gpointer) type);
1449 if (type)
1451 gdk_drag_status(context, action, time);
1452 g_dataset_set_data_full(context, "drop_dest_path",
1453 g_strdup(icon->path), g_free);
1454 if (type == drop_dest_dir)
1455 dnd_spring_load(context, NULL);
1457 if (dnd_highlight && dnd_highlight != pi->widget)
1459 gtk_drag_unhighlight(dnd_highlight);
1460 dnd_highlight = NULL;
1463 if (dnd_highlight == NULL)
1465 gtk_drag_highlight(pi->widget);
1466 dnd_highlight = pi->widget;
1470 return type != NULL;
1474 static void add_uri_list(GtkWidget *widget,
1475 GdkDragContext *context,
1476 gint x,
1477 gint y,
1478 GtkSelectionData *selection_data,
1479 guint info,
1480 guint32 time,
1481 Panel *panel)
1483 gboolean after = FALSE;
1484 GList *uris, *next;
1486 if (!selection_data->data)
1487 return;
1489 g_return_if_fail(selection_data->data[selection_data->length] == '\0');
1491 if (g_object_get_data(G_OBJECT(widget), "after"))
1492 after = TRUE;
1494 uris = uri_list_to_glist(selection_data->data);
1496 for (next = uris; next; next = next->next)
1498 guchar *path;
1500 path = get_local_path((EscapedPath *) next->data);
1502 if (path) {
1503 panel_add_item(panel, path, NULL, after, NULL, NULL, FALSE);
1504 g_free(path);
1508 g_list_free(uris);
1511 static void drag_end(GtkWidget *widget,
1512 GdkDragContext *context,
1513 Icon *icon)
1515 if (tmp_icon_selected)
1517 icon_select_only(NULL);
1518 tmp_icon_selected = FALSE;
1522 static void drag_leave(GtkWidget *widget,
1523 GdkDragContext *context,
1524 guint32 time,
1525 Icon *icon)
1527 panel_drag_leave(widget, context, time, ((PanelIcon *) icon)->panel);
1529 if (dnd_highlight && dnd_highlight == widget)
1531 gtk_drag_unhighlight(dnd_highlight);
1532 dnd_highlight = NULL;
1535 dnd_spring_abort();
1538 /* Create XML icon nodes for these widgets.
1539 * Always frees the widgets list.
1541 static void make_widgets(xmlNodePtr side, GList *widgets)
1543 GList *next;
1545 for (next = widgets; next; next = next->next)
1547 Icon *icon;
1548 xmlNodePtr tree;
1550 icon = g_object_get_data(G_OBJECT(next->data), "icon");
1552 if (!icon)
1554 g_warning("Can't find Icon from widget\n");
1555 continue;
1558 tree = xmlNewTextChild(side, NULL, "icon", icon->src_path);
1560 xmlSetProp(tree, "label", icon->item->leafname);
1561 if (icon->shortcut)
1562 xmlSetProp(tree, "shortcut", icon->shortcut);
1563 if (icon->args)
1564 xmlSetProp(tree, "args", icon->args);
1565 if (icon->locked)
1566 xmlSetProp(tree, "locked", "true");
1569 if (widgets)
1570 g_list_free(widgets);
1573 void panel_save(Panel *panel)
1575 xmlDocPtr doc;
1576 xmlNodePtr root;
1577 xmlNodePtr options;
1578 guchar *save = NULL;
1579 guchar *save_new = NULL;
1581 g_return_if_fail(panel != NULL);
1583 if (strchr(panel->name, '/'))
1584 save = g_strdup(panel->name);
1585 else
1587 guchar *leaf;
1589 leaf = g_strconcat("pan_", panel->name, NULL);
1590 save = choices_find_xdg_path_save(leaf, PROJECT, SITE, TRUE);
1591 g_free(leaf);
1594 if (!save)
1595 return;
1597 doc = xmlNewDoc("1.0");
1598 xmlDocSetRootElement(doc, xmlNewDocNode(doc, NULL, "panel", NULL));
1600 root = xmlDocGetRootElement(doc);
1602 xmlSetProp(root, "side", panel_side_to_name(panel->side));
1604 options = xmlNewChild(root, NULL, "options", NULL);
1605 set_int_prop(options, "style", panel->style);
1606 set_int_prop(options, "width", panel->width);
1607 set_int_prop(options, "avoid", panel->avoid);
1608 set_int_prop(options, "xinerama", panel->xinerama);
1609 set_int_prop(options, "monitor", panel->monitor);
1611 make_widgets(xmlNewChild(root, NULL, "start", NULL),
1612 gtk_container_get_children(GTK_CONTAINER(panel->before)));
1614 make_widgets(xmlNewChild(root, NULL, "end", NULL),
1615 g_list_reverse(gtk_container_get_children(
1616 GTK_CONTAINER(panel->after))));
1618 save_new = g_strconcat(save, ".new", NULL);
1619 if (save_xml_file(doc, save_new) || rename(save_new, save))
1620 delayed_error(_("Error saving panel %s: %s"),
1621 save, g_strerror(errno));
1622 g_free(save_new);
1624 g_free(save);
1625 if (doc)
1626 xmlFreeDoc(doc);
1629 /* Create a frame widget which can be used to add icons to the panel */
1630 static GtkWidget *make_insert_frame(Panel *panel)
1632 GtkWidget *frame;
1633 GtkTargetEntry target_table[] = {
1634 {"text/uri-list", 0, TARGET_URI_LIST},
1637 frame = gtk_frame_new(NULL);
1638 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
1639 gtk_widget_set_size_request(frame, 16, 16);
1641 g_signal_connect(frame, "drag-motion",
1642 G_CALLBACK(insert_drag_motion), panel);
1643 g_signal_connect(frame, "drag-leave",
1644 G_CALLBACK(panel_drag_leave), panel);
1646 g_signal_connect(frame, "drag-data-received",
1647 G_CALLBACK(add_uri_list), panel);
1648 gtk_drag_dest_set(frame,
1649 GTK_DEST_DEFAULT_ALL,
1650 target_table,
1651 sizeof(target_table) / sizeof(*target_table),
1652 GDK_ACTION_COPY);
1654 return frame;
1657 static gboolean enter_icon(GtkWidget *widget,
1658 GdkEventCrossing *event,
1659 Icon *icon)
1661 icon_may_update(icon);
1662 panel_icon_set_tip((PanelIcon *) icon);
1664 return FALSE;
1667 static gint panel_leave_event(GtkWidget *widget,
1668 GdkEventCrossing *event,
1669 Panel *panel)
1671 GdkWindow *pinboard;
1673 if (event->mode != GDK_CROSSING_NORMAL)
1674 return FALSE; /* Grab for menu, DnD, etc */
1676 if (panel_keep_below(panel, TRUE))
1678 /* Shouldn't need this as well as keep_below but some WMs don't
1679 * automatically lower as soon as the hint is set */
1680 pinboard = pinboard_get_window();
1681 window_put_just_above(panel->window->window, pinboard);
1684 return FALSE;
1687 /* If (x, y) is at the edge of the panel then raise */
1688 static void motion_may_raise(Panel *panel, int x, int y)
1690 gboolean raise;
1692 if (panel->side == PANEL_TOP)
1693 raise = y == 0;
1694 else if (panel->side == PANEL_BOTTOM)
1695 raise = y == panel->window->allocation.height - 1;
1696 else if (panel->side == PANEL_LEFT)
1697 raise = x == 0;
1698 else
1699 raise = x == panel->window->allocation.width - 1;
1701 if (raise && panel_keep_below(panel, FALSE))
1703 /* Shouldn't need this as well as keep_below but some WMs don't
1704 * automatically raise as soon as the hint is set */
1705 gdk_window_raise(panel->window->window);
1709 static gboolean may_autoscroll(Panel *panel)
1711 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1712 gint max, panel_x, panel_y, delta, new;
1714 if (panel->adj->upper <= panel->adj->page_size)
1715 goto stop_scrolling; /* Can see everything already */
1717 gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
1719 if (horz)
1721 delta = panel_x;
1722 max = panel->window->allocation.width;
1723 if (panel_y < 0 || panel_y > panel->window->allocation.height)
1724 goto stop_scrolling; /* Not over the panel */
1726 else
1728 delta = panel_y;
1729 max = panel->window->allocation.height;
1730 if (panel_x < 0 || panel_x > panel->window->allocation.width)
1731 goto stop_scrolling; /* Not over the panel */
1734 if (delta >= 20 && delta <= max - 20)
1735 goto stop_scrolling; /* Not at either end */
1737 panel->autoscroll_speed = MIN(panel->autoscroll_speed + 2, 200);
1739 new = panel->adj->value - ((delta < 20) ? panel->autoscroll_speed
1740 : -panel->autoscroll_speed);
1741 new = CLAMP(new, 0, panel->adj->upper - panel->adj->page_size);
1742 gtk_adjustment_set_value(panel->adj, new);
1744 panel->autoscroll_to = g_timeout_add(40,
1745 (GSourceFunc) may_autoscroll, panel);
1747 return FALSE;
1749 stop_scrolling:
1750 panel->autoscroll_speed = 0;
1751 return FALSE;
1754 static gint panel_motion_event(GtkWidget *widget,
1755 GdkEventMotion *event,
1756 Panel *panel)
1758 motion_may_raise(panel, event->x, event->y);
1760 if (motion_state != MOTION_REPOSITION)
1761 if (panel->autoscroll_speed == 0)
1762 may_autoscroll(panel);
1764 return FALSE;
1767 static gint icon_motion_event(GtkWidget *widget,
1768 GdkEventMotion *event,
1769 PanelIcon *pi)
1771 Panel *panel = pi->panel;
1772 GList *list, *me;
1773 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1774 int val;
1775 int dir = 0;
1777 if (motion_state == MOTION_READY_FOR_DND)
1779 if (dnd_motion_moved(event))
1780 start_drag(pi, event);
1781 return TRUE;
1783 else if (motion_state != MOTION_REPOSITION)
1784 return FALSE;
1786 list = gtk_container_get_children(GTK_CONTAINER(panel->before));
1787 list = g_list_append(list, NULL); /* The gap in the middle */
1788 list = g_list_concat(list,
1789 gtk_container_get_children(GTK_CONTAINER(panel->after)));
1790 me = g_list_find(list, widget);
1792 g_return_val_if_fail(me != NULL, TRUE);
1794 val = horz ? event->x_root : event->y_root;
1796 if (me->prev)
1798 GtkWidget *prev;
1799 int x, y;
1801 if (me->prev->data)
1802 prev = GTK_WIDGET(me->prev->data);
1803 else
1804 prev = panel->gap;
1806 gdk_window_get_origin(prev->window, &x, &y);
1808 if (val <= (horz ? x : y))
1809 dir = -1;
1812 if (dir == 0 && me->next)
1814 GtkWidget *next;
1815 int x, y, w, h;
1817 if (me->next->data)
1818 next = GTK_WIDGET(me->next->data);
1819 else
1820 next = panel->gap;
1822 gdk_window_get_origin(next->window, &x, &y);
1824 gdk_drawable_get_size(next->window, &w, &h);
1826 x += w;
1827 y += h;
1829 if (val >= (horz ? x : y)-1)
1831 if (next == panel->gap)
1832 dir = +2;
1833 else
1834 dir = +1;
1838 if (dir)
1839 reposition_icon(pi, g_list_index(list, widget) + dir);
1841 return TRUE;
1844 static void reposition_icon_on_side(GtkWidget *side, GtkWidget *widget,
1845 int index)
1847 GList *list;
1849 list = gtk_container_get_children(GTK_CONTAINER(side));
1851 /* Want to move icon to the list in the given 'side'. Is it there
1852 * already?
1855 if (!g_list_find(list, widget))
1857 /* No, reparent */
1858 gtk_grab_remove(widget);
1859 gtk_widget_reparent(widget, side);
1860 dnd_motion_grab_pointer();
1861 gtk_grab_add(widget);
1864 gtk_box_reorder_child(GTK_BOX(side), widget, index);
1866 g_list_free(list);
1869 /* Move icon to this index in the complete widget list.
1870 * 0 makes the icon the left-most icon. The gap in the middle has
1871 * an index number, which allows you to specify that the icon should
1872 * go on the left or right side.
1874 static void reposition_icon(PanelIcon *pi, int index)
1876 Panel *panel = pi->panel;
1877 GtkWidget *widget = pi->widget;
1878 GList *list;
1879 int before_len;
1881 list = gtk_container_get_children(GTK_CONTAINER(panel->before));
1882 before_len = g_list_length(list);
1883 g_list_free(list);
1885 if (index <= before_len)
1886 reposition_icon_on_side(panel->before, widget, index);
1887 else
1888 reposition_icon_on_side(panel->after, widget,
1889 index - (before_len + 1));
1891 panel_save(panel);
1894 static void start_drag(PanelIcon *pi, GdkEventMotion *event)
1896 GtkWidget *widget = pi->widget;
1897 Icon *icon = (Icon *) pi;
1899 if (!icon->selected)
1901 if (event->state & GDK_BUTTON1_MASK)
1903 /* Select just this one */
1904 icon_select_only(icon);
1905 tmp_icon_selected = TRUE;
1907 else
1908 icon_set_selected(icon, TRUE);
1911 g_return_if_fail(icon_selection != NULL);
1913 if (icon_selection->next == NULL)
1914 drag_one_item(widget, event, icon->path, icon->item, NULL);
1915 else
1917 guchar *uri_list;
1919 uri_list = icon_create_uri_list();
1920 drag_selection(widget, event, uri_list);
1921 g_free(uri_list);
1925 static void applet_died(GtkWidget *socket)
1927 gboolean never_plugged;
1929 never_plugged = (!g_object_get_data(G_OBJECT(socket), "lost_plug"))
1930 && !GTK_SOCKET(socket)->plug_window;
1932 if (never_plugged)
1934 report_error(
1935 _("Applet quit without ever creating a widget!"));
1936 gtk_widget_destroy(socket);
1939 gtk_widget_unref(socket);
1942 static void socket_destroyed(GtkWidget *socket, GtkWidget *widget)
1944 g_object_set_data(G_OBJECT(socket), "lost_plug", "yes");
1946 gtk_widget_unref(socket);
1948 gtk_widget_destroy(widget); /* Remove from panel */
1950 if (!closing_panel)
1951 panel_save(g_object_get_data(G_OBJECT(socket), "panel"));
1954 /* Try to run this applet.
1955 * Cases:
1957 * - No executable AppletRun:
1958 * icon->socket == NULL (unchanged) on return.
1960 * Otherwise, create socket (setting icon->socket) and ref it twice.
1962 * - AppletRun quits without connecting a plug:
1963 * On child death lost_plug is unset and socket is empty.
1964 * Unref socket.
1965 * Report error and destroy widget (to 'socket destroyed').
1967 * - AppletRun quits while plug is in socket:
1968 * Unref socket once. Socket will be destroyed later.
1970 * - Socket is destroyed.
1971 * Set lost_plug = "yes" and remove widget from panel.
1972 * Unref socket.
1974 static void run_applet(PanelIcon *pi)
1976 GError *error = NULL;
1977 char *argv[3];
1978 gint pid;
1979 Icon *icon = (Icon *) pi;
1981 argv[0] = (char *) make_path(icon->path, "AppletRun");
1983 if (access(argv[0], X_OK) != 0)
1984 return;
1986 pi->socket = gtk_socket_new();
1988 gtk_container_add(GTK_CONTAINER(pi->widget), pi->socket);
1989 gtk_widget_show_all(pi->socket);
1990 gtk_widget_realize(pi->socket);
1992 /* Always get button-2 events so we can drag */
1993 XGrabButton(gdk_display, Button2, AnyModifier,
1994 GDK_WINDOW_XWINDOW(pi->socket->window),
1995 False,
1996 ButtonPressMask | ButtonReleaseMask | Button2MotionMask,
1997 GrabModeAsync, /* Pointer */
1998 GrabModeAsync, /* Keyboard */
1999 None, None);
2002 gchar *pos;
2003 PanelSide side = pi->panel->side;
2005 /* Set a hint to let applets position their menus correctly */
2006 pos = g_strdup_printf("%s,%d",
2007 panel_side_to_name(side), MENU_MARGIN(side));
2008 gdk_property_change(pi->socket->window,
2009 gdk_atom_intern("_ROX_PANEL_MENU_POS", FALSE),
2010 gdk_atom_intern("STRING", FALSE),
2011 8, GDK_PROP_MODE_REPLACE,
2012 pos, strlen(pos));
2013 g_free(pos);
2015 /* Ensure that the properties are set before starting the
2016 * applet.
2018 gdk_flush();
2021 g_object_set_data(G_OBJECT(pi->widget), "icon", pi);
2022 g_object_set_data(G_OBJECT(pi->socket), "panel", pi->panel);
2024 argv[1] = g_strdup_printf("%ld",
2025 GDK_WINDOW_XWINDOW(pi->socket->window));
2026 argv[2] = NULL;
2028 if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
2029 NULL, NULL, &pid, &error))
2031 delayed_error(_("Error running applet:\n%s"), error->message);
2032 g_error_free(error);
2033 gtk_widget_destroy(pi->socket);
2034 pi->socket = NULL;
2036 else
2038 gtk_widget_ref(pi->socket);
2039 on_child_death(pid, (CallbackFn) applet_died, pi->socket);
2041 gtk_widget_ref(pi->socket);
2042 g_signal_connect(pi->socket, "destroy",
2043 G_CALLBACK(socket_destroyed), pi->widget);
2046 g_free(argv[1]);
2049 static void panel_post_resize(GtkWidget *win, GtkRequisition *req, Panel *panel)
2051 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
2053 req->width = panel->geometry.width;
2054 req->height += EDGE_WIDTH;
2056 else
2058 int h = panel->geometry.height;
2060 if (current_panel[PANEL_TOP])
2062 GtkWidget *win = current_panel[PANEL_TOP]->window;
2063 h -= win->allocation.height;
2066 if (current_panel[PANEL_BOTTOM])
2068 GtkWidget *win = current_panel[PANEL_BOTTOM]->window;
2069 h -= win->allocation.height;
2072 req->height = h;
2073 req->width += EDGE_WIDTH;
2077 static void update_side(GtkWidget *side)
2079 GList *kids, *next;
2081 kids = gtk_container_get_children(GTK_CONTAINER(side));
2082 for (next = kids; next; next = next->next)
2084 PanelIcon *pi;
2085 pi = g_object_get_data(next->data, "icon");
2086 panel_icon_set_tip(pi);
2088 g_list_free(kids);
2091 /* Tips or style has changed -- update everything on this panel */
2092 static void panel_set_style(Panel *panel)
2094 update_side(panel->before);
2095 update_side(panel->after);
2096 gtk_widget_queue_resize(panel->window);
2099 static gboolean recreate_panels(char **names)
2101 int i;
2103 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
2105 if (names[i])
2107 panel_new(names[i], i);
2108 g_free(names[i]);
2112 g_free(names);
2114 return FALSE;
2117 static void update_side_size(GtkWidget *side)
2119 GList *kids, *next;
2121 kids = gtk_container_get_children(GTK_CONTAINER(side));
2122 for (next = kids; next; next = next->next)
2124 PanelIcon *pi;
2125 pi = g_object_get_data(next->data, "icon");
2126 gtk_widget_queue_resize(pi->widget);
2128 g_list_free(kids);
2131 /* Update panel size and redraw */
2132 static void panel_update(Panel *panel)
2134 update_side_size(panel->before);
2135 update_side_size(panel->after);
2136 gtk_widget_queue_resize(panel->window);
2137 gtk_widget_queue_draw(panel->window);
2140 static void panel_style_changed(void)
2142 int i;
2144 if (o_override_redirect.has_changed)
2146 gchar **names;
2148 names = g_new(char *, PANEL_NUMBER_OF_SIDES);
2150 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
2152 Panel *panel = current_panel[i];
2153 names[i] = panel ? g_strdup(panel->name) : NULL;
2154 panel_new(NULL, i);
2157 g_idle_add((GtkFunction) recreate_panels, names);
2161 static gboolean draw_panel_edge(GtkWidget *widget, GdkEventExpose *event,
2162 Panel *panel)
2164 int x, y, width, height;
2166 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
2168 width = panel->geometry.width;
2169 height = EDGE_WIDTH;
2171 x = 0;
2172 if (panel->side == PANEL_BOTTOM)
2173 y = 0;
2174 else
2175 y = widget->allocation.height - EDGE_WIDTH;
2177 else
2179 width = EDGE_WIDTH;
2180 height = panel->geometry.height;
2182 y = 0;
2183 if (panel->side == PANEL_RIGHT)
2184 x = 0;
2185 else
2186 x = widget->allocation.width - EDGE_WIDTH;
2189 gdk_draw_rectangle(widget->window,
2190 widget->style->fg_gc[GTK_STATE_NORMAL], TRUE,
2191 x, y, width, height);
2193 return FALSE;
2196 static gpointer parent_class;
2198 static void panel_icon_destroy(Icon *icon)
2200 PanelIcon *pi = (PanelIcon *) icon;
2202 g_return_if_fail(pi != NULL);
2204 if (pi->image)
2205 g_object_unref(pi->image);
2207 g_return_if_fail(pi->widget != NULL);
2209 gtk_widget_destroy(pi->widget);
2212 static void panel_remove_items(void)
2214 Panel *panel;
2216 g_return_if_fail(icon_selection != NULL);
2218 panel = ((PanelIcon *) icon_selection->data)->panel;
2220 while (icon_selection)
2221 icon_destroy((Icon *) icon_selection->data);
2223 panel_save(panel);
2226 /* Icon's size, shape or appearance has changed - update the display */
2227 static void panel_icon_redraw(Icon *icon)
2229 PanelIcon *pi = (PanelIcon *) icon;
2231 gtk_widget_set_state(pi->widget,
2232 icon->selected ? GTK_STATE_SELECTED
2233 : GTK_STATE_NORMAL);
2235 /* Will regenerate the scaled icon from the new image */
2236 gtk_widget_queue_resize(pi->widget);
2238 panel_icon_set_tip((PanelIcon *) icon);
2241 static void panel_icon_update(Icon *icon)
2243 PanelIcon *pi = (PanelIcon *) icon;
2245 gtk_widget_queue_draw(pi->widget);
2246 gtk_label_set_text(GTK_LABEL(pi->label), icon->item->leafname);
2247 panel_save(pi->panel);
2250 /* The point of this is to clear the selection if the existing icons
2251 * aren't from the same panel...
2253 static gboolean panel_icon_same_group(Icon *icon, Icon *other)
2255 if (IS_PANEL_ICON(other))
2257 PanelIcon *a = (PanelIcon *) icon;
2258 PanelIcon *b = (PanelIcon *) other;
2260 return a->panel == b->panel;
2262 else
2263 return FALSE;
2266 static void panel_icon_class_init(gpointer gclass, gpointer data)
2268 IconClass *icon = (IconClass *) gclass;
2270 parent_class = g_type_class_peek_parent(gclass);
2272 icon->destroy = panel_icon_destroy;
2273 icon->redraw = panel_icon_redraw;
2274 icon->update = panel_icon_update;
2275 icon->remove_items = panel_remove_items;
2276 icon->same_group = panel_icon_same_group;
2277 icon->wink = panel_icon_wink;
2280 static void panel_icon_init(GTypeInstance *object, gpointer gclass)
2282 PanelIcon *pi = (PanelIcon *) object;
2284 pi->widget = NULL;
2285 pi->image = NULL;
2286 pi->label = NULL;
2287 pi->socket = NULL;
2288 pi->style = TEXT_UNDER_ICON;
2291 static GType panel_icon_get_type(void)
2293 static GType type = 0;
2295 if (!type)
2297 static const GTypeInfo info =
2299 sizeof (PanelIconClass),
2300 NULL, /* base_init */
2301 NULL, /* base_finalise */
2302 panel_icon_class_init,
2303 NULL, /* class_finalise */
2304 NULL, /* class_data */
2305 sizeof(PanelIcon),
2306 0, /* n_preallocs */
2307 panel_icon_init
2310 type = g_type_register_static(icon_get_type(),
2311 "PanelIcon", &info, 0);
2314 return type;
2317 static PanelIcon *panel_icon_new(Panel *panel,
2318 const char *pathname,
2319 const char *name)
2321 PanelIcon *pi;
2322 Icon *icon;
2324 pi = g_object_new(panel_icon_get_type(), NULL);
2325 icon = (Icon *) pi;
2327 icon_set_path(icon, pathname, name);
2328 pi->panel = panel;
2330 return pi;
2333 static gboolean panel_want_show_text(PanelIcon *pi)
2335 Icon *icon = (Icon *) pi;
2336 Panel *panel = pi->panel;
2338 if (!icon->item->leafname[0])
2339 return FALSE;
2341 if (panel->style == SHOW_BOTH)
2342 return TRUE;
2343 if (panel->style == SHOW_ICON)
2344 return FALSE;
2346 if (icon->item->flags & ITEM_FLAG_APPDIR)
2347 return FALSE;
2349 if (EXECUTABLE_FILE(icon->item))
2350 return FALSE;
2352 return TRUE;
2355 static void xinerama_sensitive(GladeXML *glade, gboolean sensitive)
2357 gtk_widget_set_sensitive(
2358 glade_xml_get_widget(glade, "panel_xinerama_monitor"),
2359 sensitive);
2362 inline static Panel *panel_from_opts_widget(GtkWidget *widget)
2364 return g_object_get_data(G_OBJECT(gtk_widget_get_toplevel(widget)),
2365 "rox-panel");
2368 static void panel_style_radio_toggled(GtkToggleButton *widget, int style)
2370 Panel *panel;
2372 if (!gtk_toggle_button_get_active(widget))
2373 return;
2374 panel = panel_from_opts_widget(GTK_WIDGET(widget));
2375 if (style != panel->style)
2377 panel->style = style;
2378 panel_set_style(panel);
2379 panel_save(panel);
2383 static void panel_xinerama_changed(Panel *panel)
2385 panel_update_geometry(panel);
2386 reposition_panel(panel->window, &panel->window->allocation, panel);
2387 gtk_widget_queue_resize(panel->window);
2388 panel_save(panel);
2391 static void panel_side_radio_toggled(GtkWidget *widget, PanelSide new_side)
2393 Panel *panel;
2394 PanelSide old_side;
2395 char *name, *other_side_name;
2397 if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
2398 return;
2400 panel = panel_from_opts_widget(widget);
2401 old_side = panel->side;
2402 if (new_side == old_side)
2403 return;
2405 name = g_strdup(panel->name);
2406 other_side_name = current_panel[new_side]
2407 ? g_strdup(current_panel[new_side]->name)
2408 : NULL;
2410 panel_new(name, new_side);
2411 g_object_set_data(G_OBJECT(gtk_widget_get_toplevel(widget)),
2412 "rox-panel", current_panel[new_side]);
2413 panel_new(other_side_name, old_side);
2415 g_free(name);
2416 g_free(other_side_name);
2419 static void panel_style_radio_0_toggled_cb(GtkToggleButton *widget)
2421 panel_style_radio_toggled(widget, 0);
2424 static void panel_style_radio_1_toggled_cb(GtkToggleButton *widget)
2426 panel_style_radio_toggled(widget, 1);
2429 static void panel_style_radio_2_toggled_cb(GtkToggleButton *widget)
2431 panel_style_radio_toggled(widget, 2);
2434 static void panel_width_changed_cb(GtkSpinButton *widget)
2436 Panel *panel = panel_from_opts_widget(GTK_WIDGET(widget));
2437 int width = gtk_spin_button_get_value_as_int(widget);
2439 if (width != panel->width)
2441 panel->width = width;
2442 panel_update(panel);
2443 panel_save(panel);
2447 static void panel_avoid_toggled_cb(GtkToggleButton *widget)
2449 Panel *panel = panel_from_opts_widget(GTK_WIDGET(widget));
2450 gboolean avoid = gtk_toggle_button_get_active(widget);
2452 if (avoid != panel->avoid)
2454 panel->avoid = avoid;
2455 panel_setup_struts(panel, &panel->window->allocation);
2456 panel_save(panel);
2460 static void panel_xinerama_confine_toggled_cb(GtkWidget *widget)
2462 Panel *panel = panel_from_opts_widget(widget);
2463 gboolean xinerama = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
2465 xinerama_sensitive(glade_get_widget_tree(gtk_widget_get_toplevel(widget)),
2466 xinerama);
2467 if (xinerama != panel->xinerama)
2469 panel->xinerama = xinerama;
2470 panel_xinerama_changed(panel);
2474 static void panel_xinerama_monitor_changed_cb(GtkSpinButton *widget)
2476 Panel *panel = panel_from_opts_widget(GTK_WIDGET(widget));
2477 int monitor = gtk_spin_button_get_value_as_int(widget);
2479 if (monitor != panel->monitor)
2481 panel->monitor = monitor;
2482 panel_xinerama_changed(panel);
2486 static void panel_pos_top_toggled_cb(GtkWidget *widget)
2488 panel_side_radio_toggled(widget, PANEL_TOP);
2491 static void panel_pos_bottom_toggled_cb(GtkWidget *widget)
2493 panel_side_radio_toggled(widget, PANEL_BOTTOM);
2496 static void panel_pos_left_toggled_cb(GtkWidget *widget)
2498 panel_side_radio_toggled(widget, PANEL_LEFT);
2501 static void panel_pos_right_toggled_cb(GtkWidget *widget)
2503 panel_side_radio_toggled(widget, PANEL_RIGHT);
2506 #define CONNECT_GLADE_CB(glade, handler) \
2507 glade_xml_signal_connect(glade, #handler, G_CALLBACK(handler))
2509 static void panel_connect_dialog_signal_handlers(GladeXML *glade)
2511 CONNECT_GLADE_CB(glade, gtk_widget_destroy);
2512 CONNECT_GLADE_CB(glade, panel_style_radio_0_toggled_cb);
2513 CONNECT_GLADE_CB(glade, panel_style_radio_1_toggled_cb);
2514 CONNECT_GLADE_CB(glade, panel_style_radio_2_toggled_cb);
2515 CONNECT_GLADE_CB(glade, panel_width_changed_cb);
2516 CONNECT_GLADE_CB(glade, panel_avoid_toggled_cb);
2517 CONNECT_GLADE_CB(glade, panel_xinerama_confine_toggled_cb);
2518 CONNECT_GLADE_CB(glade, panel_xinerama_monitor_changed_cb);
2519 CONNECT_GLADE_CB(glade, panel_pos_top_toggled_cb);
2520 CONNECT_GLADE_CB(glade, panel_pos_bottom_toggled_cb);
2521 CONNECT_GLADE_CB(glade, panel_pos_left_toggled_cb);
2522 CONNECT_GLADE_CB(glade, panel_pos_right_toggled_cb);
2525 static void panel_setup_options_dialog(GladeXML *glade, Panel *panel)
2527 char *wnm;
2528 const char *pos_radio;
2530 wnm = g_strdup_printf("panel_style_radio_%d", panel->style);
2531 gtk_toggle_button_set_active(
2532 GTK_TOGGLE_BUTTON(glade_xml_get_widget(glade, wnm)),
2533 TRUE);
2534 g_free(wnm);
2535 gtk_spin_button_set_value(
2536 GTK_SPIN_BUTTON(glade_xml_get_widget(glade, "panel_width")),
2537 panel->width);
2538 gtk_toggle_button_set_active(
2539 GTK_TOGGLE_BUTTON(glade_xml_get_widget(glade, "panel_avoid")),
2540 panel->avoid);
2541 gtk_toggle_button_set_active(
2542 GTK_TOGGLE_BUTTON(glade_xml_get_widget(glade,
2543 "panel_xinerama_confine")),
2544 panel->xinerama);
2545 gtk_spin_button_set_adjustment(
2546 GTK_SPIN_BUTTON(glade_xml_get_widget(glade, "panel_xinerama_monitor")),
2547 GTK_ADJUSTMENT(gtk_adjustment_new(MAX(0, panel->monitor),
2548 0, n_monitors - 1, 1, 10, 1)));
2549 xinerama_sensitive(glade, panel->xinerama);
2550 switch (panel->side)
2552 case PANEL_TOP:
2553 pos_radio = "panel_pos_top";
2554 break;
2555 case PANEL_BOTTOM:
2556 pos_radio = "panel_pos_bottom";
2557 break;
2558 case PANEL_LEFT:
2559 pos_radio = "panel_pos_left";
2560 break;
2561 case PANEL_RIGHT:
2562 pos_radio = "panel_pos_right";
2563 break;
2564 default:
2565 pos_radio = NULL;
2566 break;
2568 g_return_if_fail(pos_radio != NULL);
2569 gtk_toggle_button_set_active(
2570 GTK_TOGGLE_BUTTON(glade_xml_get_widget(glade, pos_radio)),
2571 TRUE);
2574 static void panel_show_options(Panel *panel)
2576 GtkWidget *dialog;
2577 gboolean already_showing = FALSE;
2578 GladeXML *glade = NULL;
2580 if (panel_options_dialog)
2582 dialog = panel_options_dialog;
2583 already_showing = TRUE;
2584 glade = glade_get_widget_tree(dialog);
2586 else
2588 glade = get_glade_xml("Panel Options");
2589 dialog = glade_xml_get_widget(glade, "Panel Options");
2590 panel_options_dialog = dialog;
2591 g_signal_connect(dialog, "destroy",
2592 G_CALLBACK(gtk_widget_destroyed),
2593 &panel_options_dialog);
2594 panel_connect_dialog_signal_handlers(glade);
2596 g_object_set_data(G_OBJECT(panel_options_dialog), "rox-panel", panel);
2598 panel_setup_options_dialog(glade, panel);
2600 if (already_showing)
2602 GtkWindow *win = GTK_WINDOW(dialog);
2604 gtk_widget_hide(dialog);
2605 /* This extra set_position() should ensure it moves to new position
2606 * under pointer */
2607 gtk_window_set_position(win, GTK_WIN_POS_CENTER_ALWAYS);
2608 gtk_window_set_position(win, GTK_WIN_POS_MOUSE);
2609 gtk_window_present(win);
2611 else
2613 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
2614 gtk_widget_show_all(dialog);
2618 static void panel_position_menu(GtkMenu *menu, gint *x, gint *y,
2619 gboolean *push_in, gpointer data)
2621 int *pos = (int *) data;
2622 GtkRequisition requisition;
2623 int margin = pos[2];
2624 int mon = pos[3];
2625 int mon_right = monitor_geom[mon].x +
2626 monitor_geom[mon].width;
2627 int mon_bottom = monitor_geom[mon].y +
2628 monitor_geom[mon].height;
2630 gtk_widget_size_request(GTK_WIDGET(menu), &requisition);
2632 if (pos[0] == -1)
2633 *x = mon_right - margin - requisition.width;
2634 else if (pos[0] == -2)
2635 *x = monitor_geom[mon].x + margin;
2636 else
2637 *x = pos[0] - (requisition.width >> 2);
2639 if (pos[1] == -1)
2640 *y = mon_bottom - margin - requisition.height;
2641 else if (pos[1] == -2)
2642 *y = monitor_geom[mon].y + margin;
2643 else
2644 *y = pos[1] - (requisition.height >> 2);
2646 *x = CLAMP(*x, 0, mon_right - requisition.width);
2647 *y = CLAMP(*y, 0, mon_bottom - requisition.height);
2649 *push_in = FALSE;
2652 static void panel_remove_callback(PanelSide side)
2654 GtkWidget *dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL,
2655 GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL,
2656 _("Are you sure you want to remove this panel from the desktop?"));
2658 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
2659 gtk_window_set_title(GTK_WINDOW(dialog), _("Remove Panel"));
2660 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
2661 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
2662 panel_new(NULL, side);
2663 gtk_widget_destroy(dialog);
2666 static void panel_show_menu(GdkEventButton *event, PanelIcon *pi, Panel *panel)
2668 GtkWidget *option_item;
2669 GtkWidget *del_item;
2670 PanelSide side = panel->side;
2671 int pos[4];
2673 pos[0] = event->x_root;
2674 pos[1] = event->y_root;
2675 pos[2] = MENU_MARGIN(side);
2676 /* FIXME: Should we read screen from event's window rather than
2677 * using default? */
2678 pos[3] = gdk_screen_get_monitor_at_point(
2679 gdk_screen_get_default(),
2680 event->x_root, event->y_root);
2682 option_item = gtk_image_menu_item_new_with_label(_("Panel Options..."));
2683 g_signal_connect_swapped(option_item, "activate",
2684 G_CALLBACK(panel_show_options), panel);
2686 del_item = gtk_image_menu_item_new_with_label(_("Remove Panel"));
2687 add_stock_to_menu_item(del_item, GTK_STOCK_REMOVE);
2688 g_signal_connect_swapped(del_item, "activate",
2689 G_CALLBACK(panel_remove_callback), GINT_TO_POINTER(side));
2691 icon_prepare_menu((Icon *) pi, option_item, del_item, NULL);
2693 if (side == PANEL_LEFT)
2694 pos[0] = -2;
2695 else if (side == PANEL_RIGHT)
2696 pos[0] = -1;
2698 if (side == PANEL_TOP)
2699 pos[1] = -2;
2700 else if (side == PANEL_BOTTOM)
2701 pos[1] = -1;
2703 gtk_menu_popup(GTK_MENU(icon_menu), NULL, NULL,
2704 panel_position_menu,
2705 (gpointer) pos, event->button, event->time);
2708 /* Note: also called from icon handler */
2709 static gboolean panel_drag_motion(GtkWidget *widget,
2710 GdkDragContext *context,
2711 gint x,
2712 gint y,
2713 guint time,
2714 Panel *panel)
2716 int panel_x, panel_y;
2718 gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
2720 motion_may_raise(panel, panel_x, panel_y);
2721 gdk_drag_status(context, 0, time);
2723 return TRUE;
2726 static gboolean insert_drag_motion(GtkWidget *widget,
2727 GdkDragContext *context,
2728 gint x,
2729 gint y,
2730 guint time,
2731 Panel *panel)
2733 int panel_x, panel_y;
2735 gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
2736 motion_may_raise(panel, panel_x, panel_y);
2738 return FALSE;
2741 /* Note: also called from icon handler */
2742 static void panel_drag_leave(GtkWidget *widget,
2743 GdkDragContext *context,
2744 guint32 time,
2745 Panel *panel)
2747 GdkWindow *pinboard, *window;
2748 GtkAllocation *alloc = &panel->window->allocation;
2749 int x, y;
2751 window = panel->window->window;
2752 gdk_window_get_pointer(window, &x, &y, NULL);
2753 if ((x < 0 || y < 0 || x > alloc->width || y > alloc->height) &&
2754 panel_keep_below(panel, TRUE))
2756 /* Shouldn't need this as well as keep_below but some WMs don't
2757 * automatically lower as soon as the hint is set */
2758 pinboard = pinboard_get_window();
2759 window_put_just_above(panel->window->window, pinboard);
2763 static void panel_update_geometry(Panel *panel)
2765 if (panel->xinerama && panel->monitor >= n_monitors)
2767 g_warning(_("Xinerama monitor %d unavailable"), panel->monitor);
2768 panel->xinerama = FALSE;
2771 if (panel->xinerama)
2773 panel->geometry = monitor_geom[panel->monitor];
2775 else
2777 panel->geometry.x = panel->geometry.y = 0;
2778 panel->geometry.width = screen_width;
2779 panel->geometry.height = screen_height;
2783 static GList *build_monitor_number(Option *option, xmlNode *node, guchar *label)
2785 GtkObject *adj;
2787 adj = gtk_adjustment_new(MAX(0, panel_monitor),
2788 0, n_monitors - 1, 1, 10, 1);
2789 return build_numentry_base(option, node, label, GTK_ADJUSTMENT(adj));
2792 static const char *panel_side_to_translated_name(PanelSide side)
2794 switch (side)
2796 case PANEL_TOP:
2797 return _("Top");
2798 case PANEL_BOTTOM:
2799 return _("Bottom");
2800 case PANEL_LEFT:
2801 return _("Left");
2802 case PANEL_RIGHT:
2803 return _("Right");
2804 case PANEL_DEFAULT_SIDE:
2805 return _("Default");
2806 default:
2807 break;
2809 return _("Unknown side");
2812 const char *panel_side_to_name(PanelSide side)
2814 switch (side)
2816 case PANEL_TOP:
2817 return "Top";
2818 case PANEL_BOTTOM:
2819 return "Bottom";
2820 case PANEL_LEFT:
2821 return "Left";
2822 case PANEL_RIGHT:
2823 return "Right";
2824 case PANEL_DEFAULT_SIDE:
2825 return "Default";
2826 default:
2827 break;
2829 return "UnknownSide";
2832 /* Returns PANEL_NUMBER_OF_SIDES if name is invalid */
2833 PanelSide panel_name_to_side(gchar *side)
2835 if (strcmp(side, "Top") == 0)
2836 return PANEL_TOP;
2837 else if (strcmp(side, "Bottom") == 0)
2838 return PANEL_BOTTOM;
2839 else if (strcmp(side, "Left") == 0)
2840 return PANEL_LEFT;
2841 else if (strcmp(side, "Right") == 0)
2842 return PANEL_RIGHT;
2843 else
2844 g_warning("Unknown panel side '%s'", side);
2845 return PANEL_NUMBER_OF_SIDES;
2848 static void panel_add_callback(PanelSide side)
2850 g_return_if_fail(current_panel[side] == NULL);
2851 panel_new(panel_side_to_name(side), side);
2854 GtkWidget *panel_new_panel_submenu(void)
2856 GtkWidget *menu = gtk_menu_new();
2857 PanelSide side;
2859 for (side = 0; side < PANEL_NUMBER_OF_SIDES; ++side)
2861 GtkWidget *item = gtk_menu_item_new_with_label(
2862 panel_side_to_translated_name(side));
2864 g_signal_connect_swapped(item, "activate",
2865 G_CALLBACK(panel_add_callback), GINT_TO_POINTER(side));
2866 gtk_widget_set_sensitive(item, current_panel[side] == NULL);
2867 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2868 gtk_widget_show(item);
2870 return menu;
2873 /* Set the panel to be above/below other windows (setting = TRUE for below),
2874 * and return TRUE.
2876 * If o_panel_on_top is set then do nothing and return FALSE.
2878 static gboolean panel_keep_below(Panel *panel, gboolean setting)
2880 if (!o_panel_on_top.int_value)
2882 keep_below(panel->window->window, setting);
2883 return TRUE;
2885 return FALSE;