Converted README to markdown
[rox-filer.git] / ROX-Filer / src / panel.c
blobff6b6a1ec424f859948927d7de7642b9ddbcc6af
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;
219 static Option o_panel_obey_workarea;
221 static gint panel_monitor = -1;
223 static int closing_panel = 0; /* Don't panel_save; destroying! */
225 /****************************************************************
226 * EXTERNAL INTERFACE *
227 ****************************************************************/
229 void panel_init(void)
231 option_add_int(&o_panel_style, "panel_style", SHOW_APPS_SMALL);
232 option_add_int(&o_panel_width, "panel_width", 52);
234 option_add_int(&o_panel_xinerama, "panel_xinerama", 0);
235 option_add_int(&o_panel_monitor, "panel_monitor", 0);
237 option_add_int(&o_panel_avoid, "panel_avoid", TRUE);
238 option_add_int(&o_panel_is_dock, "panel_is_dock", TRUE);
239 option_add_int(&o_panel_on_top, "panel_on_top", FALSE);
241 option_add_int(&o_panel_obey_workarea, "panel_obey_workarea", FALSE);
243 option_add_notify(panel_style_changed);
245 option_register_widget("monitor-number", build_monitor_number);
248 /* Return a free edge for a new panel.
249 * If no edge is free, returns PANEL_BOTTOM.
251 static PanelSide find_free_side()
253 if (!current_panel[PANEL_BOTTOM])
254 return PANEL_BOTTOM;
256 if (!current_panel[PANEL_TOP])
257 return PANEL_TOP;
259 if (!current_panel[PANEL_LEFT])
260 return PANEL_LEFT;
262 if (!current_panel[PANEL_RIGHT])
263 return PANEL_RIGHT;
265 return PANEL_BOTTOM;
268 /* Returns TRUE and sets *target if the property exists, otherwise returns
269 * FALSE and leaves *target unchanged */
270 static gboolean get_int_prop(xmlNodePtr node, const char *name, int *target)
272 char *prop = xmlGetProp(node, name);
274 if (prop)
276 *target = atoi(prop);
277 g_free(prop);
278 return TRUE;
280 return FALSE;
283 static void set_int_prop(xmlNodePtr node, const char *name, int value)
285 char prop[16];
287 sprintf(prop, "%d", value);
288 xmlSetProp(node, name, prop);
291 static void panel_load_options_from_xml(Panel *panel, xmlDocPtr doc)
293 xmlNodePtr root;
294 xmlNodePtr options;
296 root = xmlDocGetRootElement(doc);
297 options = get_subnode(root, NULL, "options");
298 if (!options)
299 return;
300 get_int_prop(options, "style", &panel->style);
301 get_int_prop(options, "width", &panel->width);
302 get_int_prop(options, "avoid", &panel->avoid);
303 get_int_prop(options, "xinerama", &panel->xinerama);
304 get_int_prop(options, "monitor", &panel->monitor);
307 static void save_panels(void)
309 char *filename = choices_find_xdg_path_save("panels",
310 "ROX-Filer", "rox.sourceforge.net", TRUE);
311 char *tmp = g_strconcat(filename, ".new", NULL);
312 FILE *fp = fopen(tmp, "w");
314 if (fp)
316 PanelSide n;
318 for (n = 0; n < PANEL_NUMBER_OF_SIDES; ++n)
320 if (current_panel[n])
321 fprintf(fp, "%s\n", current_panel[n]->name);
323 fclose(fp);
324 if (rename(tmp, filename))
325 g_critical(_("Unable to replace '%s'"), filename);
327 else
329 g_critical(_("Unable to save '%s'"), tmp);
331 g_free(tmp);
332 g_free(filename);
335 /* 'name' may be NULL or "" to remove the panel */
336 Panel *panel_new(const gchar *name, PanelSide side)
338 guchar *load_path;
339 Panel *panel;
340 GtkWidget *vp, *box, *frame, *align;
341 xmlDocPtr panel_doc = NULL;
342 gboolean need_resave = FALSE;
344 g_return_val_if_fail(side == PANEL_DEFAULT_SIDE ||
345 (side >= 0 && side < PANEL_NUMBER_OF_SIDES), NULL);
346 g_return_val_if_fail(loading_panel == NULL, NULL);
348 if (name && *name == '\0')
349 name = NULL;
351 if (!name)
352 load_path = NULL;
353 else if (strchr(name, '/'))
354 load_path = g_strdup(name);
355 else
357 guchar *leaf;
359 leaf = g_strconcat("pan_", name, NULL);
360 load_path = choices_find_xdg_path_load(leaf, PROJECT, SITE);
361 g_free(leaf);
364 if (load_path && access(load_path, F_OK) == 0)
366 char *saved_side;
367 xmlNodePtr root;
369 panel_doc = xmlParseFile(load_path);
370 root = xmlDocGetRootElement(panel_doc);
372 saved_side = xmlGetProp(root, "side");
373 if (saved_side)
375 PanelSide old_side;
376 old_side = panel_name_to_side(saved_side);
377 g_free(saved_side);
379 if (side == PANEL_DEFAULT_SIDE)
380 side = old_side;
381 else if (side != old_side)
382 need_resave = TRUE;
384 else
385 need_resave = TRUE;
388 if (side == PANEL_DEFAULT_SIDE)
389 side = find_free_side();
391 if (current_panel[side])
393 if (name)
394 number_of_windows++;
395 closing_panel++;
396 gtk_widget_destroy(current_panel[side]->window);
397 closing_panel--;
398 if (name)
399 number_of_windows--;
402 if (name == NULL || *name == '\0')
404 save_panels();
405 return NULL;
408 panel = g_new(Panel, 1);
409 panel->name = g_strdup(name);
410 panel->side = side;
411 panel->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
412 panel->autoscroll_speed = 0;
414 /* These are fallbacks from legacy global options */
415 panel->style = o_panel_style.int_value;
416 panel->width = o_panel_width.int_value;
417 panel->xinerama = o_panel_xinerama.int_value;
418 panel->monitor = o_panel_monitor.int_value;
419 panel->avoid = o_panel_avoid.int_value;
421 /* Now try to load options from this panel's XML */
422 if (panel_doc)
424 panel_load_options_from_xml(panel, panel_doc);
426 else
428 /* Otherwise ensure old settings are migrated */
429 need_resave = TRUE;
432 panel_update_geometry(panel);
434 gtk_window_set_resizable(GTK_WINDOW(panel->window), FALSE);
435 gtk_window_set_wmclass(GTK_WINDOW(panel->window), "ROX-Panel", PROJECT);
436 gtk_widget_set_name(panel->window, "rox-panel");
437 gtk_widget_set_events(panel->window,
438 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
439 GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
441 /* We make the panel a drop target only so that we can auto-raise! */
442 gtk_drag_dest_set(panel->window, 0, NULL, 0, GDK_ACTION_PRIVATE);
443 g_signal_connect(panel->window, "drag_leave",
444 G_CALLBACK(panel_drag_leave), panel);
445 g_signal_connect(panel->window, "drag_motion",
446 G_CALLBACK(panel_drag_motion), panel);
448 g_signal_connect(panel->window, "delete-event",
449 G_CALLBACK(panel_delete), panel);
450 g_signal_connect(panel->window, "destroy",
451 G_CALLBACK(panel_destroyed), panel);
452 g_signal_connect(panel->window, "button_press_event",
453 G_CALLBACK(panel_button_press), panel);
454 g_signal_connect(panel->window, "button_release_event",
455 G_CALLBACK(panel_button_release), panel);
456 g_signal_connect(panel->window, "motion-notify-event",
457 G_CALLBACK(panel_motion_event), panel);
458 g_signal_connect(panel->window, "leave-notify-event",
459 G_CALLBACK(panel_leave_event), panel);
461 if (panel->side == PANEL_RIGHT)
462 align = gtk_alignment_new(1.0, 0.0, 0.0, 1.0);
463 else if (panel->side == PANEL_BOTTOM)
464 align = gtk_alignment_new(0.0, 1.0, 1.0, 0.0);
465 else if (panel->side == PANEL_TOP)
466 align = gtk_alignment_new(0.0, 0.0, 1.0, 0.0);
467 else
468 align = gtk_alignment_new(0.0, 0.0, 0.0, 1.0);
470 gtk_container_add(GTK_CONTAINER(panel->window), align);
472 vp = gtk_viewport_new(NULL, NULL);
473 gtk_container_set_resize_mode(GTK_CONTAINER(vp), GTK_RESIZE_PARENT);
474 gtk_viewport_set_shadow_type(GTK_VIEWPORT(vp), GTK_SHADOW_NONE);
475 gtk_container_add(GTK_CONTAINER(align), vp);
477 g_signal_connect(align, "expose-event",
478 G_CALLBACK(draw_panel_edge), panel);
480 if (side == PANEL_TOP || side == PANEL_BOTTOM)
482 panel->adj = gtk_viewport_get_hadjustment(GTK_VIEWPORT(vp));
483 box = gtk_hbox_new(FALSE, 0);
484 panel->before = gtk_hbox_new(FALSE, 0);
485 panel->after = gtk_hbox_new(FALSE, 0);
487 else
489 panel->adj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(vp));
490 box = gtk_vbox_new(FALSE, 0);
491 panel->before = gtk_vbox_new(FALSE, 0);
492 panel->after = gtk_vbox_new(FALSE, 0);
495 gtk_container_add(GTK_CONTAINER(vp), box);
496 gtk_box_pack_start(GTK_BOX(box), panel->before, FALSE, TRUE, 0);
497 gtk_box_pack_end(GTK_BOX(box), panel->after, FALSE, TRUE, 0);
499 frame = make_insert_frame(panel);
500 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
502 /* This is used so that we can find the middle easily! */
503 panel->gap = gtk_event_box_new();
504 gtk_box_pack_start(GTK_BOX(box), panel->gap, FALSE, FALSE, 0);
506 frame = make_insert_frame(panel);
507 g_object_set_data(G_OBJECT(frame), "after", "yes");
508 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
510 if (o_panel_is_dock.int_value)
511 gtk_window_set_type_hint(GTK_WINDOW(panel->window),
512 GDK_WINDOW_TYPE_HINT_DOCK);
514 gtk_widget_realize(panel->window);
515 make_panel_window(panel->window);
516 gtk_window_stick(GTK_WINDOW(panel->window));
518 gtk_widget_show_all(align);
520 loading_panel = panel;
521 if (panel_doc)
523 panel_load_from_xml(panel, panel_doc);
524 xmlFreeDoc(panel_doc);
526 if (need_resave)
527 panel_save(panel);
529 else if (load_path)
531 parse_file(load_path, pan_from_file);
532 info_message(_("Your old panel file has been "
533 "converted to the new XML format."));
534 panel_save(panel);
536 else
538 /* Don't scare users with an empty panel... */
539 guchar *apps;
541 panel_add_item(panel, "~", "Home", FALSE, NULL, NULL, FALSE);
543 apps = pathdup(make_path(app_dir, ".."));
544 if (apps)
546 panel_add_item(panel, apps, "Apps", FALSE, NULL, NULL, FALSE);
547 g_free(apps);
550 loading_panel = NULL;
551 g_free(load_path);
553 current_panel[side] = panel;
555 gtk_widget_queue_resize(box);
556 g_signal_connect(panel->window, "size-request",
557 G_CALLBACK(panel_post_resize), panel);
558 g_signal_connect(panel->window, "size-allocate",
559 G_CALLBACK(reposition_panel), panel);
561 number_of_windows++;
562 gdk_window_lower(panel->window->window);
563 gtk_widget_show(panel->window);
564 /* This has no effect until after window is showing; GTK+ bug? */
565 if (panel_keep_below(panel, TRUE))
567 GdkWindow *pinboard;
569 pinboard = pinboard_get_window();
570 /* (if pinboard is NULL, will go right to the back) */
571 window_put_just_above(panel->window->window, pinboard);
574 save_panels();
576 return panel;
579 /* Externally visible function to add an item to a panel */
580 gboolean panel_add(PanelSide side,
581 const gchar *path, const gchar *label, gboolean after, const gchar *shortcut, const gchar *args,
582 gboolean locked)
584 g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, FALSE);
586 g_return_val_if_fail(current_panel[side] != NULL, FALSE);
588 panel_add_item(current_panel[side], path, label, after, shortcut, args, locked);
590 return TRUE;
593 /* Add the area covered by the panels to the region */
594 void panel_mark_used(GdkRegion *used)
596 int i;
598 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
600 Panel *panel = current_panel[i];
601 GdkRectangle rect;
603 if (!panel)
604 continue;
606 gdk_window_get_root_origin(panel->window->window,
607 &rect.x, &rect.y);
608 rect.width = panel->window->allocation.width;
609 rect.height = panel->window->allocation.height;
611 gdk_region_union_with_rect(used, &rect);
615 /* On xrandr screen size changes, update all panels */
616 void panel_update_size(void)
618 int i;
620 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
622 if (current_panel[i])
624 panel_update_geometry(current_panel[i]);
625 reposition_panel(current_panel[i]->window,
626 &current_panel[i]->window->allocation,
627 current_panel[i]);
628 gtk_widget_queue_resize(current_panel[i]->window);
633 /****************************************************************
634 * INTERNAL FUNCTIONS *
635 ****************************************************************/
637 /* User has tried to close the panel via the window manager - confirm */
638 static int panel_delete(GtkWidget *widget, GdkEvent *event, Panel *panel)
640 return !confirm(_("You have tried to close a panel via the window "
641 "manager - I usually find that this is accidental... "
642 "really close?"),
643 GTK_STOCK_CLOSE, NULL);
646 static void panel_destroyed(GtkWidget *widget, Panel *panel)
648 if (panel_options_dialog)
650 Panel *dlg_panel = g_object_get_data(G_OBJECT(panel_options_dialog),
651 "rox-panel");
653 if (dlg_panel == panel)
654 gtk_widget_destroy(panel_options_dialog);
657 if (current_panel[panel->side] == panel)
658 current_panel[panel->side] = NULL;
660 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
662 if (current_panel[PANEL_RIGHT])
663 gtk_widget_queue_resize(
664 current_panel[PANEL_RIGHT]->window);
665 if (current_panel[PANEL_LEFT])
666 gtk_widget_queue_resize(
667 current_panel[PANEL_LEFT]->window);
670 if (panel->autoscroll_speed)
671 g_source_remove(panel->autoscroll_to);
673 g_free(panel->name);
674 g_free(panel);
676 one_less_window();
679 static void panel_load_side(Panel *panel, xmlNodePtr side, gboolean after)
681 xmlNodePtr node;
682 char *label, *path, *shortcut, *args, *tmp;
683 gboolean locked;
685 for (node = side->xmlChildrenNode; node; node = node->next)
687 if (node->type != XML_ELEMENT_NODE)
688 continue;
689 if (strcmp(node->name, "icon") != 0)
690 continue;
692 label = xmlGetProp(node, "label");
693 if (!label)
694 label = g_strdup("<missing label>");
695 path = xmlNodeGetContent(node);
696 if (!path)
697 path = g_strdup("<missing path>");
698 shortcut = xmlGetProp(node, "shortcut");
699 args = xmlGetProp(node, "args");
700 tmp = xmlGetProp(node, "locked");
701 if (tmp)
703 locked = text_to_boolean(tmp, FALSE);
704 g_free(tmp);
706 else
707 locked = FALSE;
709 panel_add_item(panel, path, label, after, shortcut, args, locked);
711 g_free(path);
712 g_free(label);
713 g_free(shortcut);
714 g_free(args);
718 /* Create one panel icon for each icon in the doc */
719 static void panel_load_from_xml(Panel *panel, xmlDocPtr doc)
721 xmlNodePtr root;
723 root = xmlDocGetRootElement(doc);
724 panel_load_side(panel, get_subnode(root, NULL, "start"), FALSE);
725 panel_load_side(panel, get_subnode(root, NULL, "end"), TRUE);
728 /* Called for each line in the config file while loading a new panel */
729 static const char *pan_from_file(gchar *line)
731 gchar *sep, *leaf;
733 g_return_val_if_fail(line != NULL, NULL);
734 g_return_val_if_fail(loading_panel != NULL, NULL);
736 if (*line == '\0')
737 return NULL;
739 sep = strpbrk(line, "<>");
740 if (!sep)
741 return _("Missing < or > in panel config file");
743 if (sep != line)
744 leaf = g_strndup(line, sep - line);
745 else
746 leaf = NULL;
748 panel_add_item(loading_panel, sep + 1, leaf, sep[0] == '>',
749 NULL, NULL, FALSE);
751 g_free(leaf);
753 return NULL;
756 static gboolean icon_pointer_in(GtkWidget *widget,
757 GdkEventCrossing *event,
758 Icon *icon)
760 gtk_widget_set_state(widget,
761 icon->selected ? GTK_STATE_SELECTED : GTK_STATE_PRELIGHT);
763 return 0;
766 static gboolean icon_pointer_out(GtkWidget *widget,
767 GdkEventCrossing *event,
768 Icon *icon)
770 gtk_widget_set_state(widget,
771 icon->selected ? GTK_STATE_SELECTED : GTK_STATE_NORMAL);
773 return 0;
776 static void panel_icon_destroyed(PanelIcon *pi)
778 g_return_if_fail(pi->widget != NULL);
780 pi->widget = NULL;
782 g_object_unref(pi);
785 /* Set the tooltip AND hide/show the label */
786 static void panel_icon_set_tip(PanelIcon *pi)
788 XMLwrapper *ai;
789 xmlNode *node;
790 Icon *icon = (Icon *) pi;
792 g_return_if_fail(pi != NULL);
794 if (pi->label)
796 if (panel_want_show_text(pi))
797 gtk_widget_show(pi->label);
798 else
799 gtk_widget_hide(pi->label);
802 if (pi->socket)
803 ai = NULL;
804 else
805 ai = appinfo_get(icon->path, icon->item);
807 if (ai && ((node = xml_get_section(ai, NULL, "Summary"))))
809 guchar *str;
810 str = xmlNodeListGetString(node->doc,
811 node->xmlChildrenNode, 1);
812 if (str)
814 gtk_tooltips_set_tip(tooltips, pi->widget, str, NULL);
815 g_free(str);
818 else if ((!panel_want_show_text(pi)) && !pi->socket)
820 if (icon->item->leafname && icon->item->leafname[0])
821 gtk_tooltips_set_tip(tooltips, pi->widget,
822 icon->item->leafname, NULL);
824 else
825 gtk_tooltips_set_tip(tooltips, pi->widget, NULL, NULL);
827 if (ai)
828 g_object_unref(ai);
831 /* Add an icon with this path to the panel. If after is TRUE then the
832 * icon is added to the right/bottom end of the panel.
834 * If name is NULL a suitable name is taken from path.
836 static void panel_add_item(Panel *panel,
837 const gchar *path,
838 const gchar *name,
839 gboolean after,
840 const gchar *shortcut,
841 const gchar *args,
842 gboolean locked)
844 GtkWidget *widget;
845 PanelIcon *pi;
846 Icon *icon;
848 g_return_if_fail(panel != NULL);
849 g_return_if_fail(path != NULL);
851 widget = gtk_event_box_new();
852 gtk_widget_set_events(widget,
853 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
854 GDK_BUTTON3_MOTION_MASK |
855 GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
856 GDK_BUTTON_RELEASE_MASK);
858 gtk_box_pack_start(GTK_BOX(after ? panel->after : panel->before),
859 widget, FALSE, TRUE, 0);
860 if (after)
861 gtk_box_reorder_child(GTK_BOX(panel->after), widget, 0);
863 gtk_widget_realize(widget);
865 pi = panel_icon_new(panel, path, name);
866 icon = (Icon *) pi;
868 /* Widget takes the initial ref of Icon */
869 g_object_set_data(G_OBJECT(widget), "icon", pi);
871 pi->widget = widget;
872 g_object_ref(widget);
874 gtk_widget_set_name(pi->widget, "panel-icon");
876 g_signal_connect_swapped(widget, "destroy",
877 G_CALLBACK(panel_icon_destroyed), pi);
879 if (icon->item->base_type == TYPE_DIRECTORY)
880 run_applet(pi);
882 g_signal_connect(widget, "button_release_event",
883 G_CALLBACK(icon_button_release), pi);
884 g_signal_connect(widget, "button_press_event",
885 G_CALLBACK(icon_button_press), pi);
886 g_signal_connect(widget, "motion-notify-event",
887 G_CALLBACK(icon_motion_event), pi);
888 g_signal_connect(widget, "enter-notify-event",
889 G_CALLBACK(icon_pointer_in), pi);
890 g_signal_connect(widget, "leave-notify-event",
891 G_CALLBACK(icon_pointer_out), pi);
893 if (!pi->socket)
895 g_signal_connect(widget, "enter-notify-event",
896 G_CALLBACK(enter_icon), pi);
897 g_signal_connect_after(widget, "expose_event",
898 G_CALLBACK(expose_icon), pi);
899 g_signal_connect(widget, "drag_data_get",
900 G_CALLBACK(drag_data_get), NULL);
902 g_signal_connect(widget, "size_request",
903 G_CALLBACK(size_request), pi);
905 drag_set_panel_dest(pi);
907 pi->label = gtk_label_new(icon->item->leafname);
908 gtk_container_add(GTK_CONTAINER(pi->widget), pi->label);
909 gtk_misc_set_alignment(GTK_MISC(pi->label), 0.5, 1);
910 gtk_misc_set_padding(GTK_MISC(pi->label), 1, 2);
913 icon_set_shortcut(icon, shortcut);
914 icon_set_arguments(icon, args);
915 icon->locked = locked;
917 if (!loading_panel)
918 panel_save(panel);
920 panel_icon_set_tip(pi);
921 gtk_widget_show(widget);
924 static gboolean remove_item_from_side(GtkWidget *container, const gchar *path,
925 const gchar *label)
927 GList *kids, *next;
928 gboolean found = FALSE;
930 kids = gtk_container_get_children(GTK_CONTAINER(container));
932 for (next = kids; next; next = next->next)
934 Icon *icon;
935 icon = g_object_get_data(G_OBJECT(next->data), "icon");
936 if (!icon)
937 continue;
939 if ((!path || strcmp(path, icon->src_path) == 0) &&
940 (!label || strcmp(label, icon->item->leafname)==0))
942 icon->locked = FALSE;
943 icon_destroy(icon);
944 found = TRUE;
945 break;
949 g_list_free(kids);
951 return found;
954 /* Remove an item with this path. If more than one item matches, only
955 * one is removed. If label is not NULL then it must also match the item.
956 * Returns TRUE if an item was successfully removed.
958 gboolean panel_remove_item(PanelSide side, const gchar *path,
959 const gchar *label)
961 Panel *panel;
963 g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, FALSE);
965 g_return_val_if_fail(path != NULL || label != NULL, FALSE);
967 panel = current_panel[side];
968 if (!panel)
970 g_warning("No panel on this side of the screen!");
971 return FALSE;
974 if (remove_item_from_side(panel->before, path, label) ||
975 remove_item_from_side(panel->after, path, label))
977 panel_save(panel);
978 panel_update(panel);
979 return TRUE;
982 g_warning("Panel item path='%s', label='%s' not found", path, label);
983 return FALSE;
986 /* Called when Gtk+ wants to know how much space an icon needs.
987 * 'req' is already big enough for the label, if shown.
989 static void size_request(GtkWidget *widget, GtkRequisition *req, PanelIcon *pi)
991 Icon *icon = (Icon *) pi;
992 gboolean horz = (pi->panel->side == PANEL_TOP ||
993 pi->panel->side == PANEL_BOTTOM);
994 int max_width = 100;
995 int max_height = 100;
996 int image_width, image_height;
997 Panel *panel = pi->panel;
999 if (horz)
1000 max_height = panel->width - req->height;
1001 else
1002 max_width = MAX(panel->width, req->width);
1004 /* TODO: really need to recreate? */
1005 if (pi->image)
1006 g_object_unref(pi->image);
1008 pi->image = scale_pixbuf(di_image(icon->item)->src_pixbuf,
1009 MAX(20, max_width), MAX(20, max_height));
1011 image_width = gdk_pixbuf_get_width(pi->image);
1012 image_height = gdk_pixbuf_get_height(pi->image);
1014 if (req->height > 0 && max_height < req->height)
1016 pi->style = TEXT_BESIDE_ICON;
1017 req->width += image_width;
1018 req->height = MAX(req->height, image_height);
1019 gtk_misc_set_alignment(GTK_MISC(pi->label), 1, 0.5);
1021 else
1023 pi->style = TEXT_UNDER_ICON;
1024 req->width = MAX(req->width, image_width);
1025 req->height += image_height;
1026 gtk_misc_set_alignment(GTK_MISC(pi->label), 0.5, 1);
1029 if (horz)
1030 req->width += PANEL_ICON_SPACING;
1031 else
1032 req->height += PANEL_ICON_SPACING;
1035 static gint expose_icon(GtkWidget *widget,
1036 GdkEventExpose *event,
1037 PanelIcon *pi)
1039 return draw_icon(widget, &event->area, pi);
1042 static gint draw_icon(GtkWidget *widget, GdkRectangle *badarea, PanelIcon *pi)
1044 GdkRectangle area;
1045 int width, height;
1046 Icon *icon = (Icon *) pi;
1047 int image_x;
1048 int image_y;
1049 GdkPixbuf *image;
1050 int text_height = 0;
1052 gdk_drawable_get_size(widget->window, &area.width, &area.height);
1054 if (panel_want_show_text(pi))
1055 text_height = pi->label->requisition.height;
1057 g_return_val_if_fail(pi->image != NULL, FALSE);
1059 image = pi->image;
1061 width = gdk_pixbuf_get_width(image);
1062 height = gdk_pixbuf_get_height(image);
1064 if (pi->style == TEXT_UNDER_ICON)
1066 image_x = (area.width - width) >> 1;
1067 image_y = (area.height - height - text_height) >> 1;
1069 else
1071 image_x = PANEL_ICON_SPACING - 2;
1072 image_y = (area.height - height) >> 1;
1075 gdk_pixbuf_render_to_drawable_alpha(
1076 image,
1077 widget->window,
1078 0, 0, /* src */
1079 image_x, image_y, /* dest */
1080 width, height,
1081 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
1082 GDK_RGB_DITHER_NORMAL, 0, 0);
1084 if (icon->item->flags & ITEM_FLAG_SYMLINK)
1086 draw_emblem_on_icon(widget->window, widget->style,
1087 ROX_STOCK_SYMLINK, &image_x, image_y+2);
1089 if (icon->item->flags & ITEM_FLAG_MOUNT_POINT)
1091 draw_emblem_on_icon(widget->window, widget->style,
1092 icon->item->flags & ITEM_FLAG_MOUNTED
1093 ? ROX_STOCK_MOUNTED
1094 : ROX_STOCK_MOUNT,
1095 &image_x, image_y+2);
1097 return FALSE;
1100 static void panel_icon_wink(Icon *icon)
1102 PanelIcon *pi = (PanelIcon *) icon;
1104 wink_widget(pi->widget);
1107 /* icon may be NULL if the event is on the background */
1108 static void perform_action(Panel *panel, PanelIcon *pi, GdkEventButton *event)
1110 BindAction action;
1111 Icon *icon = (Icon *) pi;
1113 action = bind_lookup_bev(icon ? BIND_PANEL_ICON : BIND_PANEL, event);
1115 if (pi && pi->socket)
1116 if (action != ACT_POPUP_MENU && action != ACT_MOVE_ICON)
1117 return;
1119 switch (action)
1121 case ACT_OPEN_ITEM:
1122 dnd_motion_ungrab();
1123 wink_widget(pi->widget);
1124 icon_run(icon);
1125 break;
1126 case ACT_EDIT_ITEM:
1127 dnd_motion_ungrab();
1128 wink_widget(pi->widget);
1129 run_diritem(icon->path, icon->item, NULL, NULL, TRUE);
1130 break;
1131 case ACT_POPUP_MENU:
1132 dnd_motion_ungrab();
1133 panel_show_menu(event, pi, panel);
1134 break;
1135 case ACT_MOVE_ICON:
1136 dnd_motion_start(MOTION_REPOSITION);
1137 break;
1138 case ACT_PRIME_AND_SELECT:
1139 if (!icon->selected)
1140 icon_select_only(icon);
1141 dnd_motion_start(MOTION_READY_FOR_DND);
1142 break;
1143 case ACT_PRIME_AND_TOGGLE:
1144 icon_set_selected(icon, !icon->selected);
1145 dnd_motion_start(MOTION_READY_FOR_DND);
1146 break;
1147 case ACT_PRIME_FOR_DND:
1148 dnd_motion_start(MOTION_READY_FOR_DND);
1149 break;
1150 case ACT_TOGGLE_SELECTED:
1151 icon_set_selected(icon, !icon->selected);
1152 break;
1153 case ACT_SELECT_EXCL:
1154 icon_set_selected(icon, TRUE);
1155 break;
1156 case ACT_IGNORE:
1157 break;
1158 case ACT_CLEAR_SELECTION:
1159 dnd_motion_ungrab();
1160 icon_select_only(NULL);
1161 break;
1162 default:
1163 g_warning("Unsupported action : %d\n", action);
1164 break;
1168 static gint panel_button_release(GtkWidget *widget,
1169 GdkEventButton *event,
1170 Panel *panel)
1172 if (dnd_motion_release(event))
1173 return TRUE;
1175 perform_action(panel, NULL, event);
1177 return TRUE;
1180 static gint panel_button_press(GtkWidget *widget,
1181 GdkEventButton *event,
1182 Panel *panel)
1184 if (dnd_motion_press(panel->window, event))
1185 perform_action(panel, NULL, event);
1187 return TRUE;
1190 static gint icon_button_release(GtkWidget *widget,
1191 GdkEventButton *event,
1192 PanelIcon *pi)
1194 if (pi->socket && event->button == 1)
1195 return FALSE; /* Restart button */
1197 if (dnd_motion_release(event))
1198 return TRUE;
1200 perform_action(pi->panel, pi, event);
1202 return TRUE;
1205 static gint icon_button_press(GtkWidget *widget,
1206 GdkEventButton *event,
1207 PanelIcon *pi)
1209 if (pi->socket && event->button == 1)
1210 return FALSE; /* Restart button */
1212 if (dnd_motion_press(widget, event))
1213 perform_action(pi->panel, pi, event);
1215 return TRUE;
1218 /* Stop windows from maximising over all/part of us */
1219 static void panel_setup_struts(Panel *panel, GtkAllocation *alloc)
1221 int thickness;
1222 struct {
1223 gulong left, right, top, bottom;
1224 gulong left_start_y, left_end_y;
1225 gulong right_start_y, right_end_y;
1226 gulong top_start_x, top_end_x;
1227 gulong bottom_start_x, bottom_end_x;
1228 } strut = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1230 if (panel->avoid == FALSE)
1231 thickness = 2;
1232 else if (panel->side == PANEL_TOP ||
1233 panel->side == PANEL_BOTTOM)
1234 thickness = alloc->height;
1235 else
1236 thickness = alloc->width;
1238 switch (panel->side)
1240 case PANEL_LEFT:
1241 if (!panel->xinerama || !monitor_adjacent[panel->monitor].left)
1243 strut.left = panel->geometry.x + thickness;
1244 strut.left_start_y = panel->geometry.y;
1245 strut.left_end_y = panel->geometry.y +
1246 panel->geometry.height - 1;
1248 /* else there is (part of) a monitor
1249 * to the left */
1250 else
1252 thickness = 0;
1254 break;
1255 case PANEL_RIGHT:
1256 if (!panel->xinerama || !monitor_adjacent[panel->monitor].right)
1258 /* RHS of monitor might not abut edge
1259 * of total virtual screen */
1260 strut.right = screen_width -
1261 panel->geometry.x -
1262 panel->geometry.width +
1263 thickness;
1264 strut.right_start_y = panel->geometry.y;
1265 strut.right_end_y = panel->geometry.y +
1266 panel->geometry.height - 1;
1268 /* else there is (part of) a monitor
1269 * to the right */
1270 else
1272 thickness = 0;
1274 break;
1275 case PANEL_TOP:
1276 if (!panel->xinerama || !monitor_adjacent[panel->monitor].top)
1278 strut.top = panel->geometry.y + thickness;
1279 strut.top_start_x = panel->geometry.x;
1280 strut.top_end_x = panel->geometry.x +
1281 panel->geometry.width - 1;
1283 /* else there is (part of) a monitor above */
1284 else
1286 thickness = 0;
1288 break;
1289 default: /* PANEL_BOTTOM */
1290 if (!panel->xinerama ||
1291 !monitor_adjacent[panel->monitor].bottom)
1293 /* Bottom of monitor might not abut
1294 * edge of total virtual screen */
1295 strut.bottom = screen_height -
1296 panel->geometry.y -
1297 panel->geometry.height +
1298 thickness;
1299 strut.bottom_start_x = panel->geometry.x;
1300 strut.bottom_end_x = panel->geometry.x +
1301 panel->geometry.width - 1;
1303 /* else there is (part of) a monitor below */
1304 else
1306 thickness = 0;
1308 break;
1311 if (thickness)
1313 /* Set full-width strut as well as partial in case
1314 * partial isn't supported by wm */
1315 gdk_property_change(panel->window->window,
1316 gdk_atom_intern("_NET_WM_STRUT",
1317 FALSE),
1318 gdk_atom_intern("CARDINAL", FALSE),
1319 32, GDK_PROP_MODE_REPLACE,
1320 (gchar *) &strut, 4);
1321 gdk_property_change(panel->window->window,
1322 gdk_atom_intern("_NET_WM_STRUT_PARTIAL",
1323 FALSE),
1324 gdk_atom_intern("CARDINAL", FALSE),
1325 32, GDK_PROP_MODE_REPLACE,
1326 (gchar *) &strut, 12);
1328 else
1330 gdk_property_delete(panel->window->window,
1331 gdk_atom_intern("_NET_WM_STRUT_PARTIAL",
1332 FALSE));
1333 gdk_property_delete(panel->window->window,
1334 gdk_atom_intern("_NET_WM_STRUT",
1335 FALSE));
1339 static void reposition_panel(GtkWidget *window,
1340 GtkAllocation *alloc, Panel *panel)
1342 int x = panel->geometry.x;
1343 int y = panel->geometry.y;
1344 PanelSide side = panel->side;
1346 if (side == PANEL_LEFT || side == PANEL_RIGHT)
1348 if (side == PANEL_RIGHT)
1349 x += panel->geometry.width - alloc->width;
1351 if (current_panel[PANEL_TOP])
1353 GtkWidget *win = current_panel[PANEL_TOP]->window;
1354 y += win->allocation.height;
1358 if (side == PANEL_BOTTOM)
1359 y += panel->geometry.height - alloc->height;
1361 gtk_window_move(GTK_WINDOW(panel->window), x, y);
1362 gdk_window_move(panel->window->window, x, y);
1364 if (side == PANEL_BOTTOM || side == PANEL_TOP)
1366 if (current_panel[PANEL_RIGHT])
1367 gtk_widget_queue_resize(
1368 current_panel[PANEL_RIGHT]->window);
1369 if (current_panel[PANEL_LEFT])
1370 gtk_widget_queue_resize(
1371 current_panel[PANEL_LEFT]->window);
1374 panel_setup_struts(panel, alloc);
1377 /* Same as drag_set_dest(), but for panel icons */
1378 static void drag_set_panel_dest(PanelIcon *pi)
1380 GtkWidget *obj = pi->widget;
1382 make_drop_target(pi->widget, 0);
1384 g_signal_connect(obj, "drag_motion", G_CALLBACK(drag_motion), pi);
1385 g_signal_connect(obj, "drag_leave", G_CALLBACK(drag_leave), pi);
1386 g_signal_connect(obj, "drag_end", G_CALLBACK(drag_end), pi);
1389 static gboolean drag_motion(GtkWidget *widget,
1390 GdkDragContext *context,
1391 gint x,
1392 gint y,
1393 guint time,
1394 PanelIcon *pi)
1396 GdkDragAction action = context->suggested_action;
1397 const char *type = NULL;
1398 Icon *icon = (Icon *) pi;
1399 DirItem *item = icon->item;
1400 int panel_x, panel_y;
1402 gdk_window_get_pointer(pi->panel->window->window,
1403 &panel_x, &panel_y, NULL);
1404 motion_may_raise(pi->panel, panel_x, panel_y);
1406 /* Should we scroll the panel when dragging? */
1407 if (motion_state != MOTION_REPOSITION)
1408 if (pi->panel->autoscroll_speed == 0)
1409 may_autoscroll(pi->panel);
1411 if (icon->selected)
1412 goto out; /* Can't drag a selection to itself */
1414 type = dnd_motion_item(context, &item);
1416 if ((context->actions & GDK_ACTION_ASK) && o_dnd_left_menu.int_value
1417 && type != drop_dest_prog)
1419 guint state;
1420 gdk_window_get_pointer(NULL, NULL, NULL, &state);
1421 if (state & GDK_BUTTON1_MASK)
1422 action = GDK_ACTION_ASK;
1425 if (!item)
1426 type = NULL;
1427 out:
1428 /* We actually must pretend to accept the drop, even if the
1429 * directory isn't writeable, so that the spring-opening
1430 * thing works.
1433 /* Don't allow drops to non-writeable directories */
1434 if (o_dnd_spring_open.int_value == FALSE &&
1435 type == drop_dest_dir &&
1436 access(icon->path, W_OK) != 0)
1438 type = NULL;
1441 g_dataset_set_data(context, "drop_dest_type", (gpointer) type);
1442 if (type)
1444 gdk_drag_status(context, action, time);
1445 g_dataset_set_data_full(context, "drop_dest_path",
1446 g_strdup(icon->path), g_free);
1447 if (type == drop_dest_dir)
1448 dnd_spring_load(context, NULL);
1450 if (dnd_highlight && dnd_highlight != pi->widget)
1452 gtk_drag_unhighlight(dnd_highlight);
1453 dnd_highlight = NULL;
1456 if (dnd_highlight == NULL)
1458 gtk_drag_highlight(pi->widget);
1459 dnd_highlight = pi->widget;
1463 return type != NULL;
1467 static void add_uri_list(GtkWidget *widget,
1468 GdkDragContext *context,
1469 gint x,
1470 gint y,
1471 GtkSelectionData *selection_data,
1472 guint info,
1473 guint32 time,
1474 Panel *panel)
1476 gboolean after = FALSE;
1477 GList *uris, *next;
1479 if (!selection_data->data)
1480 return;
1482 g_return_if_fail(selection_data->data[selection_data->length] == '\0');
1484 if (g_object_get_data(G_OBJECT(widget), "after"))
1485 after = TRUE;
1487 uris = uri_list_to_glist(selection_data->data);
1489 for (next = uris; next; next = next->next)
1491 guchar *path;
1493 path = get_local_path((EscapedPath *) next->data);
1495 if (path) {
1496 panel_add_item(panel, path, NULL, after, NULL, NULL, FALSE);
1497 g_free(path);
1501 g_list_free(uris);
1504 static void drag_end(GtkWidget *widget,
1505 GdkDragContext *context,
1506 Icon *icon)
1508 if (tmp_icon_selected)
1510 icon_select_only(NULL);
1511 tmp_icon_selected = FALSE;
1515 static void drag_leave(GtkWidget *widget,
1516 GdkDragContext *context,
1517 guint32 time,
1518 Icon *icon)
1520 panel_drag_leave(widget, context, time, ((PanelIcon *) icon)->panel);
1522 if (dnd_highlight && dnd_highlight == widget)
1524 gtk_drag_unhighlight(dnd_highlight);
1525 dnd_highlight = NULL;
1528 dnd_spring_abort();
1531 /* Create XML icon nodes for these widgets.
1532 * Always frees the widgets list.
1534 static void make_widgets(xmlNodePtr side, GList *widgets)
1536 GList *next;
1538 for (next = widgets; next; next = next->next)
1540 Icon *icon;
1541 xmlNodePtr tree;
1543 icon = g_object_get_data(G_OBJECT(next->data), "icon");
1545 if (!icon)
1547 g_warning("Can't find Icon from widget\n");
1548 continue;
1551 tree = xmlNewTextChild(side, NULL, "icon", icon->src_path);
1553 xmlSetProp(tree, "label", icon->item->leafname);
1554 if (icon->shortcut)
1555 xmlSetProp(tree, "shortcut", icon->shortcut);
1556 if (icon->args)
1557 xmlSetProp(tree, "args", icon->args);
1558 if (icon->locked)
1559 xmlSetProp(tree, "locked", "true");
1562 if (widgets)
1563 g_list_free(widgets);
1566 void panel_save(Panel *panel)
1568 xmlDocPtr doc;
1569 xmlNodePtr root;
1570 xmlNodePtr options;
1571 guchar *save = NULL;
1572 guchar *save_new = NULL;
1574 g_return_if_fail(panel != NULL);
1576 if (strchr(panel->name, '/'))
1577 save = g_strdup(panel->name);
1578 else
1580 guchar *leaf;
1582 leaf = g_strconcat("pan_", panel->name, NULL);
1583 save = choices_find_xdg_path_save(leaf, PROJECT, SITE, TRUE);
1584 g_free(leaf);
1587 if (!save)
1588 return;
1590 doc = xmlNewDoc("1.0");
1591 xmlDocSetRootElement(doc, xmlNewDocNode(doc, NULL, "panel", NULL));
1593 root = xmlDocGetRootElement(doc);
1595 xmlSetProp(root, "side", panel_side_to_name(panel->side));
1597 options = xmlNewChild(root, NULL, "options", NULL);
1598 set_int_prop(options, "style", panel->style);
1599 set_int_prop(options, "width", panel->width);
1600 set_int_prop(options, "avoid", panel->avoid);
1601 set_int_prop(options, "xinerama", panel->xinerama);
1602 set_int_prop(options, "monitor", panel->monitor);
1604 make_widgets(xmlNewChild(root, NULL, "start", NULL),
1605 gtk_container_get_children(GTK_CONTAINER(panel->before)));
1607 make_widgets(xmlNewChild(root, NULL, "end", NULL),
1608 g_list_reverse(gtk_container_get_children(
1609 GTK_CONTAINER(panel->after))));
1611 save_new = g_strconcat(save, ".new", NULL);
1612 if (save_xml_file(doc, save_new) || rename(save_new, save))
1613 delayed_error(_("Error saving panel %s: %s"),
1614 save, g_strerror(errno));
1615 g_free(save_new);
1617 g_free(save);
1618 if (doc)
1619 xmlFreeDoc(doc);
1622 /* Create a frame widget which can be used to add icons to the panel */
1623 static GtkWidget *make_insert_frame(Panel *panel)
1625 GtkWidget *frame;
1626 GtkTargetEntry target_table[] = {
1627 {"text/uri-list", 0, TARGET_URI_LIST},
1630 frame = gtk_frame_new(NULL);
1631 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
1632 gtk_widget_set_size_request(frame, 16, 16);
1634 g_signal_connect(frame, "drag-motion",
1635 G_CALLBACK(insert_drag_motion), panel);
1636 g_signal_connect(frame, "drag-leave",
1637 G_CALLBACK(panel_drag_leave), panel);
1639 g_signal_connect(frame, "drag-data-received",
1640 G_CALLBACK(add_uri_list), panel);
1641 gtk_drag_dest_set(frame,
1642 GTK_DEST_DEFAULT_ALL,
1643 target_table,
1644 sizeof(target_table) / sizeof(*target_table),
1645 GDK_ACTION_COPY);
1647 return frame;
1650 static gboolean enter_icon(GtkWidget *widget,
1651 GdkEventCrossing *event,
1652 Icon *icon)
1654 icon_may_update(icon);
1655 panel_icon_set_tip((PanelIcon *) icon);
1657 return FALSE;
1660 static gint panel_leave_event(GtkWidget *widget,
1661 GdkEventCrossing *event,
1662 Panel *panel)
1664 GdkWindow *pinboard;
1666 if (event->mode != GDK_CROSSING_NORMAL)
1667 return FALSE; /* Grab for menu, DnD, etc */
1669 if (event->x >= 0 &&
1670 event->y >= 0 &&
1671 event->x < widget->allocation.width &&
1672 event->y < widget->allocation.height)
1674 /* Still inside the panel's area.
1675 * Probably we've moved over a panel applet. */
1676 return FALSE;
1679 if (panel_keep_below(panel, TRUE))
1681 /* Shouldn't need this as well as keep_below but some WMs don't
1682 * automatically lower as soon as the hint is set */
1683 pinboard = pinboard_get_window();
1684 window_put_just_above(panel->window->window, pinboard);
1687 return FALSE;
1690 /* If (x, y) is at the edge of the panel then raise */
1691 static void motion_may_raise(Panel *panel, int x, int y)
1693 gboolean raise;
1695 if (panel->side == PANEL_TOP)
1696 raise = y == 0;
1697 else if (panel->side == PANEL_BOTTOM)
1698 raise = y == panel->window->allocation.height - 1;
1699 else if (panel->side == PANEL_LEFT)
1700 raise = x == 0;
1701 else
1702 raise = x == panel->window->allocation.width - 1;
1704 if (raise && panel_keep_below(panel, FALSE))
1706 /* Shouldn't need this as well as keep_below but some WMs don't
1707 * automatically raise as soon as the hint is set */
1708 gdk_window_raise(panel->window->window);
1712 static gboolean may_autoscroll(Panel *panel)
1714 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1715 gint max, panel_x, panel_y, delta, new;
1717 if (panel->adj->upper <= panel->adj->page_size)
1718 goto stop_scrolling; /* Can see everything already */
1720 gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
1722 if (horz)
1724 delta = panel_x;
1725 max = panel->window->allocation.width;
1726 if (panel_y < 0 || panel_y > panel->window->allocation.height)
1727 goto stop_scrolling; /* Not over the panel */
1729 else
1731 delta = panel_y;
1732 max = panel->window->allocation.height;
1733 if (panel_x < 0 || panel_x > panel->window->allocation.width)
1734 goto stop_scrolling; /* Not over the panel */
1737 if (delta >= 20 && delta <= max - 20)
1738 goto stop_scrolling; /* Not at either end */
1740 panel->autoscroll_speed = MIN(panel->autoscroll_speed + 2, 200);
1742 new = panel->adj->value - ((delta < 20) ? panel->autoscroll_speed
1743 : -panel->autoscroll_speed);
1744 new = CLAMP(new, 0, panel->adj->upper - panel->adj->page_size);
1745 gtk_adjustment_set_value(panel->adj, new);
1747 panel->autoscroll_to = g_timeout_add(40,
1748 (GSourceFunc) may_autoscroll, panel);
1750 return FALSE;
1752 stop_scrolling:
1753 panel->autoscroll_speed = 0;
1754 return FALSE;
1757 static gint panel_motion_event(GtkWidget *widget,
1758 GdkEventMotion *event,
1759 Panel *panel)
1761 motion_may_raise(panel, event->x, event->y);
1763 if (motion_state != MOTION_REPOSITION)
1764 if (panel->autoscroll_speed == 0)
1765 may_autoscroll(panel);
1767 return FALSE;
1770 static gint icon_motion_event(GtkWidget *widget,
1771 GdkEventMotion *event,
1772 PanelIcon *pi)
1774 Panel *panel = pi->panel;
1775 GList *list, *me;
1776 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1777 int val;
1778 int dir = 0;
1780 if (motion_state == MOTION_READY_FOR_DND)
1782 if (dnd_motion_moved(event))
1783 start_drag(pi, event);
1784 return TRUE;
1786 else if (motion_state != MOTION_REPOSITION)
1787 return FALSE;
1789 list = gtk_container_get_children(GTK_CONTAINER(panel->before));
1790 list = g_list_append(list, NULL); /* The gap in the middle */
1791 list = g_list_concat(list,
1792 gtk_container_get_children(GTK_CONTAINER(panel->after)));
1793 me = g_list_find(list, widget);
1795 g_return_val_if_fail(me != NULL, TRUE);
1797 val = horz ? event->x_root : event->y_root;
1799 if (me->prev)
1801 GtkWidget *prev;
1802 int x, y;
1804 if (me->prev->data)
1805 prev = GTK_WIDGET(me->prev->data);
1806 else
1807 prev = panel->gap;
1809 gdk_window_get_origin(prev->window, &x, &y);
1811 if (val <= (horz ? x : y))
1812 dir = -1;
1815 if (dir == 0 && me->next)
1817 GtkWidget *next;
1818 int x, y, w, h;
1820 if (me->next->data)
1821 next = GTK_WIDGET(me->next->data);
1822 else
1823 next = panel->gap;
1825 gdk_window_get_origin(next->window, &x, &y);
1827 gdk_drawable_get_size(next->window, &w, &h);
1829 x += w;
1830 y += h;
1832 if (val >= (horz ? x : y)-1)
1834 if (next == panel->gap)
1835 dir = +2;
1836 else
1837 dir = +1;
1841 if (dir)
1842 reposition_icon(pi, g_list_index(list, widget) + dir);
1844 return TRUE;
1847 static void reposition_icon_on_side(GtkWidget *side, GtkWidget *widget,
1848 int index)
1850 GList *list;
1852 list = gtk_container_get_children(GTK_CONTAINER(side));
1854 /* Want to move icon to the list in the given 'side'. Is it there
1855 * already?
1858 if (!g_list_find(list, widget))
1860 /* No, reparent */
1861 gtk_grab_remove(widget);
1862 gtk_widget_reparent(widget, side);
1863 dnd_motion_grab_pointer();
1864 gtk_grab_add(widget);
1867 gtk_box_reorder_child(GTK_BOX(side), widget, index);
1869 g_list_free(list);
1872 /* Move icon to this index in the complete widget list.
1873 * 0 makes the icon the left-most icon. The gap in the middle has
1874 * an index number, which allows you to specify that the icon should
1875 * go on the left or right side.
1877 static void reposition_icon(PanelIcon *pi, int index)
1879 Panel *panel = pi->panel;
1880 GtkWidget *widget = pi->widget;
1881 GList *list;
1882 int before_len;
1884 list = gtk_container_get_children(GTK_CONTAINER(panel->before));
1885 before_len = g_list_length(list);
1886 g_list_free(list);
1888 if (index <= before_len)
1889 reposition_icon_on_side(panel->before, widget, index);
1890 else
1891 reposition_icon_on_side(panel->after, widget,
1892 index - (before_len + 1));
1894 panel_save(panel);
1897 static void start_drag(PanelIcon *pi, GdkEventMotion *event)
1899 GtkWidget *widget = pi->widget;
1900 Icon *icon = (Icon *) pi;
1902 if (!icon->selected)
1904 if (event->state & GDK_BUTTON1_MASK)
1906 /* Select just this one */
1907 icon_select_only(icon);
1908 tmp_icon_selected = TRUE;
1910 else
1911 icon_set_selected(icon, TRUE);
1914 g_return_if_fail(icon_selection != NULL);
1916 if (icon_selection->next == NULL)
1917 drag_one_item(widget, event, icon->path, icon->item, NULL);
1918 else
1920 guchar *uri_list;
1922 uri_list = icon_create_uri_list();
1923 drag_selection(widget, event, uri_list);
1924 g_free(uri_list);
1928 static void applet_died(GtkWidget *socket)
1930 gboolean never_plugged;
1932 never_plugged = (!g_object_get_data(G_OBJECT(socket), "lost_plug"))
1933 && !GTK_SOCKET(socket)->plug_window;
1935 if (never_plugged)
1937 report_error(
1938 _("Applet quit without ever creating a widget!"));
1939 gtk_widget_destroy(socket);
1942 gtk_widget_unref(socket);
1945 static void socket_destroyed(GtkWidget *socket, GtkWidget *widget)
1947 g_object_set_data(G_OBJECT(socket), "lost_plug", "yes");
1949 gtk_widget_unref(socket);
1951 gtk_widget_destroy(widget); /* Remove from panel */
1953 if (!closing_panel)
1954 panel_save(g_object_get_data(G_OBJECT(socket), "panel"));
1957 /* Try to run this applet.
1958 * Cases:
1960 * - No executable AppletRun:
1961 * icon->socket == NULL (unchanged) on return.
1963 * Otherwise, create socket (setting icon->socket) and ref it twice.
1965 * - AppletRun quits without connecting a plug:
1966 * On child death lost_plug is unset and socket is empty.
1967 * Unref socket.
1968 * Report error and destroy widget (to 'socket destroyed').
1970 * - AppletRun quits while plug is in socket:
1971 * Unref socket once. Socket will be destroyed later.
1973 * - Socket is destroyed.
1974 * Set lost_plug = "yes" and remove widget from panel.
1975 * Unref socket.
1977 static void run_applet(PanelIcon *pi)
1979 GError *error = NULL;
1980 char *argv[3];
1981 gint pid;
1982 Icon *icon = (Icon *) pi;
1984 argv[0] = (char *) make_path(icon->path, "AppletRun");
1986 if (access(argv[0], X_OK) != 0)
1987 return;
1989 pi->socket = gtk_socket_new();
1991 gtk_container_add(GTK_CONTAINER(pi->widget), pi->socket);
1992 gtk_widget_show_all(pi->socket);
1993 gtk_widget_realize(pi->socket);
1995 /* Always get button-2 events so we can drag */
1996 XGrabButton(gdk_display, Button2, AnyModifier,
1997 GDK_WINDOW_XWINDOW(pi->socket->window),
1998 False,
1999 ButtonPressMask | ButtonReleaseMask | Button2MotionMask,
2000 GrabModeAsync, /* Pointer */
2001 GrabModeAsync, /* Keyboard */
2002 None, None);
2005 gchar *pos;
2006 PanelSide side = pi->panel->side;
2008 /* Set a hint to let applets position their menus correctly */
2009 pos = g_strdup_printf("%s,%d",
2010 panel_side_to_name(side), MENU_MARGIN(side));
2011 gdk_property_change(pi->socket->window,
2012 gdk_atom_intern("_ROX_PANEL_MENU_POS", FALSE),
2013 gdk_atom_intern("STRING", FALSE),
2014 8, GDK_PROP_MODE_REPLACE,
2015 pos, strlen(pos));
2016 g_free(pos);
2018 /* Ensure that the properties are set before starting the
2019 * applet.
2021 gdk_flush();
2024 g_object_set_data(G_OBJECT(pi->widget), "icon", pi);
2025 g_object_set_data(G_OBJECT(pi->socket), "panel", pi->panel);
2027 argv[1] = g_strdup_printf("%ld",
2028 GDK_WINDOW_XWINDOW(pi->socket->window));
2029 argv[2] = NULL;
2031 if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
2032 NULL, NULL, &pid, &error))
2034 delayed_error(_("Error running applet:\n%s"), error->message);
2035 g_error_free(error);
2036 gtk_widget_destroy(pi->socket);
2037 pi->socket = NULL;
2039 else
2041 gtk_widget_ref(pi->socket);
2042 on_child_death(pid, (CallbackFn) applet_died, pi->socket);
2044 gtk_widget_ref(pi->socket);
2045 g_signal_connect(pi->socket, "destroy",
2046 G_CALLBACK(socket_destroyed), pi->widget);
2049 g_free(argv[1]);
2052 static void panel_post_resize(GtkWidget *win, GtkRequisition *req, Panel *panel)
2054 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
2056 req->width = panel->geometry.width;
2057 req->height += EDGE_WIDTH;
2059 else
2061 int h = panel->geometry.height;
2063 if (current_panel[PANEL_TOP])
2065 GtkWidget *win = current_panel[PANEL_TOP]->window;
2066 h -= win->allocation.height;
2069 if (current_panel[PANEL_BOTTOM])
2071 GtkWidget *win = current_panel[PANEL_BOTTOM]->window;
2072 h -= win->allocation.height;
2075 req->height = h;
2076 req->width += EDGE_WIDTH;
2080 static void update_side(GtkWidget *side)
2082 GList *kids, *next;
2084 kids = gtk_container_get_children(GTK_CONTAINER(side));
2085 for (next = kids; next; next = next->next)
2087 PanelIcon *pi;
2088 pi = g_object_get_data(next->data, "icon");
2089 panel_icon_set_tip(pi);
2091 g_list_free(kids);
2094 /* Tips or style has changed -- update everything on this panel */
2095 static void panel_set_style(Panel *panel)
2097 update_side(panel->before);
2098 update_side(panel->after);
2099 gtk_widget_queue_resize(panel->window);
2102 static gboolean recreate_panels(char **names)
2104 int i;
2106 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
2108 if (names[i])
2110 panel_new(names[i], i);
2111 g_free(names[i]);
2115 g_free(names);
2117 return FALSE;
2120 static void update_side_size(GtkWidget *side)
2122 GList *kids, *next;
2124 kids = gtk_container_get_children(GTK_CONTAINER(side));
2125 for (next = kids; next; next = next->next)
2127 PanelIcon *pi;
2128 pi = g_object_get_data(next->data, "icon");
2129 gtk_widget_queue_resize(pi->widget);
2131 g_list_free(kids);
2134 /* Update panel size and redraw */
2135 static void panel_update(Panel *panel)
2137 update_side_size(panel->before);
2138 update_side_size(panel->after);
2139 gtk_widget_queue_resize(panel->window);
2140 gtk_widget_queue_draw(panel->window);
2143 static void panel_style_changed(void)
2145 int i;
2147 if (o_override_redirect.has_changed)
2149 gchar **names;
2151 names = g_new(char *, PANEL_NUMBER_OF_SIDES);
2153 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
2155 Panel *panel = current_panel[i];
2156 names[i] = panel ? g_strdup(panel->name) : NULL;
2157 panel_new(NULL, i);
2160 g_idle_add((GtkFunction) recreate_panels, names);
2164 static gboolean draw_panel_edge(GtkWidget *widget, GdkEventExpose *event,
2165 Panel *panel)
2167 int x, y, width, height;
2169 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
2171 width = panel->geometry.width;
2172 height = EDGE_WIDTH;
2174 x = 0;
2175 if (panel->side == PANEL_BOTTOM)
2176 y = 0;
2177 else
2178 y = widget->allocation.height - EDGE_WIDTH;
2180 else
2182 width = EDGE_WIDTH;
2183 height = panel->geometry.height;
2185 y = 0;
2186 if (panel->side == PANEL_RIGHT)
2187 x = 0;
2188 else
2189 x = widget->allocation.width - EDGE_WIDTH;
2192 gdk_draw_rectangle(widget->window,
2193 widget->style->fg_gc[GTK_STATE_NORMAL], TRUE,
2194 x, y, width, height);
2196 return FALSE;
2199 static gpointer parent_class;
2201 static void panel_icon_destroy(Icon *icon)
2203 PanelIcon *pi = (PanelIcon *) icon;
2205 g_return_if_fail(pi != NULL);
2207 if (pi->image)
2208 g_object_unref(pi->image);
2210 g_return_if_fail(pi->widget != NULL);
2212 gtk_widget_destroy(pi->widget);
2215 static void panel_remove_items(void)
2217 Panel *panel;
2219 g_return_if_fail(icon_selection != NULL);
2221 panel = ((PanelIcon *) icon_selection->data)->panel;
2223 while (icon_selection)
2224 icon_destroy((Icon *) icon_selection->data);
2226 panel_save(panel);
2229 /* Icon's size, shape or appearance has changed - update the display */
2230 static void panel_icon_redraw(Icon *icon)
2232 PanelIcon *pi = (PanelIcon *) icon;
2234 gtk_widget_set_state(pi->widget,
2235 icon->selected ? GTK_STATE_SELECTED
2236 : GTK_STATE_NORMAL);
2238 /* Will regenerate the scaled icon from the new image */
2239 gtk_widget_queue_resize(pi->widget);
2241 panel_icon_set_tip((PanelIcon *) icon);
2244 static void panel_icon_update(Icon *icon)
2246 PanelIcon *pi = (PanelIcon *) icon;
2248 gtk_widget_queue_draw(pi->widget);
2249 gtk_label_set_text(GTK_LABEL(pi->label), icon->item->leafname);
2250 panel_save(pi->panel);
2253 /* The point of this is to clear the selection if the existing icons
2254 * aren't from the same panel...
2256 static gboolean panel_icon_same_group(Icon *icon, Icon *other)
2258 if (IS_PANEL_ICON(other))
2260 PanelIcon *a = (PanelIcon *) icon;
2261 PanelIcon *b = (PanelIcon *) other;
2263 return a->panel == b->panel;
2265 else
2266 return FALSE;
2269 static void panel_icon_class_init(gpointer gclass, gpointer data)
2271 IconClass *icon = (IconClass *) gclass;
2273 parent_class = g_type_class_peek_parent(gclass);
2275 icon->destroy = panel_icon_destroy;
2276 icon->redraw = panel_icon_redraw;
2277 icon->update = panel_icon_update;
2278 icon->remove_items = panel_remove_items;
2279 icon->same_group = panel_icon_same_group;
2280 icon->wink = panel_icon_wink;
2283 static void panel_icon_init(GTypeInstance *object, gpointer gclass)
2285 PanelIcon *pi = (PanelIcon *) object;
2287 pi->widget = NULL;
2288 pi->image = NULL;
2289 pi->label = NULL;
2290 pi->socket = NULL;
2291 pi->style = TEXT_UNDER_ICON;
2294 static GType panel_icon_get_type(void)
2296 static GType type = 0;
2298 if (!type)
2300 static const GTypeInfo info =
2302 sizeof (PanelIconClass),
2303 NULL, /* base_init */
2304 NULL, /* base_finalise */
2305 panel_icon_class_init,
2306 NULL, /* class_finalise */
2307 NULL, /* class_data */
2308 sizeof(PanelIcon),
2309 0, /* n_preallocs */
2310 panel_icon_init
2313 type = g_type_register_static(icon_get_type(),
2314 "PanelIcon", &info, 0);
2317 return type;
2320 static PanelIcon *panel_icon_new(Panel *panel,
2321 const char *pathname,
2322 const char *name)
2324 PanelIcon *pi;
2325 Icon *icon;
2327 pi = g_object_new(panel_icon_get_type(), NULL);
2328 icon = (Icon *) pi;
2330 icon_set_path(icon, pathname, name);
2331 pi->panel = panel;
2333 return pi;
2336 static gboolean panel_want_show_text(PanelIcon *pi)
2338 Icon *icon = (Icon *) pi;
2339 Panel *panel = pi->panel;
2341 if (!icon->item->leafname[0])
2342 return FALSE;
2344 if (panel->style == SHOW_BOTH)
2345 return TRUE;
2346 if (panel->style == SHOW_ICON)
2347 return FALSE;
2349 if (icon->item->flags & ITEM_FLAG_APPDIR)
2350 return FALSE;
2352 if (EXECUTABLE_FILE(icon->item))
2353 return FALSE;
2355 return TRUE;
2358 static void xinerama_sensitive(GtkBuilder *builder, gboolean sensitive)
2360 gtk_widget_set_sensitive(
2361 GTK_WIDGET(gtk_builder_get_object(builder, "panel_xinerama_monitor")),
2362 sensitive);
2365 inline static Panel *panel_from_opts_widget(GtkWidget *widget)
2367 return g_object_get_data(G_OBJECT(gtk_widget_get_toplevel(widget)),
2368 "rox-panel");
2371 static void panel_style_radio_toggled(GtkToggleButton *widget, int style)
2373 Panel *panel;
2375 if (!gtk_toggle_button_get_active(widget))
2376 return;
2377 panel = panel_from_opts_widget(GTK_WIDGET(widget));
2378 if (style != panel->style)
2380 panel->style = style;
2381 panel_set_style(panel);
2382 panel_save(panel);
2386 static void panel_xinerama_changed(Panel *panel)
2388 panel_update_geometry(panel);
2389 reposition_panel(panel->window, &panel->window->allocation, panel);
2390 gtk_widget_queue_resize(panel->window);
2391 panel_save(panel);
2394 static void panel_side_radio_toggled(GtkWidget *widget, PanelSide new_side)
2396 Panel *panel;
2397 PanelSide old_side;
2398 char *name, *other_side_name;
2400 if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
2401 return;
2403 panel = panel_from_opts_widget(widget);
2404 old_side = panel->side;
2405 if (new_side == old_side)
2406 return;
2408 name = g_strdup(panel->name);
2409 other_side_name = current_panel[new_side]
2410 ? g_strdup(current_panel[new_side]->name)
2411 : NULL;
2413 panel_new(name, new_side);
2414 g_object_set_data(G_OBJECT(gtk_widget_get_toplevel(widget)),
2415 "rox-panel", current_panel[new_side]);
2416 panel_new(other_side_name, old_side);
2418 g_free(name);
2419 g_free(other_side_name);
2422 static void panel_style_radio_0_toggled_cb(GtkToggleButton *widget)
2424 panel_style_radio_toggled(widget, 0);
2427 static void panel_style_radio_1_toggled_cb(GtkToggleButton *widget)
2429 panel_style_radio_toggled(widget, 1);
2432 static void panel_style_radio_2_toggled_cb(GtkToggleButton *widget)
2434 panel_style_radio_toggled(widget, 2);
2437 static void panel_width_changed_cb(GtkSpinButton *widget)
2439 Panel *panel = panel_from_opts_widget(GTK_WIDGET(widget));
2440 int width = gtk_spin_button_get_value_as_int(widget);
2442 if (width != panel->width)
2444 panel->width = width;
2445 panel_update(panel);
2446 panel_save(panel);
2450 static void panel_avoid_toggled_cb(GtkToggleButton *widget)
2452 Panel *panel = panel_from_opts_widget(GTK_WIDGET(widget));
2453 gboolean avoid = gtk_toggle_button_get_active(widget);
2455 if (avoid != panel->avoid)
2457 panel->avoid = avoid;
2458 panel_setup_struts(panel, &panel->window->allocation);
2459 panel_save(panel);
2463 static void panel_xinerama_confine_toggled_cb(GtkWidget *widget, GtkWidget *spinner)
2465 Panel *panel = panel_from_opts_widget(widget);
2466 gboolean xinerama = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
2468 gtk_widget_set_sensitive(spinner, xinerama);
2470 if (xinerama != panel->xinerama)
2472 panel->xinerama = xinerama;
2473 panel_xinerama_changed(panel);
2477 static void panel_xinerama_monitor_changed_cb(GtkSpinButton *widget)
2479 Panel *panel = panel_from_opts_widget(GTK_WIDGET(widget));
2480 int monitor = gtk_spin_button_get_value_as_int(widget);
2482 if (monitor != panel->monitor)
2484 panel->monitor = monitor;
2485 panel_xinerama_changed(panel);
2489 static void panel_pos_top_toggled_cb(GtkWidget *widget)
2491 panel_side_radio_toggled(widget, PANEL_TOP);
2494 static void panel_pos_bottom_toggled_cb(GtkWidget *widget)
2496 panel_side_radio_toggled(widget, PANEL_BOTTOM);
2499 static void panel_pos_left_toggled_cb(GtkWidget *widget)
2501 panel_side_radio_toggled(widget, PANEL_LEFT);
2504 static void panel_pos_right_toggled_cb(GtkWidget *widget)
2506 panel_side_radio_toggled(widget, PANEL_RIGHT);
2509 static void panel_connect_dialog_signal_handlers(GtkBuilder *builder,
2510 GObject *object,
2511 const gchar *signal_name,
2512 const gchar *handler_name,
2513 GObject *connect_object,
2514 GConnectFlags flags,
2515 gpointer user_data)
2517 void *fn = NULL;
2519 if (strcmp(handler_name, "gtk_widget_destroy") == 0)
2520 fn = gtk_widget_destroy;
2521 else if (strcmp(handler_name, "panel_style_radio_0_toggled_cb") == 0)
2522 fn = panel_style_radio_0_toggled_cb;
2523 else if (strcmp(handler_name, "panel_style_radio_0_toggled_cb") == 0)
2524 fn = panel_style_radio_0_toggled_cb;
2525 else if (strcmp(handler_name, "panel_style_radio_1_toggled_cb") == 0)
2526 fn = panel_style_radio_1_toggled_cb;
2527 else if (strcmp(handler_name, "panel_style_radio_2_toggled_cb") == 0)
2528 fn = panel_style_radio_2_toggled_cb;
2529 else if (strcmp(handler_name, "panel_width_changed_cb") == 0)
2530 fn = panel_width_changed_cb;
2531 else if (strcmp(handler_name, "panel_avoid_toggled_cb") == 0)
2532 fn = panel_avoid_toggled_cb;
2533 else if (strcmp(handler_name, "panel_xinerama_confine_toggled_cb") == 0)
2534 fn = panel_xinerama_confine_toggled_cb;
2535 else if (strcmp(handler_name, "panel_xinerama_monitor_changed_cb") == 0)
2536 fn = panel_xinerama_monitor_changed_cb;
2537 else if (strcmp(handler_name, "panel_pos_top_toggled_cb") == 0)
2538 fn = panel_pos_top_toggled_cb;
2539 else if (strcmp(handler_name, "panel_pos_bottom_toggled_cb") == 0)
2540 fn = panel_pos_bottom_toggled_cb;
2541 else if (strcmp(handler_name, "panel_pos_left_toggled_cb") == 0)
2542 fn = panel_pos_left_toggled_cb;
2543 else if (strcmp(handler_name, "panel_pos_right_toggled_cb") == 0)
2544 fn = panel_pos_right_toggled_cb;
2546 if (fn != NULL)
2548 g_signal_connect(object, signal_name, fn, connect_object);
2550 else
2552 g_warning("Unknown handler '%s'", handler_name);
2556 static void panel_setup_options_dialog(GtkBuilder *builder, Panel *panel)
2558 char *wnm;
2559 const char *pos_radio;
2561 wnm = g_strdup_printf("panel_style_radio_%d", panel->style);
2562 gtk_toggle_button_set_active(
2563 GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, wnm)),
2564 TRUE);
2565 g_free(wnm);
2566 gtk_spin_button_set_value(
2567 GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "panel_width")),
2568 panel->width);
2569 gtk_toggle_button_set_active(
2570 GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "panel_avoid")),
2571 panel->avoid);
2572 gtk_toggle_button_set_active(
2573 GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder,
2574 "panel_xinerama_confine")),
2575 panel->xinerama);
2576 gtk_spin_button_set_adjustment(
2577 GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "panel_xinerama_monitor")),
2578 GTK_ADJUSTMENT(gtk_adjustment_new(MAX(0, panel->monitor),
2579 0, n_monitors - 1, 1, 10, 0)));
2580 xinerama_sensitive(builder, panel->xinerama);
2581 switch (panel->side)
2583 case PANEL_TOP:
2584 pos_radio = "panel_pos_top";
2585 break;
2586 case PANEL_BOTTOM:
2587 pos_radio = "panel_pos_bottom";
2588 break;
2589 case PANEL_LEFT:
2590 pos_radio = "panel_pos_left";
2591 break;
2592 case PANEL_RIGHT:
2593 pos_radio = "panel_pos_right";
2594 break;
2595 default:
2596 pos_radio = NULL;
2597 break;
2599 g_return_if_fail(pos_radio != NULL);
2600 gtk_toggle_button_set_active(
2601 GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, pos_radio)),
2602 TRUE);
2605 static void panel_show_options(Panel *panel)
2607 GtkWidget *dialog;
2608 gboolean already_showing = FALSE;
2609 GtkBuilder *builder;
2610 gchar *ids[] = {"adjustment1", "adjustment2", "Panel Options", NULL};
2612 builder = get_gtk_builder(ids);
2614 if (panel_options_dialog)
2616 dialog = panel_options_dialog;
2617 already_showing = TRUE;
2619 else
2621 dialog = GTK_WIDGET(gtk_builder_get_object(builder, "Panel Options"));
2622 panel_options_dialog = dialog;
2623 g_signal_connect(dialog, "destroy",
2624 G_CALLBACK(gtk_widget_destroyed),
2625 &panel_options_dialog);
2626 gtk_builder_connect_signals_full(builder, &panel_connect_dialog_signal_handlers, NULL);
2628 g_object_set_data(G_OBJECT(panel_options_dialog), "rox-panel", panel);
2630 panel_setup_options_dialog(builder, panel);
2632 if (already_showing)
2634 GtkWindow *win = GTK_WINDOW(dialog);
2636 gtk_widget_hide(dialog);
2637 /* This extra set_position() should ensure it moves to new position
2638 * under pointer */
2639 gtk_window_set_position(win, GTK_WIN_POS_CENTER_ALWAYS);
2640 gtk_window_set_position(win, GTK_WIN_POS_MOUSE);
2641 gtk_window_present(win);
2643 else
2645 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
2646 gtk_widget_show_all(dialog);
2649 g_object_unref(builder);
2652 static void panel_position_menu(GtkMenu *menu, gint *x, gint *y,
2653 gboolean *push_in, gpointer data)
2655 int *pos = (int *) data;
2656 GtkRequisition requisition;
2657 int margin = pos[2];
2658 int mon = pos[3];
2659 int mon_right = monitor_geom[mon].x +
2660 monitor_geom[mon].width;
2661 int mon_bottom = monitor_geom[mon].y +
2662 monitor_geom[mon].height;
2664 gtk_widget_size_request(GTK_WIDGET(menu), &requisition);
2666 if (pos[0] == -1)
2667 *x = mon_right - margin - requisition.width;
2668 else if (pos[0] == -2)
2669 *x = monitor_geom[mon].x + margin;
2670 else
2671 *x = pos[0] - (requisition.width >> 2);
2673 if (pos[1] == -1)
2674 *y = mon_bottom - margin - requisition.height;
2675 else if (pos[1] == -2)
2676 *y = monitor_geom[mon].y + margin;
2677 else
2678 *y = pos[1] - (requisition.height >> 2);
2680 *x = CLAMP(*x, 0, mon_right - requisition.width);
2681 *y = CLAMP(*y, 0, mon_bottom - requisition.height);
2683 *push_in = FALSE;
2686 static void panel_remove_callback(PanelSide side)
2688 GtkWidget *dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL,
2689 GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL,
2690 _("Are you sure you want to remove this panel from the desktop?"));
2692 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
2693 gtk_window_set_title(GTK_WINDOW(dialog), _("Remove Panel"));
2694 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
2695 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
2696 panel_new(NULL, side);
2697 gtk_widget_destroy(dialog);
2700 static void panel_show_menu(GdkEventButton *event, PanelIcon *pi, Panel *panel)
2702 GtkWidget *option_item;
2703 GtkWidget *del_item;
2704 PanelSide side = panel->side;
2705 int pos[4];
2707 pos[0] = event->x_root;
2708 pos[1] = event->y_root;
2709 pos[2] = MENU_MARGIN(side);
2710 /* FIXME: Should we read screen from event's window rather than
2711 * using default? */
2712 pos[3] = gdk_screen_get_monitor_at_point(
2713 gdk_screen_get_default(),
2714 event->x_root, event->y_root);
2716 option_item = gtk_image_menu_item_new_with_label(_("Panel Options..."));
2717 g_signal_connect_swapped(option_item, "activate",
2718 G_CALLBACK(panel_show_options), panel);
2720 del_item = gtk_image_menu_item_new_with_label(_("Remove Panel"));
2721 add_stock_to_menu_item(del_item, GTK_STOCK_REMOVE);
2722 g_signal_connect_swapped(del_item, "activate",
2723 G_CALLBACK(panel_remove_callback), GINT_TO_POINTER(side));
2725 icon_prepare_menu((Icon *) pi, option_item, del_item, NULL);
2727 if (side == PANEL_LEFT)
2728 pos[0] = -2;
2729 else if (side == PANEL_RIGHT)
2730 pos[0] = -1;
2732 if (side == PANEL_TOP)
2733 pos[1] = -2;
2734 else if (side == PANEL_BOTTOM)
2735 pos[1] = -1;
2737 gtk_menu_popup(GTK_MENU(icon_menu), NULL, NULL,
2738 panel_position_menu,
2739 (gpointer) pos, event->button, event->time);
2742 /* Note: also called from icon handler */
2743 static gboolean panel_drag_motion(GtkWidget *widget,
2744 GdkDragContext *context,
2745 gint x,
2746 gint y,
2747 guint time,
2748 Panel *panel)
2750 int panel_x, panel_y;
2752 gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
2754 motion_may_raise(panel, panel_x, panel_y);
2755 gdk_drag_status(context, 0, time);
2757 return TRUE;
2760 static gboolean insert_drag_motion(GtkWidget *widget,
2761 GdkDragContext *context,
2762 gint x,
2763 gint y,
2764 guint time,
2765 Panel *panel)
2767 int panel_x, panel_y;
2769 gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
2770 motion_may_raise(panel, panel_x, panel_y);
2772 return FALSE;
2775 /* Note: also called from icon handler */
2776 static void panel_drag_leave(GtkWidget *widget,
2777 GdkDragContext *context,
2778 guint32 time,
2779 Panel *panel)
2781 GdkWindow *pinboard, *window;
2782 GtkAllocation *alloc = &panel->window->allocation;
2783 int x, y;
2785 window = panel->window->window;
2786 gdk_window_get_pointer(window, &x, &y, NULL);
2787 if ((x < 0 || y < 0 || x > alloc->width || y > alloc->height) &&
2788 panel_keep_below(panel, TRUE))
2790 /* Shouldn't need this as well as keep_below but some WMs don't
2791 * automatically lower as soon as the hint is set */
2792 pinboard = pinboard_get_window();
2793 window_put_just_above(panel->window->window, pinboard);
2797 static void panel_update_geometry(Panel *panel)
2799 if (panel->xinerama && panel->monitor >= n_monitors)
2801 g_warning(_("Xinerama monitor %d unavailable"), panel->monitor);
2802 panel->xinerama = FALSE;
2805 if (panel->xinerama)
2807 panel->geometry = monitor_geom[panel->monitor];
2809 else if (o_panel_obey_workarea.int_value)
2811 get_work_area(&panel->geometry.x, &panel->geometry.y,
2812 &panel->geometry.width, &panel->geometry.height);
2814 else
2816 panel->geometry.x = panel->geometry.y = 0;
2817 panel->geometry.width = screen_width;
2818 panel->geometry.height = screen_height;
2822 static GList *build_monitor_number(Option *option, xmlNode *node, guchar *label)
2824 GtkObject *adj;
2826 adj = gtk_adjustment_new(MAX(0, panel_monitor),
2827 0, n_monitors - 1, 1, 10, 1);
2828 return build_numentry_base(option, node, label, GTK_ADJUSTMENT(adj));
2831 static const char *panel_side_to_translated_name(PanelSide side)
2833 switch (side)
2835 case PANEL_TOP:
2836 return _("Top");
2837 case PANEL_BOTTOM:
2838 return _("Bottom");
2839 case PANEL_LEFT:
2840 return _("Left");
2841 case PANEL_RIGHT:
2842 return _("Right");
2843 case PANEL_DEFAULT_SIDE:
2844 return _("Default");
2845 default:
2846 break;
2848 return _("Unknown side");
2851 const char *panel_side_to_name(PanelSide side)
2853 switch (side)
2855 case PANEL_TOP:
2856 return "Top";
2857 case PANEL_BOTTOM:
2858 return "Bottom";
2859 case PANEL_LEFT:
2860 return "Left";
2861 case PANEL_RIGHT:
2862 return "Right";
2863 case PANEL_DEFAULT_SIDE:
2864 return "Default";
2865 default:
2866 break;
2868 return "UnknownSide";
2871 /* Returns PANEL_NUMBER_OF_SIDES if name is invalid */
2872 PanelSide panel_name_to_side(gchar *side)
2874 if (strcmp(side, "Top") == 0)
2875 return PANEL_TOP;
2876 else if (strcmp(side, "Bottom") == 0)
2877 return PANEL_BOTTOM;
2878 else if (strcmp(side, "Left") == 0)
2879 return PANEL_LEFT;
2880 else if (strcmp(side, "Right") == 0)
2881 return PANEL_RIGHT;
2882 else
2883 g_warning("Unknown panel side '%s'", side);
2884 return PANEL_NUMBER_OF_SIDES;
2887 static void panel_add_callback(PanelSide side)
2889 g_return_if_fail(current_panel[side] == NULL);
2890 panel_new(panel_side_to_name(side), side);
2893 GtkWidget *panel_new_panel_submenu(void)
2895 GtkWidget *menu = gtk_menu_new();
2896 PanelSide side;
2898 for (side = 0; side < PANEL_NUMBER_OF_SIDES; ++side)
2900 GtkWidget *item = gtk_menu_item_new_with_label(
2901 panel_side_to_translated_name(side));
2903 g_signal_connect_swapped(item, "activate",
2904 G_CALLBACK(panel_add_callback), GINT_TO_POINTER(side));
2905 gtk_widget_set_sensitive(item, current_panel[side] == NULL);
2906 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2907 gtk_widget_show(item);
2909 return menu;
2912 /* Set the panel to be above/below other windows (setting = TRUE for below),
2913 * and return TRUE.
2915 * If o_panel_on_top is set then do nothing and return FALSE.
2917 static gboolean panel_keep_below(Panel *panel, gboolean setting)
2919 if (!o_panel_on_top.int_value)
2921 keep_below(panel->window->window, setting);
2922 return TRUE;
2924 return FALSE;