Got rid of panel options from main Options dialog.
[rox-filer/translations.git] / ROX-Filer / src / panel.c
blobc33da740e33912d45809399dd0f3c811b7f8b90b
1 /*
2 * ROX-Filer, filer for the ROX desktop project
3 * Copyright (C) 2006, Thomas Leonard and others (see changelog for details).
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
17 * Place, Suite 330, Boston, MA 02111-1307 USA
20 /* panel.c - code for dealing with panel windows */
22 #include "config.h"
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <ctype.h>
29 #include <unistd.h>
30 #include <libxml/parser.h>
32 #include <gtk/gtk.h>
33 #include <gdk/gdkx.h>
35 #include "global.h"
37 #include "panel.h"
38 #include "options.h"
39 #include "choices.h"
40 #include "main.h"
41 #include "type.h"
42 #include "gui_support.h"
43 #include "diritem.h"
44 #include "pixmaps.h"
45 #include "filer.h"
46 #include "display.h"
47 #include "bind.h"
48 #include "dnd.h"
49 #include "support.h"
50 #include "icon.h"
51 #include "run.h"
52 #include "appinfo.h"
53 #include "pixmaps.h"
54 #include "xml.h"
55 #include "pinboard.h" /* For pinboard_get_window() */
57 /* The width of the separator at the inner edge of the panel */
58 #define EDGE_WIDTH 1
60 /* The gap between panel icons */
61 #define PANEL_ICON_SPACING 8
63 enum {TEXT_BESIDE_ICON, TEXT_UNDER_ICON};
65 static gboolean tmp_icon_selected = FALSE; /* When dragging */
67 typedef struct _PanelIconClass PanelIconClass;
68 typedef struct _PanelIcon PanelIcon;
70 struct _PanelIconClass {
71 IconClass parent;
74 struct _PanelIcon {
75 Icon icon;
76 GdkPixbuf *image;
78 Panel *panel;
79 GtkWidget *widget; /* The drawing area for the icon */
80 GtkWidget *label;
81 GtkWidget *socket; /* For applets */
83 int style;
86 #define PANEL_ICON(obj) GTK_CHECK_CAST((obj), panel_icon_get_type(), PanelIcon)
87 #define IS_PANEL_ICON(obj) \
88 G_TYPE_CHECK_INSTANCE_TYPE((obj), panel_icon_get_type())
90 Panel *current_panel[PANEL_NUMBER_OF_SIDES];
92 /* NULL => Not loading a panel */
93 static Panel *loading_panel = NULL;
95 static GtkWidget *panel_options_dialog = NULL;
97 /* Static prototypes */
98 static int panel_delete(GtkWidget *widget, GdkEvent *event, Panel *panel);
99 static void panel_destroyed(GtkWidget *widget, Panel *panel);
100 static const char *pan_from_file(gchar *line);
101 static gint icon_button_release(GtkWidget *widget,
102 GdkEventButton *event,
103 PanelIcon *pi);
104 static gint icon_button_press(GtkWidget *widget,
105 GdkEventButton *event,
106 PanelIcon *pi);
107 static void reposition_panel(GtkWidget *window,
108 GtkAllocation *alloc, Panel *panel);
109 static gint expose_icon(GtkWidget *widget,
110 GdkEventExpose *event,
111 PanelIcon *pi);
112 static gint draw_icon(GtkWidget *widget,
113 GdkRectangle *badarea,
114 PanelIcon *pi);
115 static gint panel_button_release(GtkWidget *widget,
116 GdkEventButton *event,
117 Panel *panel);
118 static gint panel_button_press(GtkWidget *widget,
119 GdkEventButton *event,
120 Panel *panel);
121 static void panel_post_resize(GtkWidget *box,
122 GtkRequisition *req, Panel *panel);
123 static void drag_set_panel_dest(PanelIcon *pi);
124 static void add_uri_list(GtkWidget *widget,
125 GdkDragContext *context,
126 gint x,
127 gint y,
128 GtkSelectionData *selection_data,
129 guint info,
130 guint32 time,
131 Panel *panel);
132 static void panel_add_item(Panel *panel,
133 const gchar *path,
134 const gchar *name,
135 gboolean after,
136 const gchar *shortcut,
137 const gchar *args,
138 gboolean locked);
139 static gboolean panel_drag_motion(GtkWidget *widget,
140 GdkDragContext *context,
141 gint x,
142 gint y,
143 guint time,
144 Panel *panel);
145 static gboolean insert_drag_motion(GtkWidget *widget,
146 GdkDragContext *context,
147 gint x,
148 gint y,
149 guint time,
150 Panel *panel);
151 static gboolean drag_motion(GtkWidget *widget,
152 GdkDragContext *context,
153 gint x,
154 gint y,
155 guint time,
156 PanelIcon *pi);
157 static void panel_drag_leave(GtkWidget *widget,
158 GdkDragContext *context,
159 guint32 time,
160 Panel *panel);
161 static void drag_leave(GtkWidget *widget,
162 GdkDragContext *context,
163 guint32 time,
164 Icon *icon);
165 static GtkWidget *make_insert_frame(Panel *panel);
166 static gboolean enter_icon(GtkWidget *widget,
167 GdkEventCrossing *event,
168 Icon *icon);
169 static gint icon_motion_event(GtkWidget *widget,
170 GdkEventMotion *event,
171 PanelIcon *pi);
172 static gint panel_leave_event(GtkWidget *widget,
173 GdkEventCrossing *event,
174 Panel *panel);
175 static gint panel_motion_event(GtkWidget *widget,
176 GdkEventMotion *event,
177 Panel *panel);
178 static void reposition_icon(PanelIcon *pi, int index);
179 static void start_drag(PanelIcon *pi, GdkEventMotion *event);
180 static void drag_end(GtkWidget *widget,
181 GdkDragContext *context,
182 Icon *icon);
183 static void perform_action(Panel *panel,
184 PanelIcon *pi,
185 GdkEventButton *event);
186 static void run_applet(PanelIcon *pi);
187 static void size_request(GtkWidget *widget, GtkRequisition *req, PanelIcon *pi);
188 static void panel_load_from_xml(Panel *panel, xmlDocPtr doc);
189 static gboolean draw_panel_edge(GtkWidget *widget, GdkEventExpose *event,
190 Panel *panel);
191 static PanelIcon *panel_icon_new(Panel *panel,
192 const char *pathname,
193 const char *name);
194 static GType panel_icon_get_type(void);
195 static gboolean panel_want_show_text(PanelIcon *pi);
196 static void panel_show_menu(GdkEventButton *event, PanelIcon *pi, Panel *panel);
197 static void panel_style_changed(void);
198 static void motion_may_raise(Panel *panel, int x, int y);
199 static void panel_update(Panel *panel);
200 static GList *build_monitor_number(Option *option,
201 xmlNode *node, guchar *label);
202 static gboolean may_autoscroll(Panel *panel);
203 static void panel_update_geometry(Panel *panel);
206 static GtkWidget *dnd_highlight = NULL; /* (stops flickering) */
208 #define SHOW_BOTH 0
209 #define SHOW_APPS_SMALL 1
210 #define SHOW_ICON 2
211 static Option o_panel_style;
212 static Option o_panel_width;
213 static Option o_panel_xinerama;
214 static Option o_panel_monitor;
215 static Option o_panel_avoid;
216 static Option o_panel_is_dock;
218 static gint panel_monitor = -1;
220 static int closing_panel = 0; /* Don't panel_save; destroying! */
222 /****************************************************************
223 * EXTERNAL INTERFACE *
224 ****************************************************************/
226 void panel_init(void)
228 option_add_int(&o_panel_style, "panel_style", SHOW_APPS_SMALL);
229 option_add_int(&o_panel_width, "panel_width", 52);
231 option_add_int(&o_panel_xinerama, "panel_xinerama", 0);
232 option_add_int(&o_panel_monitor, "panel_monitor", 0);
234 option_add_int(&o_panel_avoid, "panel_avoid", TRUE);
235 option_add_int(&o_panel_is_dock, "panel_is_dock", FALSE);
237 option_add_notify(panel_style_changed);
239 option_register_widget("monitor-number", build_monitor_number);
242 /* Return a free edge for a new panel.
243 * If no edge is free, returns PANEL_BOTTOM.
245 static PanelSide find_free_side()
247 if (!current_panel[PANEL_BOTTOM])
248 return PANEL_BOTTOM;
250 if (!current_panel[PANEL_TOP])
251 return PANEL_TOP;
253 if (!current_panel[PANEL_LEFT])
254 return PANEL_LEFT;
256 if (!current_panel[PANEL_RIGHT])
257 return PANEL_RIGHT;
259 return PANEL_BOTTOM;
262 static void get_int_prop(xmlNodePtr node, const char *name, int *target)
264 char *prop = xmlGetProp(node, name);
266 if (prop)
268 *target = atoi(prop);
269 g_free(prop);
273 static void panel_load_options_from_xml(Panel *panel, xmlDocPtr doc)
275 xmlNodePtr root;
276 xmlNodePtr options;
278 root = xmlDocGetRootElement(doc);
279 options = get_subnode(root, NULL, "options");
280 if (!options)
281 return;
282 get_int_prop(options, "style", &panel->style);
283 get_int_prop(options, "width", &panel->width);
284 get_int_prop(options, "avoid", &panel->avoid);
285 get_int_prop(options, "xinerama", &panel->xinerama);
286 get_int_prop(options, "monitor", &panel->monitor);
289 /* 'name' may be NULL or "" to remove the panel */
290 Panel *panel_new(const gchar *name, PanelSide side)
292 guchar *load_path;
293 Panel *panel;
294 GtkWidget *vp, *box, *frame, *align;
295 xmlDocPtr panel_doc = NULL;
296 gboolean need_resave = FALSE;
298 g_return_val_if_fail(side == PANEL_DEFAULT_SIDE ||
299 (side >= 0 && side < PANEL_NUMBER_OF_SIDES), NULL);
300 g_return_val_if_fail(loading_panel == NULL, NULL);
302 if (name && *name == '\0')
303 name = NULL;
305 if (!name)
306 load_path = NULL;
307 else if (strchr(name, '/'))
308 load_path = g_strdup(name);
309 else
311 guchar *leaf;
313 leaf = g_strconcat("pan_", name, NULL);
314 load_path = choices_find_xdg_path_load(leaf, PROJECT, SITE);
315 g_free(leaf);
318 if (load_path && access(load_path, F_OK) == 0)
320 char *saved_side;
321 xmlNodePtr root;
323 panel_doc = xmlParseFile(load_path);
324 root = xmlDocGetRootElement(panel_doc);
326 saved_side = xmlGetProp(root, "side");
327 if (saved_side)
329 PanelSide old_side;
330 old_side = panel_name_to_side(saved_side);
331 g_free(saved_side);
333 if (side == PANEL_DEFAULT_SIDE)
334 side = old_side;
335 else if (side != old_side)
336 need_resave = TRUE;
338 else
339 need_resave = TRUE;
342 if (side == PANEL_DEFAULT_SIDE)
343 side = find_free_side();
345 if (current_panel[side])
347 if (name)
348 number_of_windows++;
349 closing_panel++;
350 gtk_widget_destroy(current_panel[side]->window);
351 closing_panel--;
352 if (name)
353 number_of_windows--;
356 if (name == NULL || *name == '\0')
357 return NULL;
359 panel = g_new(Panel, 1);
360 panel->name = g_strdup(name);
361 panel->side = side;
362 panel->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
363 panel->autoscroll_speed = 0;
365 /* These are fallbacks from legacy global options */
366 panel->style = o_panel_style.int_value;
367 panel->width = o_panel_width.int_value;
368 panel->xinerama = o_panel_xinerama.int_value;
369 panel->monitor = o_panel_monitor.int_value;
370 panel->avoid = o_panel_avoid.int_value;
372 /* Now try to load options from this panel's XML */
373 if (panel_doc)
374 panel_load_options_from_xml(panel, panel_doc);
376 panel_update_geometry(panel);
378 gtk_window_set_resizable(GTK_WINDOW(panel->window), FALSE);
379 gtk_window_set_wmclass(GTK_WINDOW(panel->window), "ROX-Panel", PROJECT);
380 gtk_widget_set_name(panel->window, "rox-panel");
381 gtk_widget_set_events(panel->window,
382 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
383 GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
385 /* We make the panel a drop target only so that we can auto-raise! */
386 gtk_drag_dest_set(panel->window, 0, NULL, 0, GDK_ACTION_PRIVATE);
387 g_signal_connect(panel->window, "drag_leave",
388 G_CALLBACK(panel_drag_leave), panel);
389 g_signal_connect(panel->window, "drag_motion",
390 G_CALLBACK(panel_drag_motion), panel);
392 g_signal_connect(panel->window, "delete-event",
393 G_CALLBACK(panel_delete), panel);
394 g_signal_connect(panel->window, "destroy",
395 G_CALLBACK(panel_destroyed), panel);
396 g_signal_connect(panel->window, "button_press_event",
397 G_CALLBACK(panel_button_press), panel);
398 g_signal_connect(panel->window, "button_release_event",
399 G_CALLBACK(panel_button_release), panel);
400 g_signal_connect(panel->window, "motion-notify-event",
401 G_CALLBACK(panel_motion_event), panel);
402 g_signal_connect(panel->window, "leave-notify-event",
403 G_CALLBACK(panel_leave_event), panel);
405 if (panel->side == PANEL_RIGHT)
406 align = gtk_alignment_new(1.0, 0.0, 0.0, 1.0);
407 else if (panel->side == PANEL_BOTTOM)
408 align = gtk_alignment_new(0.0, 1.0, 1.0, 0.0);
409 else if (panel->side == PANEL_TOP)
410 align = gtk_alignment_new(0.0, 0.0, 1.0, 0.0);
411 else
412 align = gtk_alignment_new(0.0, 0.0, 0.0, 1.0);
414 gtk_container_add(GTK_CONTAINER(panel->window), align);
416 vp = gtk_viewport_new(NULL, NULL);
417 gtk_container_set_resize_mode(GTK_CONTAINER(vp), GTK_RESIZE_PARENT);
418 gtk_viewport_set_shadow_type(GTK_VIEWPORT(vp), GTK_SHADOW_NONE);
419 gtk_container_add(GTK_CONTAINER(align), vp);
421 g_signal_connect(align, "expose-event",
422 G_CALLBACK(draw_panel_edge), panel);
424 if (side == PANEL_TOP || side == PANEL_BOTTOM)
426 panel->adj = gtk_viewport_get_hadjustment(GTK_VIEWPORT(vp));
427 box = gtk_hbox_new(FALSE, 0);
428 panel->before = gtk_hbox_new(FALSE, 0);
429 panel->after = gtk_hbox_new(FALSE, 0);
431 else
433 panel->adj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(vp));
434 box = gtk_vbox_new(FALSE, 0);
435 panel->before = gtk_vbox_new(FALSE, 0);
436 panel->after = gtk_vbox_new(FALSE, 0);
439 gtk_container_add(GTK_CONTAINER(vp), box);
440 gtk_box_pack_start(GTK_BOX(box), panel->before, FALSE, TRUE, 0);
441 gtk_box_pack_end(GTK_BOX(box), panel->after, FALSE, TRUE, 0);
443 frame = make_insert_frame(panel);
444 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
446 /* This is used so that we can find the middle easily! */
447 panel->gap = gtk_event_box_new();
448 gtk_box_pack_start(GTK_BOX(box), panel->gap, FALSE, FALSE, 0);
450 frame = make_insert_frame(panel);
451 g_object_set_data(G_OBJECT(frame), "after", "yes");
452 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
454 if (o_panel_is_dock.int_value)
455 gtk_window_set_type_hint(GTK_WINDOW(panel->window),
456 GDK_WINDOW_TYPE_HINT_DOCK);
458 gtk_widget_realize(panel->window);
459 make_panel_window(panel->window);
460 gtk_window_stick(GTK_WINDOW(panel->window));
462 gtk_widget_show_all(align);
464 loading_panel = panel;
465 if (panel_doc)
467 panel_load_from_xml(panel, panel_doc);
468 xmlFreeDoc(panel_doc);
470 if (need_resave)
471 panel_save(panel);
473 else if (load_path)
475 parse_file(load_path, pan_from_file);
476 info_message(_("Your old panel file has been "
477 "converted to the new XML format."));
478 panel_save(panel);
480 else
482 /* Don't scare users with an empty panel... */
483 guchar *apps;
485 panel_add_item(panel, "~", "Home", FALSE, NULL, NULL, FALSE);
487 apps = pathdup(make_path(app_dir, ".."));
488 if (apps)
490 panel_add_item(panel, apps, "Apps", FALSE, NULL, NULL, FALSE);
491 g_free(apps);
494 loading_panel = NULL;
495 g_free(load_path);
497 current_panel[side] = panel;
499 gtk_widget_queue_resize(box);
500 g_signal_connect(panel->window, "size-request",
501 G_CALLBACK(panel_post_resize), panel);
502 g_signal_connect(panel->window, "size-allocate",
503 G_CALLBACK(reposition_panel), panel);
505 number_of_windows++;
506 gdk_window_lower(panel->window->window);
507 gtk_widget_show(panel->window);
508 /* This has no effect until after window is showing; GTK+ bug? */
509 keep_below(panel->window->window, TRUE);
512 GdkWindow *pinboard;
514 pinboard = pinboard_get_window();
515 /* (if pinboard is NULL, will go right to the back) */
516 window_put_just_above(panel->window->window, pinboard);
519 return panel;
522 /* Externally visible function to add an item to a panel */
523 gboolean panel_add(PanelSide side,
524 const gchar *path, const gchar *label, gboolean after, const gchar *shortcut, const gchar *args,
525 gboolean locked)
527 g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, FALSE);
529 g_return_val_if_fail(current_panel[side] != NULL, FALSE);
531 panel_add_item(current_panel[side], path, label, after, shortcut, args, locked);
533 return TRUE;
536 /* Add the area covered by the panels to the region */
537 void panel_mark_used(GdkRegion *used)
539 int i;
541 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
543 Panel *panel = current_panel[i];
544 GdkRectangle rect;
546 if (!panel)
547 continue;
549 gdk_window_get_root_origin(panel->window->window,
550 &rect.x, &rect.y);
551 rect.width = panel->window->allocation.width;
552 rect.height = panel->window->allocation.height;
554 gdk_region_union_with_rect(used, &rect);
558 /* On xrandr screen size changes, update all panels */
559 void panel_update_size(void)
561 int i;
563 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
565 if (current_panel[i])
567 reposition_panel(current_panel[i]->window,
568 &current_panel[i]->window->allocation,
569 current_panel[i]);
570 gtk_widget_queue_resize(current_panel[i]->window);
575 /****************************************************************
576 * INTERNAL FUNCTIONS *
577 ****************************************************************/
579 /* User has tried to close the panel via the window manager - confirm */
580 static int panel_delete(GtkWidget *widget, GdkEvent *event, Panel *panel)
582 return !confirm(_("You have tried to close a panel via the window "
583 "manager - I usually find that this is accidental... "
584 "really close?"),
585 GTK_STOCK_CLOSE, NULL);
588 static void panel_destroyed(GtkWidget *widget, Panel *panel)
590 if (panel_options_dialog)
592 Panel *dlg_panel = g_object_get_data(G_OBJECT(panel_options_dialog),
593 "rox-panel");
595 if (dlg_panel == panel)
596 gtk_widget_destroy(panel_options_dialog);
599 if (current_panel[panel->side] == panel)
600 current_panel[panel->side] = NULL;
602 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
604 if (current_panel[PANEL_RIGHT])
605 gtk_widget_queue_resize(
606 current_panel[PANEL_RIGHT]->window);
607 if (current_panel[PANEL_LEFT])
608 gtk_widget_queue_resize(
609 current_panel[PANEL_LEFT]->window);
612 if (panel->autoscroll_speed)
613 g_source_remove(panel->autoscroll_to);
615 g_free(panel->name);
616 g_free(panel);
618 one_less_window();
621 static void panel_load_side(Panel *panel, xmlNodePtr side, gboolean after)
623 xmlNodePtr node;
624 char *label, *path, *shortcut, *args, *tmp;
625 gboolean locked;
627 for (node = side->xmlChildrenNode; node; node = node->next)
629 if (node->type != XML_ELEMENT_NODE)
630 continue;
631 if (strcmp(node->name, "icon") != 0)
632 continue;
634 label = xmlGetProp(node, "label");
635 if (!label)
636 label = g_strdup("<missing label>");
637 path = xmlNodeGetContent(node);
638 if (!path)
639 path = g_strdup("<missing path>");
640 shortcut = xmlGetProp(node, "shortcut");
641 args = xmlGetProp(node, "args");
642 tmp = xmlGetProp(node, "locked");
643 if (tmp)
645 locked = text_to_boolean(tmp, FALSE);
646 g_free(tmp);
648 else
649 locked = FALSE;
651 panel_add_item(panel, path, label, after, shortcut, args, locked);
653 g_free(path);
654 g_free(label);
655 g_free(shortcut);
656 g_free(args);
660 /* Create one panel icon for each icon in the doc */
661 static void panel_load_from_xml(Panel *panel, xmlDocPtr doc)
663 xmlNodePtr root;
665 root = xmlDocGetRootElement(doc);
666 panel_load_side(panel, get_subnode(root, NULL, "start"), FALSE);
667 panel_load_side(panel, get_subnode(root, NULL, "end"), TRUE);
670 /* Called for each line in the config file while loading a new panel */
671 static const char *pan_from_file(gchar *line)
673 gchar *sep, *leaf;
675 g_return_val_if_fail(line != NULL, NULL);
676 g_return_val_if_fail(loading_panel != NULL, NULL);
678 if (*line == '\0')
679 return NULL;
681 sep = strpbrk(line, "<>");
682 if (!sep)
683 return _("Missing < or > in panel config file");
685 if (sep != line)
686 leaf = g_strndup(line, sep - line);
687 else
688 leaf = NULL;
690 panel_add_item(loading_panel, sep + 1, leaf, sep[0] == '>',
691 NULL, NULL, FALSE);
693 g_free(leaf);
695 return NULL;
698 static gboolean icon_pointer_in(GtkWidget *widget,
699 GdkEventCrossing *event,
700 Icon *icon)
702 gtk_widget_set_state(widget,
703 icon->selected ? GTK_STATE_SELECTED : GTK_STATE_PRELIGHT);
705 return 0;
708 static gboolean icon_pointer_out(GtkWidget *widget,
709 GdkEventCrossing *event,
710 Icon *icon)
712 gtk_widget_set_state(widget,
713 icon->selected ? GTK_STATE_SELECTED : GTK_STATE_NORMAL);
715 return 0;
718 static void panel_icon_destroyed(PanelIcon *pi)
720 g_return_if_fail(pi->widget != NULL);
722 pi->widget = NULL;
724 g_object_unref(pi);
727 /* Set the tooltip AND hide/show the label */
728 static void panel_icon_set_tip(PanelIcon *pi)
730 XMLwrapper *ai;
731 xmlNode *node;
732 Icon *icon = (Icon *) pi;
734 g_return_if_fail(pi != NULL);
736 if (pi->label)
738 if (panel_want_show_text(pi))
739 gtk_widget_show(pi->label);
740 else
741 gtk_widget_hide(pi->label);
744 if (pi->socket)
745 ai = NULL;
746 else
747 ai = appinfo_get(icon->path, icon->item);
749 if (ai && ((node = xml_get_section(ai, NULL, "Summary"))))
751 guchar *str;
752 str = xmlNodeListGetString(node->doc,
753 node->xmlChildrenNode, 1);
754 if (str)
756 gtk_tooltips_set_tip(tooltips, pi->widget, str, NULL);
757 g_free(str);
760 else if ((!panel_want_show_text(pi)) && !pi->socket)
762 if (icon->item->leafname && icon->item->leafname[0])
763 gtk_tooltips_set_tip(tooltips, pi->widget,
764 icon->item->leafname, NULL);
766 else
767 gtk_tooltips_set_tip(tooltips, pi->widget, NULL, NULL);
769 if (ai)
770 g_object_unref(ai);
773 /* Add an icon with this path to the panel. If after is TRUE then the
774 * icon is added to the right/bottom end of the panel.
776 * If name is NULL a suitable name is taken from path.
778 static void panel_add_item(Panel *panel,
779 const gchar *path,
780 const gchar *name,
781 gboolean after,
782 const gchar *shortcut,
783 const gchar *args,
784 gboolean locked)
786 GtkWidget *widget;
787 PanelIcon *pi;
788 Icon *icon;
790 g_return_if_fail(panel != NULL);
791 g_return_if_fail(path != NULL);
793 widget = gtk_event_box_new();
794 gtk_widget_set_events(widget,
795 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
796 GDK_BUTTON3_MOTION_MASK |
797 GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
798 GDK_BUTTON_RELEASE_MASK);
800 gtk_box_pack_start(GTK_BOX(after ? panel->after : panel->before),
801 widget, FALSE, TRUE, 0);
802 if (after)
803 gtk_box_reorder_child(GTK_BOX(panel->after), widget, 0);
805 gtk_widget_realize(widget);
807 pi = panel_icon_new(panel, path, name);
808 icon = (Icon *) pi;
810 /* Widget takes the initial ref of Icon */
811 g_object_set_data(G_OBJECT(widget), "icon", pi);
813 pi->widget = widget;
814 g_object_ref(widget);
816 gtk_widget_set_name(pi->widget, "panel-icon");
818 g_signal_connect_swapped(widget, "destroy",
819 G_CALLBACK(panel_icon_destroyed), pi);
821 if (icon->item->base_type == TYPE_DIRECTORY)
822 run_applet(pi);
824 g_signal_connect(widget, "button_release_event",
825 G_CALLBACK(icon_button_release), pi);
826 g_signal_connect(widget, "button_press_event",
827 G_CALLBACK(icon_button_press), pi);
828 g_signal_connect(widget, "motion-notify-event",
829 G_CALLBACK(icon_motion_event), pi);
830 g_signal_connect(widget, "enter-notify-event",
831 G_CALLBACK(icon_pointer_in), pi);
832 g_signal_connect(widget, "leave-notify-event",
833 G_CALLBACK(icon_pointer_out), pi);
835 if (!pi->socket)
837 g_signal_connect(widget, "enter-notify-event",
838 G_CALLBACK(enter_icon), pi);
839 g_signal_connect_after(widget, "expose_event",
840 G_CALLBACK(expose_icon), pi);
841 g_signal_connect(widget, "drag_data_get",
842 G_CALLBACK(drag_data_get), NULL);
844 g_signal_connect(widget, "size_request",
845 G_CALLBACK(size_request), pi);
847 drag_set_panel_dest(pi);
849 pi->label = gtk_label_new(icon->item->leafname);
850 gtk_container_add(GTK_CONTAINER(pi->widget), pi->label);
851 gtk_misc_set_alignment(GTK_MISC(pi->label), 0.5, 1);
852 gtk_misc_set_padding(GTK_MISC(pi->label), 1, 2);
855 icon_set_shortcut(icon, shortcut);
856 icon_set_arguments(icon, args);
857 icon->locked = locked;
859 if (!loading_panel)
860 panel_save(panel);
862 panel_icon_set_tip(pi);
863 gtk_widget_show(widget);
866 static gboolean remove_item_from_side(GtkWidget *container, const gchar *path,
867 const gchar *label)
869 GList *kids, *next;
870 gboolean found = FALSE;
872 kids = gtk_container_get_children(GTK_CONTAINER(container));
874 for (next = kids; next; next = next->next)
876 Icon *icon;
877 icon = g_object_get_data(G_OBJECT(next->data), "icon");
878 if (!icon)
879 continue;
881 if ((!path || strcmp(path, icon->src_path) == 0) &&
882 (!label || strcmp(label, icon->item->leafname)==0))
884 icon->locked = FALSE;
885 icon_destroy(icon);
886 found = TRUE;
887 break;
891 g_list_free(kids);
893 return found;
896 /* Remove an item with this path. If more than one item matches, only
897 * one is removed. If label is not NULL then it must also match the item.
898 * Returns TRUE if an item was successfully removed.
900 gboolean panel_remove_item(PanelSide side, const gchar *path,
901 const gchar *label)
903 Panel *panel;
905 g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, FALSE);
907 g_return_val_if_fail(path != NULL || label != NULL, FALSE);
909 panel = current_panel[side];
910 if (!panel)
912 g_warning("No panel on this side of the screen!");
913 return FALSE;
916 if (remove_item_from_side(panel->before, path, label) ||
917 remove_item_from_side(panel->after, path, label))
919 panel_save(panel);
920 panel_update(panel);
921 return TRUE;
924 g_warning("Panel item path='%s', label='%s' not found", path, label);
925 return FALSE;
928 /* Called when Gtk+ wants to know how much space an icon needs.
929 * 'req' is already big enough for the label, if shown.
931 static void size_request(GtkWidget *widget, GtkRequisition *req, PanelIcon *pi)
933 Icon *icon = (Icon *) pi;
934 gboolean horz = (pi->panel->side == PANEL_TOP ||
935 pi->panel->side == PANEL_BOTTOM);
936 int max_width = 100;
937 int max_height = 100;
938 int image_width, image_height;
939 Panel *panel = pi->panel;
941 if (horz)
942 max_height = panel->width - req->height;
943 else
944 max_width = MAX(panel->width, req->width);
946 /* TODO: really need to recreate? */
947 if (pi->image)
948 g_object_unref(pi->image);
950 pi->image = scale_pixbuf(di_image(icon->item)->src_pixbuf,
951 MAX(20, max_width), MAX(20, max_height));
953 image_width = gdk_pixbuf_get_width(pi->image);
954 image_height = gdk_pixbuf_get_height(pi->image);
956 if (req->height > 0 && max_height < req->height)
958 pi->style = TEXT_BESIDE_ICON;
959 req->width += image_width;
960 req->height = MAX(req->height, image_height);
961 gtk_misc_set_alignment(GTK_MISC(pi->label), 1, 0.5);
963 else
965 pi->style = TEXT_UNDER_ICON;
966 req->width = MAX(req->width, image_width);
967 req->height += image_height;
968 gtk_misc_set_alignment(GTK_MISC(pi->label), 0.5, 1);
971 if (horz)
972 req->width += PANEL_ICON_SPACING;
973 else
974 req->height += PANEL_ICON_SPACING;
977 static gint expose_icon(GtkWidget *widget,
978 GdkEventExpose *event,
979 PanelIcon *pi)
981 return draw_icon(widget, &event->area, pi);
984 static gint draw_icon(GtkWidget *widget, GdkRectangle *badarea, PanelIcon *pi)
986 GdkRectangle area;
987 int width, height;
988 Icon *icon = (Icon *) pi;
989 int image_x;
990 int image_y;
991 GdkPixbuf *image;
992 int text_height = 0;
994 gdk_drawable_get_size(widget->window, &area.width, &area.height);
996 if (panel_want_show_text(pi))
997 text_height = pi->label->requisition.height;
999 g_return_val_if_fail(pi->image != NULL, FALSE);
1001 image = pi->image;
1003 width = gdk_pixbuf_get_width(image);
1004 height = gdk_pixbuf_get_height(image);
1006 if (pi->style == TEXT_UNDER_ICON)
1008 image_x = (area.width - width) >> 1;
1009 image_y = (area.height - height - text_height) >> 1;
1011 else
1013 image_x = PANEL_ICON_SPACING - 2;
1014 image_y = (area.height - height) >> 1;
1017 gdk_pixbuf_render_to_drawable_alpha(
1018 image,
1019 widget->window,
1020 0, 0, /* src */
1021 image_x, image_y, /* dest */
1022 width, height,
1023 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
1024 GDK_RGB_DITHER_NORMAL, 0, 0);
1026 if (icon->item->flags & ITEM_FLAG_SYMLINK)
1028 gdk_pixbuf_render_to_drawable_alpha(im_symlink->pixbuf,
1029 widget->window,
1030 0, 0, /* src */
1031 image_x, image_y + 2, /* dest */
1032 -1, -1,
1033 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
1034 GDK_RGB_DITHER_NORMAL, 0, 0);
1036 if (icon->item->flags & ITEM_FLAG_MOUNT_POINT)
1038 MaskedPixmap *mp = icon->item->flags & ITEM_FLAG_MOUNTED
1039 ? im_mounted
1040 : im_unmounted;
1042 gdk_pixbuf_render_to_drawable_alpha(mp->pixbuf,
1043 widget->window,
1044 0, 0, /* src */
1045 image_x, image_y + 2, /* dest */
1046 -1, -1,
1047 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
1048 GDK_RGB_DITHER_NORMAL, 0, 0);
1050 return FALSE;
1053 static void panel_icon_wink(Icon *icon)
1055 PanelIcon *pi = (PanelIcon *) icon;
1057 wink_widget(pi->widget);
1060 /* icon may be NULL if the event is on the background */
1061 static void perform_action(Panel *panel, PanelIcon *pi, GdkEventButton *event)
1063 BindAction action;
1064 Icon *icon = (Icon *) pi;
1066 action = bind_lookup_bev(icon ? BIND_PANEL_ICON : BIND_PANEL, event);
1068 if (pi && pi->socket)
1069 if (action != ACT_POPUP_MENU && action != ACT_MOVE_ICON)
1070 return;
1072 switch (action)
1074 case ACT_OPEN_ITEM:
1075 dnd_motion_ungrab();
1076 wink_widget(pi->widget);
1077 icon_run(icon);
1078 break;
1079 case ACT_EDIT_ITEM:
1080 dnd_motion_ungrab();
1081 wink_widget(pi->widget);
1082 run_diritem(icon->path, icon->item, NULL, NULL, TRUE);
1083 break;
1084 case ACT_POPUP_MENU:
1085 dnd_motion_ungrab();
1086 panel_show_menu(event, pi, panel);
1087 break;
1088 case ACT_MOVE_ICON:
1089 dnd_motion_start(MOTION_REPOSITION);
1090 break;
1091 case ACT_PRIME_AND_SELECT:
1092 if (!icon->selected)
1093 icon_select_only(icon);
1094 dnd_motion_start(MOTION_READY_FOR_DND);
1095 break;
1096 case ACT_PRIME_AND_TOGGLE:
1097 icon_set_selected(icon, !icon->selected);
1098 dnd_motion_start(MOTION_READY_FOR_DND);
1099 break;
1100 case ACT_PRIME_FOR_DND:
1101 dnd_motion_start(MOTION_READY_FOR_DND);
1102 break;
1103 case ACT_TOGGLE_SELECTED:
1104 icon_set_selected(icon, !icon->selected);
1105 break;
1106 case ACT_SELECT_EXCL:
1107 icon_set_selected(icon, TRUE);
1108 break;
1109 case ACT_IGNORE:
1110 break;
1111 case ACT_CLEAR_SELECTION:
1112 dnd_motion_ungrab();
1113 icon_select_only(NULL);
1114 break;
1115 default:
1116 g_warning("Unsupported action : %d\n", action);
1117 break;
1121 static gint panel_button_release(GtkWidget *widget,
1122 GdkEventButton *event,
1123 Panel *panel)
1125 if (dnd_motion_release(event))
1126 return TRUE;
1128 perform_action(panel, NULL, event);
1130 return TRUE;
1133 static gint panel_button_press(GtkWidget *widget,
1134 GdkEventButton *event,
1135 Panel *panel)
1137 if (dnd_motion_press(panel->window, event))
1138 perform_action(panel, NULL, event);
1140 return TRUE;
1143 static gint icon_button_release(GtkWidget *widget,
1144 GdkEventButton *event,
1145 PanelIcon *pi)
1147 if (pi->socket && event->button == 1)
1148 return FALSE; /* Restart button */
1150 if (dnd_motion_release(event))
1151 return TRUE;
1153 perform_action(pi->panel, pi, event);
1155 return TRUE;
1158 static gint icon_button_press(GtkWidget *widget,
1159 GdkEventButton *event,
1160 PanelIcon *pi)
1162 if (pi->socket && event->button == 1)
1163 return FALSE; /* Restart button */
1165 if (dnd_motion_press(widget, event))
1166 perform_action(pi->panel, pi, event);
1168 return TRUE;
1171 /* Stop windows from maximising over all/part of us */
1172 static void panel_setup_struts(Panel *panel, GtkAllocation *alloc)
1174 int thickness;
1175 struct {
1176 gulong left, right, top, bottom;
1177 gulong left_start_y, left_end_y;
1178 gulong right_start_y, right_end_y;
1179 gulong top_start_x, top_end_x;
1180 gulong bottom_start_x, bottom_end_x;
1181 } strut = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1183 if (panel->avoid == FALSE)
1184 thickness = 2;
1185 else if (panel->side == PANEL_TOP ||
1186 panel->side == PANEL_BOTTOM)
1187 thickness = alloc->height;
1188 else
1189 thickness = alloc->width;
1191 switch (panel->side)
1193 case PANEL_LEFT:
1194 if (!panel->xinerama || !monitor_adjacent[panel->monitor].left)
1196 strut.left = panel->geometry.x + thickness;
1197 strut.left_start_y = panel->geometry.y;
1198 strut.left_end_y = panel->geometry.y +
1199 panel->geometry.height - 1;
1201 /* else there is (part of) a monitor
1202 * to the left */
1203 else
1205 thickness = 0;
1207 break;
1208 case PANEL_RIGHT:
1209 if (!panel->xinerama || !monitor_adjacent[panel->monitor].right)
1211 /* RHS of monitor might not abut edge
1212 * of total virtual screen */
1213 strut.right = screen_width -
1214 panel->geometry.x -
1215 panel->geometry.width +
1216 thickness;
1217 strut.right_start_y = panel->geometry.y;
1218 strut.right_end_y = panel->geometry.y +
1219 panel->geometry.height - 1;
1221 /* else there is (part of) a monitor
1222 * to the right */
1223 else
1225 thickness = 0;
1227 break;
1228 case PANEL_TOP:
1229 if (!panel->xinerama || !monitor_adjacent[panel->monitor].top)
1231 strut.top = panel->geometry.y + thickness;
1232 strut.top_start_x = panel->geometry.x;
1233 strut.top_end_x = panel->geometry.x +
1234 panel->geometry.width - 1;
1236 /* else there is (part of) a monitor above */
1237 else
1239 thickness = 0;
1241 break;
1242 default: /* PANEL_BOTTOM */
1243 if (!panel->xinerama ||
1244 !monitor_adjacent[panel->monitor].bottom)
1246 /* Bottom of monitor might not abut
1247 * edge of total virtual screen */
1248 strut.bottom = screen_height -
1249 panel->geometry.y -
1250 panel->geometry.height +
1251 thickness;
1252 strut.bottom_start_x = panel->geometry.x;
1253 strut.bottom_end_x = panel->geometry.x +
1254 panel->geometry.width - 1;
1256 /* else there is (part of) a monitor below */
1257 else
1259 thickness = 0;
1261 break;
1264 if (thickness)
1266 /* Set full-width strut as well as partial in case
1267 * partial isn't supported by wm */
1268 gdk_property_change(panel->window->window,
1269 gdk_atom_intern("_NET_WM_STRUT",
1270 FALSE),
1271 gdk_atom_intern("CARDINAL", FALSE),
1272 32, GDK_PROP_MODE_REPLACE,
1273 (gchar *) &strut, 4);
1274 gdk_property_change(panel->window->window,
1275 gdk_atom_intern("_NET_WM_STRUT_PARTIAL",
1276 FALSE),
1277 gdk_atom_intern("CARDINAL", FALSE),
1278 32, GDK_PROP_MODE_REPLACE,
1279 (gchar *) &strut, 12);
1281 else
1283 gdk_property_delete(panel->window->window,
1284 gdk_atom_intern("_NET_WM_STRUT_PARTIAL",
1285 FALSE));
1286 gdk_property_delete(panel->window->window,
1287 gdk_atom_intern("_NET_WM_STRUT",
1288 FALSE));
1292 static void reposition_panel(GtkWidget *window,
1293 GtkAllocation *alloc, Panel *panel)
1295 int x = panel->geometry.x;
1296 int y = panel->geometry.y;
1297 PanelSide side = panel->side;
1299 if (side == PANEL_LEFT || side == PANEL_RIGHT)
1301 if (side == PANEL_RIGHT)
1302 x += panel->geometry.width - alloc->width;
1304 if (current_panel[PANEL_TOP])
1306 GtkWidget *win = current_panel[PANEL_TOP]->window;
1307 y += win->allocation.height;
1311 if (side == PANEL_BOTTOM)
1312 y += panel->geometry.height - alloc->height;
1314 gtk_window_move(GTK_WINDOW(panel->window), x, y);
1315 gdk_window_move(panel->window->window, x, y);
1317 if (side == PANEL_BOTTOM || side == PANEL_TOP)
1319 if (current_panel[PANEL_RIGHT])
1320 gtk_widget_queue_resize(
1321 current_panel[PANEL_RIGHT]->window);
1322 if (current_panel[PANEL_LEFT])
1323 gtk_widget_queue_resize(
1324 current_panel[PANEL_LEFT]->window);
1327 panel_setup_struts(panel, alloc);
1330 /* Same as drag_set_dest(), but for panel icons */
1331 static void drag_set_panel_dest(PanelIcon *pi)
1333 GtkWidget *obj = pi->widget;
1335 make_drop_target(pi->widget, 0);
1337 g_signal_connect(obj, "drag_motion", G_CALLBACK(drag_motion), pi);
1338 g_signal_connect(obj, "drag_leave", G_CALLBACK(drag_leave), pi);
1339 g_signal_connect(obj, "drag_end", G_CALLBACK(drag_end), pi);
1342 static gboolean drag_motion(GtkWidget *widget,
1343 GdkDragContext *context,
1344 gint x,
1345 gint y,
1346 guint time,
1347 PanelIcon *pi)
1349 GdkDragAction action = context->suggested_action;
1350 const char *type = NULL;
1351 Icon *icon = (Icon *) pi;
1352 DirItem *item = icon->item;
1353 int panel_x, panel_y;
1355 gdk_window_get_pointer(pi->panel->window->window,
1356 &panel_x, &panel_y, NULL);
1357 motion_may_raise(pi->panel, panel_x, panel_y);
1359 /* Should we scroll the panel when dragging? */
1360 if (motion_state != MOTION_REPOSITION)
1361 if (pi->panel->autoscroll_speed == 0)
1362 may_autoscroll(pi->panel);
1364 if (icon->selected)
1365 goto out; /* Can't drag a selection to itself */
1367 type = dnd_motion_item(context, &item);
1369 if ((context->actions & GDK_ACTION_ASK) && o_dnd_left_menu.int_value
1370 && type != drop_dest_prog)
1372 guint state;
1373 gdk_window_get_pointer(NULL, NULL, NULL, &state);
1374 if (state & GDK_BUTTON1_MASK)
1375 action = GDK_ACTION_ASK;
1378 if (!item)
1379 type = NULL;
1380 out:
1381 /* We actually must pretend to accept the drop, even if the
1382 * directory isn't writeable, so that the spring-opening
1383 * thing works.
1386 /* Don't allow drops to non-writeable directories */
1387 if (o_dnd_spring_open.int_value == FALSE &&
1388 type == drop_dest_dir &&
1389 access(icon->path, W_OK) != 0)
1391 type = NULL;
1394 g_dataset_set_data(context, "drop_dest_type", (gpointer) type);
1395 if (type)
1397 gdk_drag_status(context, action, time);
1398 g_dataset_set_data_full(context, "drop_dest_path",
1399 g_strdup(icon->path), g_free);
1400 if (type == drop_dest_dir)
1401 dnd_spring_load(context, NULL);
1403 if (dnd_highlight && dnd_highlight != pi->widget)
1405 gtk_drag_unhighlight(dnd_highlight);
1406 dnd_highlight = NULL;
1409 if (dnd_highlight == NULL)
1411 gtk_drag_highlight(pi->widget);
1412 dnd_highlight = pi->widget;
1416 return type != NULL;
1420 static void add_uri_list(GtkWidget *widget,
1421 GdkDragContext *context,
1422 gint x,
1423 gint y,
1424 GtkSelectionData *selection_data,
1425 guint info,
1426 guint32 time,
1427 Panel *panel)
1429 gboolean after = FALSE;
1430 GList *uris, *next;
1432 if (!selection_data->data)
1433 return;
1435 g_return_if_fail(selection_data->data[selection_data->length] == '\0');
1437 if (g_object_get_data(G_OBJECT(widget), "after"))
1438 after = TRUE;
1440 uris = uri_list_to_glist(selection_data->data);
1442 for (next = uris; next; next = next->next)
1444 guchar *path;
1446 path = get_local_path((EscapedPath *) next->data);
1448 if (path) {
1449 panel_add_item(panel, path, NULL, after, NULL, NULL, FALSE);
1450 g_free(path);
1454 g_list_free(uris);
1457 static void drag_end(GtkWidget *widget,
1458 GdkDragContext *context,
1459 Icon *icon)
1461 if (tmp_icon_selected)
1463 icon_select_only(NULL);
1464 tmp_icon_selected = FALSE;
1468 static void drag_leave(GtkWidget *widget,
1469 GdkDragContext *context,
1470 guint32 time,
1471 Icon *icon)
1473 panel_drag_leave(widget, context, time, ((PanelIcon *) icon)->panel);
1475 if (dnd_highlight && dnd_highlight == widget)
1477 gtk_drag_unhighlight(dnd_highlight);
1478 dnd_highlight = NULL;
1481 dnd_spring_abort();
1484 /* Create XML icon nodes for these widgets.
1485 * Always frees the widgets list.
1487 static void make_widgets(xmlNodePtr side, GList *widgets)
1489 GList *next;
1491 for (next = widgets; next; next = next->next)
1493 Icon *icon;
1494 xmlNodePtr tree;
1496 icon = g_object_get_data(G_OBJECT(next->data), "icon");
1498 if (!icon)
1500 g_warning("Can't find Icon from widget\n");
1501 continue;
1504 tree = xmlNewTextChild(side, NULL, "icon", icon->src_path);
1506 xmlSetProp(tree, "label", icon->item->leafname);
1507 if (icon->shortcut)
1508 xmlSetProp(tree, "shortcut", icon->shortcut);
1509 if (icon->args)
1510 xmlSetProp(tree, "args", icon->args);
1511 if (icon->locked)
1512 xmlSetProp(tree, "locked", "true");
1515 if (widgets)
1516 g_list_free(widgets);
1519 void panel_save(Panel *panel)
1521 xmlDocPtr doc;
1522 xmlNodePtr root;
1523 xmlNodePtr options;
1524 guchar *save = NULL;
1525 guchar *save_new = NULL;
1526 char prop[16];
1528 g_return_if_fail(panel != NULL);
1530 if (strchr(panel->name, '/'))
1531 save = g_strdup(panel->name);
1532 else
1534 guchar *leaf;
1536 leaf = g_strconcat("pan_", panel->name, NULL);
1537 save = choices_find_xdg_path_save(leaf, PROJECT, SITE, TRUE);
1538 g_free(leaf);
1541 if (!save)
1542 return;
1544 doc = xmlNewDoc("1.0");
1545 xmlDocSetRootElement(doc, xmlNewDocNode(doc, NULL, "panel", NULL));
1547 root = xmlDocGetRootElement(doc);
1549 xmlSetProp(root, "side",
1550 panel->side == PANEL_TOP ? "Top" :
1551 panel->side == PANEL_BOTTOM ? "Bottom" :
1552 panel->side == PANEL_LEFT ? "Left" :
1553 "Right");
1555 options = xmlNewChild(root, NULL, "options", NULL);
1556 sprintf(prop, "%d", panel->style);
1557 xmlSetProp(options, "style", prop);
1558 sprintf(prop, "%d", panel->width);
1559 xmlSetProp(options, "width", prop);
1560 sprintf(prop, "%d", panel->avoid);
1561 xmlSetProp(options, "avoid", prop);
1562 sprintf(prop, "%d", panel->xinerama);
1563 xmlSetProp(options, "xinerama", prop);
1564 sprintf(prop, "%d", panel->monitor);
1565 xmlSetProp(options, "monitor", prop);
1567 make_widgets(xmlNewChild(root, NULL, "start", NULL),
1568 gtk_container_get_children(GTK_CONTAINER(panel->before)));
1570 make_widgets(xmlNewChild(root, NULL, "end", NULL),
1571 g_list_reverse(gtk_container_get_children(
1572 GTK_CONTAINER(panel->after))));
1574 save_new = g_strconcat(save, ".new", NULL);
1575 if (save_xml_file(doc, save_new) || rename(save_new, save))
1576 delayed_error(_("Error saving panel %s: %s"),
1577 save, g_strerror(errno));
1578 g_free(save_new);
1580 g_free(save);
1581 if (doc)
1582 xmlFreeDoc(doc);
1585 /* Create a frame widget which can be used to add icons to the panel */
1586 static GtkWidget *make_insert_frame(Panel *panel)
1588 GtkWidget *frame;
1589 GtkTargetEntry target_table[] = {
1590 {"text/uri-list", 0, TARGET_URI_LIST},
1593 frame = gtk_frame_new(NULL);
1594 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
1595 gtk_widget_set_size_request(frame, 16, 16);
1597 g_signal_connect(frame, "drag-motion",
1598 G_CALLBACK(insert_drag_motion), panel);
1599 g_signal_connect(frame, "drag-leave",
1600 G_CALLBACK(panel_drag_leave), panel);
1602 g_signal_connect(frame, "drag-data-received",
1603 G_CALLBACK(add_uri_list), panel);
1604 gtk_drag_dest_set(frame,
1605 GTK_DEST_DEFAULT_ALL,
1606 target_table,
1607 sizeof(target_table) / sizeof(*target_table),
1608 GDK_ACTION_COPY);
1610 return frame;
1613 static gboolean enter_icon(GtkWidget *widget,
1614 GdkEventCrossing *event,
1615 Icon *icon)
1617 icon_may_update(icon);
1618 panel_icon_set_tip((PanelIcon *) icon);
1620 return FALSE;
1623 static gint panel_leave_event(GtkWidget *widget,
1624 GdkEventCrossing *event,
1625 Panel *panel)
1627 GdkWindow *pinboard;
1629 if (event->mode != GDK_CROSSING_NORMAL)
1630 return FALSE; /* Grab for menu, DnD, etc */
1632 keep_below(panel->window->window, TRUE);
1634 /* Shouldn't need this as well as keep_below but some WMs don't
1635 * automatically lower as soon as the hint is set */
1636 pinboard = pinboard_get_window();
1637 window_put_just_above(panel->window->window, pinboard);
1639 return FALSE;
1642 /* If (x, y) is at the edge of the panel then raise */
1643 static void motion_may_raise(Panel *panel, int x, int y)
1645 gboolean raise;
1647 if (panel->side == PANEL_TOP)
1648 raise = y == 0;
1649 else if (panel->side == PANEL_BOTTOM)
1650 raise = y == panel->window->allocation.height - 1;
1651 else if (panel->side == PANEL_LEFT)
1652 raise = x == 0;
1653 else
1654 raise = x == panel->window->allocation.width - 1;
1656 if (raise)
1658 keep_below(panel->window->window, FALSE);
1660 /* Shouldn't need this as well as keep_below but some WMs don't
1661 * automatically raise as soon as the hint is set */
1662 gdk_window_raise(panel->window->window);
1666 static gboolean may_autoscroll(Panel *panel)
1668 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1669 gint max, panel_x, panel_y, delta, new;
1671 if (panel->adj->upper <= panel->adj->page_size)
1672 goto stop_scrolling; /* Can see everything already */
1674 gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
1676 if (horz)
1678 delta = panel_x;
1679 max = panel->window->allocation.width;
1680 if (panel_y < 0 || panel_y > panel->window->allocation.height)
1681 goto stop_scrolling; /* Not over the panel */
1683 else
1685 delta = panel_y;
1686 max = panel->window->allocation.height;
1687 if (panel_x < 0 || panel_x > panel->window->allocation.width)
1688 goto stop_scrolling; /* Not over the panel */
1691 if (delta >= 20 && delta <= max - 20)
1692 goto stop_scrolling; /* Not at either end */
1694 panel->autoscroll_speed = MIN(panel->autoscroll_speed + 2, 200);
1696 new = panel->adj->value - ((delta < 20) ? panel->autoscroll_speed
1697 : -panel->autoscroll_speed);
1698 new = CLAMP(new, 0, panel->adj->upper - panel->adj->page_size);
1699 gtk_adjustment_set_value(panel->adj, new);
1701 panel->autoscroll_to = g_timeout_add(40,
1702 (GSourceFunc) may_autoscroll, panel);
1704 return FALSE;
1706 stop_scrolling:
1707 panel->autoscroll_speed = 0;
1708 return FALSE;
1711 static gint panel_motion_event(GtkWidget *widget,
1712 GdkEventMotion *event,
1713 Panel *panel)
1715 motion_may_raise(panel, event->x, event->y);
1717 if (motion_state != MOTION_REPOSITION)
1718 if (panel->autoscroll_speed == 0)
1719 may_autoscroll(panel);
1721 return FALSE;
1724 static gint icon_motion_event(GtkWidget *widget,
1725 GdkEventMotion *event,
1726 PanelIcon *pi)
1728 Panel *panel = pi->panel;
1729 GList *list, *me;
1730 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1731 int val;
1732 int dir = 0;
1734 if (motion_state == MOTION_READY_FOR_DND)
1736 if (dnd_motion_moved(event))
1737 start_drag(pi, event);
1738 return TRUE;
1740 else if (motion_state != MOTION_REPOSITION)
1741 return FALSE;
1743 list = gtk_container_get_children(GTK_CONTAINER(panel->before));
1744 list = g_list_append(list, NULL); /* The gap in the middle */
1745 list = g_list_concat(list,
1746 gtk_container_get_children(GTK_CONTAINER(panel->after)));
1747 me = g_list_find(list, widget);
1749 g_return_val_if_fail(me != NULL, TRUE);
1751 val = horz ? event->x_root : event->y_root;
1753 if (me->prev)
1755 GtkWidget *prev;
1756 int x, y;
1758 if (me->prev->data)
1759 prev = GTK_WIDGET(me->prev->data);
1760 else
1761 prev = panel->gap;
1763 gdk_window_get_origin(prev->window, &x, &y);
1765 if (val <= (horz ? x : y))
1766 dir = -1;
1769 if (dir == 0 && me->next)
1771 GtkWidget *next;
1772 int x, y, w, h;
1774 if (me->next->data)
1775 next = GTK_WIDGET(me->next->data);
1776 else
1777 next = panel->gap;
1779 gdk_window_get_origin(next->window, &x, &y);
1781 gdk_drawable_get_size(next->window, &w, &h);
1783 x += w;
1784 y += h;
1786 if (val >= (horz ? x : y)-1)
1788 if (next == panel->gap)
1789 dir = +2;
1790 else
1791 dir = +1;
1795 if (dir)
1796 reposition_icon(pi, g_list_index(list, widget) + dir);
1798 return TRUE;
1801 static void reposition_icon_on_side(GtkWidget *side, GtkWidget *widget,
1802 int index)
1804 GList *list;
1806 list = gtk_container_get_children(GTK_CONTAINER(side));
1808 /* Want to move icon to the list in the given 'side'. Is it there
1809 * already?
1812 if (!g_list_find(list, widget))
1814 /* No, reparent */
1815 gtk_grab_remove(widget);
1816 gtk_widget_reparent(widget, side);
1817 dnd_motion_grab_pointer();
1818 gtk_grab_add(widget);
1821 gtk_box_reorder_child(GTK_BOX(side), widget, index);
1823 g_list_free(list);
1826 /* Move icon to this index in the complete widget list.
1827 * 0 makes the icon the left-most icon. The gap in the middle has
1828 * an index number, which allows you to specify that the icon should
1829 * go on the left or right side.
1831 static void reposition_icon(PanelIcon *pi, int index)
1833 Panel *panel = pi->panel;
1834 GtkWidget *widget = pi->widget;
1835 GList *list;
1836 int before_len;
1838 list = gtk_container_get_children(GTK_CONTAINER(panel->before));
1839 before_len = g_list_length(list);
1840 g_list_free(list);
1842 if (index <= before_len)
1843 reposition_icon_on_side(panel->before, widget, index);
1844 else
1845 reposition_icon_on_side(panel->after, widget,
1846 index - (before_len + 1));
1848 panel_save(panel);
1851 static void start_drag(PanelIcon *pi, GdkEventMotion *event)
1853 GtkWidget *widget = pi->widget;
1854 Icon *icon = (Icon *) pi;
1856 if (!icon->selected)
1858 if (event->state & GDK_BUTTON1_MASK)
1860 /* Select just this one */
1861 icon_select_only(icon);
1862 tmp_icon_selected = TRUE;
1864 else
1865 icon_set_selected(icon, TRUE);
1868 g_return_if_fail(icon_selection != NULL);
1870 if (icon_selection->next == NULL)
1871 drag_one_item(widget, event, icon->path, icon->item, NULL);
1872 else
1874 guchar *uri_list;
1876 uri_list = icon_create_uri_list();
1877 drag_selection(widget, event, uri_list);
1878 g_free(uri_list);
1882 static void applet_died(GtkWidget *socket)
1884 gboolean never_plugged;
1886 never_plugged = (!g_object_get_data(G_OBJECT(socket), "lost_plug"))
1887 && !GTK_SOCKET(socket)->plug_window;
1889 if (never_plugged)
1891 report_error(
1892 _("Applet quit without ever creating a widget!"));
1893 gtk_widget_destroy(socket);
1896 gtk_widget_unref(socket);
1899 static void socket_destroyed(GtkWidget *socket, GtkWidget *widget)
1901 g_object_set_data(G_OBJECT(socket), "lost_plug", "yes");
1903 gtk_widget_unref(socket);
1905 gtk_widget_destroy(widget); /* Remove from panel */
1907 if (!closing_panel)
1908 panel_save(g_object_get_data(G_OBJECT(socket), "panel"));
1911 /* Try to run this applet.
1912 * Cases:
1914 * - No executable AppletRun:
1915 * icon->socket == NULL (unchanged) on return.
1917 * Otherwise, create socket (setting icon->socket) and ref it twice.
1919 * - AppletRun quits without connecting a plug:
1920 * On child death lost_plug is unset and socket is empty.
1921 * Unref socket.
1922 * Report error and destroy widget (to 'socket destroyed').
1924 * - AppletRun quits while plug is in socket:
1925 * Unref socket once. Socket will be destroyed later.
1927 * - Socket is destroyed.
1928 * Set lost_plug = "yes" and remove widget from panel.
1929 * Unref socket.
1931 static void run_applet(PanelIcon *pi)
1933 GError *error = NULL;
1934 char *argv[3];
1935 gint pid;
1936 Icon *icon = (Icon *) pi;
1938 argv[0] = (char *) make_path(icon->path, "AppletRun");
1940 if (access(argv[0], X_OK) != 0)
1941 return;
1943 pi->socket = gtk_socket_new();
1945 gtk_container_add(GTK_CONTAINER(pi->widget), pi->socket);
1946 gtk_widget_show_all(pi->socket);
1947 gtk_widget_realize(pi->socket);
1949 /* Always get button-2 events so we can drag */
1950 XGrabButton(gdk_display, Button2, AnyModifier,
1951 GDK_WINDOW_XWINDOW(pi->socket->window),
1952 False,
1953 ButtonPressMask | ButtonReleaseMask | Button2MotionMask,
1954 GrabModeAsync, /* Pointer */
1955 GrabModeAsync, /* Keyboard */
1956 None, None);
1959 gchar *pos;
1960 PanelSide side = pi->panel->side;
1962 /* Set a hint to let applets position their menus correctly */
1963 pos = g_strdup_printf("%s,%d",
1964 side == PANEL_TOP ? "Top" :
1965 side == PANEL_BOTTOM ? "Bottom" :
1966 side == PANEL_LEFT ? "Left" :
1967 "Right", MENU_MARGIN(side));
1968 gdk_property_change(pi->socket->window,
1969 gdk_atom_intern("_ROX_PANEL_MENU_POS", FALSE),
1970 gdk_atom_intern("STRING", FALSE),
1971 8, GDK_PROP_MODE_REPLACE,
1972 pos, strlen(pos));
1973 g_free(pos);
1975 /* Ensure that the properties are set before starting the
1976 * applet.
1978 gdk_flush();
1981 g_object_set_data(G_OBJECT(pi->widget), "icon", pi);
1982 g_object_set_data(G_OBJECT(pi->socket), "panel", pi->panel);
1984 argv[1] = g_strdup_printf("%ld",
1985 GDK_WINDOW_XWINDOW(pi->socket->window));
1986 argv[2] = NULL;
1988 if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
1989 NULL, NULL, &pid, &error))
1991 delayed_error(_("Error running applet:\n%s"), error->message);
1992 g_error_free(error);
1993 gtk_widget_destroy(pi->socket);
1994 pi->socket = NULL;
1996 else
1998 gtk_widget_ref(pi->socket);
1999 on_child_death(pid, (CallbackFn) applet_died, pi->socket);
2001 gtk_widget_ref(pi->socket);
2002 g_signal_connect(pi->socket, "destroy",
2003 G_CALLBACK(socket_destroyed), pi->widget);
2006 g_free(argv[1]);
2009 static void panel_post_resize(GtkWidget *win, GtkRequisition *req, Panel *panel)
2011 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
2013 req->width = panel->geometry.width;
2014 req->height += EDGE_WIDTH;
2016 else
2018 int h = panel->geometry.height;
2020 if (current_panel[PANEL_TOP])
2022 GtkWidget *win = current_panel[PANEL_TOP]->window;
2023 h -= win->allocation.height;
2026 if (current_panel[PANEL_BOTTOM])
2028 GtkWidget *win = current_panel[PANEL_BOTTOM]->window;
2029 h -= win->allocation.height;
2032 req->height = h;
2033 req->width += EDGE_WIDTH;
2037 static void update_side(GtkWidget *side)
2039 GList *kids, *next;
2041 kids = gtk_container_get_children(GTK_CONTAINER(side));
2042 for (next = kids; next; next = next->next)
2044 PanelIcon *pi;
2045 pi = g_object_get_data(next->data, "icon");
2046 panel_icon_set_tip(pi);
2048 g_list_free(kids);
2051 /* Tips or style has changed -- update everything on this panel */
2052 static void panel_set_style(Panel *panel)
2054 update_side(panel->before);
2055 update_side(panel->after);
2056 gtk_widget_queue_resize(panel->window);
2059 static gboolean recreate_panels(char **names)
2061 int i;
2063 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
2065 if (names[i])
2067 panel_new(names[i], i);
2068 g_free(names[i]);
2072 g_free(names);
2074 return FALSE;
2077 static void update_side_size(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 gtk_widget_queue_resize(pi->widget);
2088 g_list_free(kids);
2091 /* Update panel size and redraw */
2092 static void panel_update(Panel *panel)
2094 update_side_size(panel->before);
2095 update_side_size(panel->after);
2096 gtk_widget_queue_resize(panel->window);
2097 gtk_widget_queue_draw(panel->window);
2100 static void panel_style_changed(void)
2102 int i;
2104 if (o_override_redirect.has_changed)
2106 gchar **names;
2108 names = g_new(char *, PANEL_NUMBER_OF_SIDES);
2110 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
2112 Panel *panel = current_panel[i];
2113 names[i] = panel ? g_strdup(panel->name) : NULL;
2114 panel_new(NULL, i);
2117 g_idle_add((GtkFunction) recreate_panels, names);
2121 static gboolean draw_panel_edge(GtkWidget *widget, GdkEventExpose *event,
2122 Panel *panel)
2124 int x, y, width, height;
2126 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
2128 width = panel->geometry.width;
2129 height = EDGE_WIDTH;
2131 x = 0;
2132 if (panel->side == PANEL_BOTTOM)
2133 y = 0;
2134 else
2135 y = widget->allocation.height - EDGE_WIDTH;
2137 else
2139 width = EDGE_WIDTH;
2140 height = panel->geometry.height;
2142 y = 0;
2143 if (panel->side == PANEL_RIGHT)
2144 x = 0;
2145 else
2146 x = widget->allocation.width - EDGE_WIDTH;
2149 gdk_draw_rectangle(widget->window,
2150 widget->style->fg_gc[GTK_STATE_NORMAL], TRUE,
2151 x, y, width, height);
2153 return FALSE;
2156 static gpointer parent_class;
2158 static void panel_icon_destroy(Icon *icon)
2160 PanelIcon *pi = (PanelIcon *) icon;
2162 g_return_if_fail(pi != NULL);
2164 if (pi->image)
2165 g_object_unref(pi->image);
2167 g_return_if_fail(pi->widget != NULL);
2169 gtk_widget_destroy(pi->widget);
2172 static void panel_remove_items(void)
2174 Panel *panel;
2176 g_return_if_fail(icon_selection != NULL);
2178 panel = ((PanelIcon *) icon_selection->data)->panel;
2180 while (icon_selection)
2181 icon_destroy((Icon *) icon_selection->data);
2183 panel_save(panel);
2186 /* Icon's size, shape or appearance has changed - update the display */
2187 static void panel_icon_redraw(Icon *icon)
2189 PanelIcon *pi = (PanelIcon *) icon;
2191 gtk_widget_set_state(pi->widget,
2192 icon->selected ? GTK_STATE_SELECTED
2193 : GTK_STATE_NORMAL);
2195 /* Will regenerate the scaled icon from the new image */
2196 gtk_widget_queue_resize(pi->widget);
2198 panel_icon_set_tip((PanelIcon *) icon);
2201 static void panel_icon_update(Icon *icon)
2203 PanelIcon *pi = (PanelIcon *) icon;
2205 gtk_widget_queue_draw(pi->widget);
2206 gtk_label_set_text(GTK_LABEL(pi->label), icon->item->leafname);
2207 panel_save(pi->panel);
2210 /* The point of this is to clear the selection if the existing icons
2211 * aren't from the same panel...
2213 static gboolean panel_icon_same_group(Icon *icon, Icon *other)
2215 if (IS_PANEL_ICON(other))
2217 PanelIcon *a = (PanelIcon *) icon;
2218 PanelIcon *b = (PanelIcon *) other;
2220 return a->panel == b->panel;
2222 else
2223 return FALSE;
2226 static void panel_icon_class_init(gpointer gclass, gpointer data)
2228 IconClass *icon = (IconClass *) gclass;
2230 parent_class = g_type_class_peek_parent(gclass);
2232 icon->destroy = panel_icon_destroy;
2233 icon->redraw = panel_icon_redraw;
2234 icon->update = panel_icon_update;
2235 icon->remove_items = panel_remove_items;
2236 icon->same_group = panel_icon_same_group;
2237 icon->wink = panel_icon_wink;
2240 static void panel_icon_init(GTypeInstance *object, gpointer gclass)
2242 PanelIcon *pi = (PanelIcon *) object;
2244 pi->widget = NULL;
2245 pi->image = NULL;
2246 pi->label = NULL;
2247 pi->socket = NULL;
2248 pi->style = TEXT_UNDER_ICON;
2251 static GType panel_icon_get_type(void)
2253 static GType type = 0;
2255 if (!type)
2257 static const GTypeInfo info =
2259 sizeof (PanelIconClass),
2260 NULL, /* base_init */
2261 NULL, /* base_finalise */
2262 panel_icon_class_init,
2263 NULL, /* class_finalise */
2264 NULL, /* class_data */
2265 sizeof(PanelIcon),
2266 0, /* n_preallocs */
2267 panel_icon_init
2270 type = g_type_register_static(icon_get_type(),
2271 "PanelIcon", &info, 0);
2274 return type;
2277 static PanelIcon *panel_icon_new(Panel *panel,
2278 const char *pathname,
2279 const char *name)
2281 PanelIcon *pi;
2282 Icon *icon;
2284 pi = g_object_new(panel_icon_get_type(), NULL);
2285 icon = (Icon *) pi;
2287 icon_set_path(icon, pathname, name);
2288 pi->panel = panel;
2290 return pi;
2293 static gboolean panel_want_show_text(PanelIcon *pi)
2295 Icon *icon = (Icon *) pi;
2296 Panel *panel = pi->panel;
2298 if (!icon->item->leafname[0])
2299 return FALSE;
2301 if (panel->style == SHOW_BOTH)
2302 return TRUE;
2303 if (panel->style == SHOW_ICON)
2304 return FALSE;
2306 if (icon->item->flags & ITEM_FLAG_APPDIR)
2307 return FALSE;
2309 if (EXECUTABLE_FILE(icon->item))
2310 return FALSE;
2312 return TRUE;
2315 static void xinerama_sensitive(GladeXML *glade, gboolean sensitive)
2317 gtk_widget_set_sensitive(
2318 glade_xml_get_widget(glade, "panel_xinerama_monitor"),
2319 sensitive);
2322 static void panel_setup_options_dialog(GladeXML *glade, Panel *panel)
2324 char *wnm;
2326 wnm = g_strdup_printf("panel_style_radio_%d", panel->style);
2327 gtk_toggle_button_set_active(
2328 GTK_TOGGLE_BUTTON(glade_xml_get_widget(glade, wnm)),
2329 TRUE);
2330 gtk_spin_button_set_value(
2331 GTK_SPIN_BUTTON(glade_xml_get_widget(glade, "panel_width")),
2332 panel->width);
2333 gtk_toggle_button_set_active(
2334 GTK_TOGGLE_BUTTON(glade_xml_get_widget(glade, "panel_avoid")),
2335 panel->avoid);
2336 gtk_toggle_button_set_active(
2337 GTK_TOGGLE_BUTTON(glade_xml_get_widget(glade,
2338 "panel_xinerama_confine")),
2339 panel->xinerama);
2340 gtk_spin_button_set_adjustment(
2341 GTK_SPIN_BUTTON(glade_xml_get_widget(glade, "panel_xinerama_monitor")),
2342 GTK_ADJUSTMENT(gtk_adjustment_new(MAX(0, panel->monitor),
2343 0, n_monitors - 1, 1, 10, 1)));
2344 xinerama_sensitive(glade, panel->xinerama);
2347 static void panel_show_options(Panel *panel)
2349 GtkWidget *dialog;
2350 gboolean already_showing = FALSE;
2351 GladeXML *glade = NULL;
2353 if (panel_options_dialog)
2355 dialog = panel_options_dialog;
2356 already_showing = TRUE;
2357 glade = glade_get_widget_tree(dialog);
2359 else
2361 glade = get_glade_xml("Panel Options");
2362 dialog = glade_xml_get_widget(glade, "Panel Options");
2363 panel_options_dialog = dialog;
2364 g_signal_connect(dialog, "destroy",
2365 G_CALLBACK(gtk_widget_destroyed),
2366 &panel_options_dialog);
2367 glade_xml_signal_autoconnect(glade);
2368 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CLOSE);
2370 g_object_set_data(G_OBJECT(panel_options_dialog), "rox-panel", panel);
2372 panel_setup_options_dialog(glade, panel);
2374 if (already_showing)
2376 GtkWindow *win = GTK_WINDOW(dialog);
2378 gtk_widget_hide(dialog);
2379 /* This extra set_position() should ensure it moves to new position
2380 * under pointer */
2381 gtk_window_set_position(win, GTK_WIN_POS_CENTER_ALWAYS);
2382 gtk_window_set_position(win, GTK_WIN_POS_MOUSE);
2383 gtk_window_present(win);
2385 else
2387 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
2388 gtk_widget_show_all(dialog);
2392 static void panel_position_menu(GtkMenu *menu, gint *x, gint *y,
2393 gboolean *push_in, gpointer data)
2395 int *pos = (int *) data;
2396 GtkRequisition requisition;
2397 int margin = pos[2];
2398 int mon = pos[3];
2399 int mon_right = monitor_geom[mon].x +
2400 monitor_geom[mon].width;
2401 int mon_bottom = monitor_geom[mon].y +
2402 monitor_geom[mon].height;
2404 gtk_widget_size_request(GTK_WIDGET(menu), &requisition);
2406 if (pos[0] == -1)
2407 *x = mon_right - margin - requisition.width;
2408 else if (pos[0] == -2)
2409 *x = monitor_geom[mon].x + margin;
2410 else
2411 *x = pos[0] - (requisition.width >> 2);
2413 if (pos[1] == -1)
2414 *y = mon_bottom - margin - requisition.height;
2415 else if (pos[1] == -2)
2416 *y = monitor_geom[mon].y + margin;
2417 else
2418 *y = pos[1] - (requisition.height >> 2);
2420 *x = CLAMP(*x, 0, mon_right - requisition.width);
2421 *y = CLAMP(*y, 0, mon_bottom - requisition.height);
2423 *push_in = FALSE;
2426 static void side_radio_toggled(GtkCheckMenuItem *item, Panel *panel)
2428 PanelSide new_side;
2429 PanelSide old_side;
2430 char *name, *other_side_name;
2432 if (!gtk_check_menu_item_get_active(item))
2433 return;
2434 new_side = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(item),
2435 "rox-panel-side"));
2436 old_side = panel->side;
2437 if (new_side == old_side)
2438 return;
2440 name = g_strdup(panel->name);
2441 other_side_name = current_panel[new_side]
2442 ? g_strdup(current_panel[new_side]->name)
2443 : NULL;
2445 panel_new(name, new_side);
2446 panel_new(other_side_name, old_side);
2448 g_free(name);
2449 g_free(other_side_name);
2452 static void append_pos_to_menu(GtkWidget *menu, const char *label,
2453 PanelSide side, PanelSide current_side, GSList **pgroup, Panel *panel)
2455 GtkWidget *item = gtk_radio_menu_item_new_with_label(*pgroup, label);
2457 *pgroup = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item));
2458 if (side == current_side)
2459 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2460 g_object_set_data(G_OBJECT(item), "rox-panel-side", GINT_TO_POINTER(side));
2461 g_signal_connect(item, "toggled", G_CALLBACK(side_radio_toggled), panel);
2462 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2463 gtk_widget_show(item);
2466 static void panel_show_menu(GdkEventButton *event, PanelIcon *pi, Panel *panel)
2468 GtkWidget *option_item;
2469 PanelSide side = panel->side;
2470 int pos[4];
2471 GtkWidget *pos_submenu;
2472 GtkWidget *change_side_item;
2473 GSList *pos_radio_group = NULL;
2475 pos[0] = event->x_root;
2476 pos[1] = event->y_root;
2477 pos[2] = MENU_MARGIN(side);
2478 /* FIXME: Should we read screen from event's window rather than
2479 * using default? */
2480 pos[3] = gdk_screen_get_monitor_at_point(
2481 gdk_screen_get_default(),
2482 event->x_root, event->y_root);
2484 option_item = gtk_image_menu_item_new_with_label(_("Panel Options..."));
2485 g_signal_connect_swapped(option_item, "activate",
2486 G_CALLBACK(panel_show_options), panel);
2488 pos_submenu = gtk_menu_new();
2489 append_pos_to_menu(pos_submenu, _("Top Edge"), PANEL_TOP, side,
2490 &pos_radio_group, panel);
2491 append_pos_to_menu(pos_submenu, _("Bottom Edge"), PANEL_BOTTOM, side,
2492 &pos_radio_group, panel);
2493 append_pos_to_menu(pos_submenu, _("Left Edge"), PANEL_LEFT, side,
2494 &pos_radio_group, panel);
2495 append_pos_to_menu(pos_submenu, _("Right Edge"), PANEL_RIGHT, side,
2496 &pos_radio_group, panel);
2497 change_side_item = gtk_menu_item_new_with_label(_("Change panel side"));
2498 gtk_menu_item_set_submenu(GTK_MENU_ITEM(change_side_item), pos_submenu);
2500 icon_prepare_menu((Icon *) pi, option_item, change_side_item, NULL);
2502 if (side == PANEL_LEFT)
2503 pos[0] = -2;
2504 else if (side == PANEL_RIGHT)
2505 pos[0] = -1;
2507 if (side == PANEL_TOP)
2508 pos[1] = -2;
2509 else if (side == PANEL_BOTTOM)
2510 pos[1] = -1;
2512 gtk_menu_popup(GTK_MENU(icon_menu), NULL, NULL,
2513 panel_position_menu,
2514 (gpointer) pos, event->button, event->time);
2517 /* Note: also called from icon handler */
2518 static gboolean panel_drag_motion(GtkWidget *widget,
2519 GdkDragContext *context,
2520 gint x,
2521 gint y,
2522 guint time,
2523 Panel *panel)
2525 int panel_x, panel_y;
2527 gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
2529 motion_may_raise(panel, panel_x, panel_y);
2530 gdk_drag_status(context, 0, time);
2532 return TRUE;
2535 static gboolean insert_drag_motion(GtkWidget *widget,
2536 GdkDragContext *context,
2537 gint x,
2538 gint y,
2539 guint time,
2540 Panel *panel)
2542 int panel_x, panel_y;
2544 gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
2545 motion_may_raise(panel, panel_x, panel_y);
2547 return FALSE;
2550 /* Note: also called from icon handler */
2551 static void panel_drag_leave(GtkWidget *widget,
2552 GdkDragContext *context,
2553 guint32 time,
2554 Panel *panel)
2556 GdkWindow *pinboard, *window;
2557 GtkAllocation *alloc = &panel->window->allocation;
2558 int x, y;
2560 window = panel->window->window;
2561 gdk_window_get_pointer(window, &x, &y, NULL);
2562 if (x < 0 || y < 0 || x > alloc->width || y > alloc->height)
2564 keep_below(panel->window->window, TRUE);
2566 /* Shouldn't need this as well as keep_below but some WMs don't
2567 * automatically lower as soon as the hint is set */
2568 pinboard = pinboard_get_window();
2569 window_put_just_above(panel->window->window, pinboard);
2573 static void panel_update_geometry(Panel *panel)
2575 if (panel->xinerama && panel->monitor >= n_monitors)
2577 g_warning(_("Xinerama monitor %d unavailable"), panel->monitor);
2578 panel->xinerama = FALSE;
2581 if (panel->xinerama)
2583 panel->geometry = monitor_geom[panel->monitor];
2585 else
2587 panel->geometry.x = panel->geometry.y = 0;
2588 panel->geometry.width = screen_width;
2589 panel->geometry.height = screen_height;
2593 static GList *build_monitor_number(Option *option, xmlNode *node, guchar *label)
2595 GtkObject *adj;
2597 adj = gtk_adjustment_new(MAX(0, panel_monitor),
2598 0, n_monitors - 1, 1, 10, 1);
2599 return build_numentry_base(option, node, label, GTK_ADJUSTMENT(adj));
2602 /* Returns PANEL_NUMBER_OF_SIDES if name is invalid */
2603 PanelSide panel_name_to_side(gchar *side)
2605 if (strcmp(side, "Top") == 0)
2606 return PANEL_TOP;
2607 else if (strcmp(side, "Bottom") == 0)
2608 return PANEL_BOTTOM;
2609 else if (strcmp(side, "Left") == 0)
2610 return PANEL_LEFT;
2611 else if (strcmp(side, "Right") == 0)
2612 return PANEL_RIGHT;
2613 else
2614 g_warning("Unknown panel side '%s'", side);
2615 return PANEL_NUMBER_OF_SIDES;
2618 inline static Panel *panel_from_opts_widget(GtkWidget *widget)
2620 return g_object_get_data(G_OBJECT(gtk_widget_get_toplevel(widget)),
2621 "rox-panel");
2624 static void panel_style_radio_toggled(GtkToggleButton *widget, int style)
2626 Panel *panel;
2628 if (!gtk_toggle_button_get_active(widget))
2629 return;
2630 panel = panel_from_opts_widget(GTK_WIDGET(widget));
2631 if (style != panel->style)
2633 panel->style = style;
2634 panel_set_style(panel);
2635 panel_save(panel);
2639 static void panel_xinerama_changed(Panel *panel)
2641 panel_update_geometry(panel);
2642 reposition_panel(panel->window, &panel->window->allocation, panel);
2643 gtk_widget_queue_resize(panel->window);
2644 panel_save(panel);
2647 /* Handlers autoconnected by glade can't be static but aren't called from
2648 * elsewhere so declare prototypes first to prevent warnings */
2649 void panel_style_radio_0_toggled_cb(GtkToggleButton *widget);
2650 void panel_style_radio_1_toggled_cb(GtkToggleButton *widget);
2651 void panel_style_radio_2_toggled_cb(GtkToggleButton *widget);
2652 void panel_width_changed_cb(GtkSpinButton *widget);
2653 void panel_avoid_toggled_cb(GtkToggleButton *widget);
2654 void panel_xinerama_confine_toggled_cb(GtkWidget *widget);
2655 void panel_xinerama_monitor_changed_cb(GtkSpinButton *widget);
2657 void panel_style_radio_0_toggled_cb(GtkToggleButton *widget)
2659 panel_style_radio_toggled(widget, 0);
2662 void panel_style_radio_1_toggled_cb(GtkToggleButton *widget)
2664 panel_style_radio_toggled(widget, 1);
2667 void panel_style_radio_2_toggled_cb(GtkToggleButton *widget)
2669 panel_style_radio_toggled(widget, 2);
2672 void panel_width_changed_cb(GtkSpinButton *widget)
2674 Panel *panel = panel_from_opts_widget(GTK_WIDGET(widget));
2675 int width = gtk_spin_button_get_value_as_int(widget);
2677 if (width != panel->width)
2679 panel->width = width;
2680 panel_update(panel);
2681 panel_save(panel);
2685 void panel_avoid_toggled_cb(GtkToggleButton *widget)
2687 Panel *panel = panel_from_opts_widget(GTK_WIDGET(widget));
2688 gboolean avoid = gtk_toggle_button_get_active(widget);
2690 if (avoid != panel->avoid)
2692 panel->avoid = avoid;
2693 panel_setup_struts(panel, &panel->window->allocation);
2694 panel_save(panel);
2698 void panel_xinerama_confine_toggled_cb(GtkWidget *widget)
2700 Panel *panel = panel_from_opts_widget(widget);
2701 gboolean xinerama = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
2703 xinerama_sensitive(glade_get_widget_tree(gtk_widget_get_toplevel(widget)),
2704 xinerama);
2705 if (xinerama != panel->xinerama)
2707 panel->xinerama = xinerama;
2708 panel_xinerama_changed(panel);
2712 void panel_xinerama_monitor_changed_cb(GtkSpinButton *widget)
2714 Panel *panel = panel_from_opts_widget(GTK_WIDGET(widget));
2715 int monitor = gtk_spin_button_get_value_as_int(widget);
2717 if (monitor != panel->monitor)
2719 panel->monitor = monitor;
2720 panel_xinerama_changed(panel);