More conversion of panel options to per-panel:
[rox-filer.git] / ROX-Filer / src / panel.c
blob1915556dac04b9e2881d15f567764354ba218ef6
1 /*
2 * ROX-Filer, filer for the ROX desktop project
3 * Copyright (C) 2006, Thomas Leonard and others (see changelog for details).
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
17 * Place, Suite 330, Boston, MA 02111-1307 USA
20 /* panel.c - code for dealing with panel windows */
22 #include "config.h"
24 #include <stdio.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <ctype.h>
28 #include <unistd.h>
29 #include <libxml/parser.h>
31 #include <gtk/gtk.h>
32 #include <gdk/gdkx.h>
34 #include "global.h"
36 #include "panel.h"
37 #include "options.h"
38 #include "choices.h"
39 #include "main.h"
40 #include "type.h"
41 #include "gui_support.h"
42 #include "diritem.h"
43 #include "pixmaps.h"
44 #include "filer.h"
45 #include "display.h"
46 #include "bind.h"
47 #include "dnd.h"
48 #include "support.h"
49 #include "icon.h"
50 #include "run.h"
51 #include "appinfo.h"
52 #include "pixmaps.h"
53 #include "xml.h"
54 #include "pinboard.h" /* For pinboard_get_window() */
56 /* The width of the separator at the inner edge of the panel */
57 #define EDGE_WIDTH 1
59 /* The gap between panel icons */
60 #define PANEL_ICON_SPACING 8
62 enum {TEXT_BESIDE_ICON, TEXT_UNDER_ICON};
64 static gboolean tmp_icon_selected = FALSE; /* When dragging */
66 typedef struct _PanelIconClass PanelIconClass;
67 typedef struct _PanelIcon PanelIcon;
69 struct _PanelIconClass {
70 IconClass parent;
73 struct _PanelIcon {
74 Icon icon;
75 GdkPixbuf *image;
77 Panel *panel;
78 GtkWidget *widget; /* The drawing area for the icon */
79 GtkWidget *label;
80 GtkWidget *socket; /* For applets */
82 int style;
85 #define PANEL_ICON(obj) GTK_CHECK_CAST((obj), panel_icon_get_type(), PanelIcon)
86 #define IS_PANEL_ICON(obj) \
87 G_TYPE_CHECK_INSTANCE_TYPE((obj), panel_icon_get_type())
89 Panel *current_panel[PANEL_NUMBER_OF_SIDES];
91 /* NULL => Not loading a panel */
92 static Panel *loading_panel = NULL;
94 static GtkWidget *panel_options_dialog = NULL;
96 /* Static prototypes */
97 static int panel_delete(GtkWidget *widget, GdkEvent *event, Panel *panel);
98 static void panel_destroyed(GtkWidget *widget, Panel *panel);
99 static const char *pan_from_file(gchar *line);
100 static gint icon_button_release(GtkWidget *widget,
101 GdkEventButton *event,
102 PanelIcon *pi);
103 static gint icon_button_press(GtkWidget *widget,
104 GdkEventButton *event,
105 PanelIcon *pi);
106 static void reposition_panel(GtkWidget *window,
107 GtkAllocation *alloc, Panel *panel);
108 static gint expose_icon(GtkWidget *widget,
109 GdkEventExpose *event,
110 PanelIcon *pi);
111 static gint draw_icon(GtkWidget *widget,
112 GdkRectangle *badarea,
113 PanelIcon *pi);
114 static gint panel_button_release(GtkWidget *widget,
115 GdkEventButton *event,
116 Panel *panel);
117 static gint panel_button_press(GtkWidget *widget,
118 GdkEventButton *event,
119 Panel *panel);
120 static void panel_post_resize(GtkWidget *box,
121 GtkRequisition *req, Panel *panel);
122 static void drag_set_panel_dest(PanelIcon *pi);
123 static void add_uri_list(GtkWidget *widget,
124 GdkDragContext *context,
125 gint x,
126 gint y,
127 GtkSelectionData *selection_data,
128 guint info,
129 guint32 time,
130 Panel *panel);
131 static void panel_add_item(Panel *panel,
132 const gchar *path,
133 const gchar *name,
134 gboolean after,
135 const gchar *shortcut,
136 const gchar *args,
137 gboolean locked);
138 static gboolean panel_drag_motion(GtkWidget *widget,
139 GdkDragContext *context,
140 gint x,
141 gint y,
142 guint time,
143 Panel *panel);
144 static gboolean insert_drag_motion(GtkWidget *widget,
145 GdkDragContext *context,
146 gint x,
147 gint y,
148 guint time,
149 Panel *panel);
150 static gboolean drag_motion(GtkWidget *widget,
151 GdkDragContext *context,
152 gint x,
153 gint y,
154 guint time,
155 PanelIcon *pi);
156 static void panel_drag_leave(GtkWidget *widget,
157 GdkDragContext *context,
158 guint32 time,
159 Panel *panel);
160 static void drag_leave(GtkWidget *widget,
161 GdkDragContext *context,
162 guint32 time,
163 Icon *icon);
164 static GtkWidget *make_insert_frame(Panel *panel);
165 static gboolean enter_icon(GtkWidget *widget,
166 GdkEventCrossing *event,
167 Icon *icon);
168 static gint icon_motion_event(GtkWidget *widget,
169 GdkEventMotion *event,
170 PanelIcon *pi);
171 static gint panel_leave_event(GtkWidget *widget,
172 GdkEventCrossing *event,
173 Panel *panel);
174 static gint panel_motion_event(GtkWidget *widget,
175 GdkEventMotion *event,
176 Panel *panel);
177 static void reposition_icon(PanelIcon *pi, int index);
178 static void start_drag(PanelIcon *pi, GdkEventMotion *event);
179 static void drag_end(GtkWidget *widget,
180 GdkDragContext *context,
181 Icon *icon);
182 static void perform_action(Panel *panel,
183 PanelIcon *pi,
184 GdkEventButton *event);
185 static void run_applet(PanelIcon *pi);
186 static void size_request(GtkWidget *widget, GtkRequisition *req, PanelIcon *pi);
187 static void panel_load_from_xml(Panel *panel, xmlDocPtr doc);
188 static gboolean draw_panel_edge(GtkWidget *widget, GdkEventExpose *event,
189 Panel *panel);
190 static PanelIcon *panel_icon_new(Panel *panel,
191 const char *pathname,
192 const char *name);
193 static GType panel_icon_get_type(void);
194 static gboolean panel_want_show_text(PanelIcon *pi);
195 static void panel_show_menu(GdkEventButton *event, PanelIcon *pi, Panel *panel);
196 static void panel_style_changed(void);
197 static void motion_may_raise(Panel *panel, int x, int y);
198 static void panel_update(Panel *panel);
199 static GList *build_monitor_number(Option *option,
200 xmlNode *node, guchar *label);
201 static gboolean may_autoscroll(Panel *panel);
202 static void panel_update_geometry(Panel *panel);
205 static GtkWidget *dnd_highlight = NULL; /* (stops flickering) */
207 #define SHOW_BOTH 0
208 #define SHOW_APPS_SMALL 1
209 #define SHOW_ICON 2
210 static Option o_panel_style;
211 static Option o_panel_width;
212 static Option o_panel_xinerama;
213 static Option o_panel_monitor;
214 static Option o_panel_avoid;
215 static Option o_panel_is_dock;
217 static gint panel_monitor = -1;
219 static int closing_panel = 0; /* Don't panel_save; destroying! */
221 /****************************************************************
222 * EXTERNAL INTERFACE *
223 ****************************************************************/
225 void panel_init(void)
227 option_add_int(&o_panel_style, "panel_style", SHOW_APPS_SMALL);
228 option_add_int(&o_panel_width, "panel_width", 52);
230 option_add_int(&o_panel_xinerama, "panel_xinerama", 0);
231 option_add_int(&o_panel_monitor, "panel_monitor", 0);
233 option_add_int(&o_panel_avoid, "panel_avoid", TRUE);
234 option_add_int(&o_panel_is_dock, "panel_is_dock", FALSE);
236 option_add_notify(panel_style_changed);
238 option_register_widget("monitor-number", build_monitor_number);
241 /* Return a free edge for a new panel.
242 * If no edge is free, returns PANEL_BOTTOM.
244 static PanelSide find_free_side()
246 if (!current_panel[PANEL_BOTTOM])
247 return PANEL_BOTTOM;
249 if (!current_panel[PANEL_TOP])
250 return PANEL_TOP;
252 if (!current_panel[PANEL_LEFT])
253 return PANEL_LEFT;
255 if (!current_panel[PANEL_RIGHT])
256 return PANEL_RIGHT;
258 return PANEL_BOTTOM;
261 /* 'name' may be NULL or "" to remove the panel */
262 Panel *panel_new(const gchar *name, PanelSide side)
264 guchar *load_path;
265 Panel *panel;
266 GtkWidget *vp, *box, *frame, *align;
267 xmlDocPtr panel_doc = NULL;
268 gboolean need_resave = FALSE;
270 g_return_val_if_fail(side == PANEL_DEFAULT_SIDE ||
271 (side >= 0 && side < PANEL_NUMBER_OF_SIDES), NULL);
272 g_return_val_if_fail(loading_panel == NULL, NULL);
274 if (name && *name == '\0')
275 name = NULL;
277 if (!name)
278 load_path = NULL;
279 else if (strchr(name, '/'))
280 load_path = g_strdup(name);
281 else
283 guchar *leaf;
285 leaf = g_strconcat("pan_", name, NULL);
286 load_path = choices_find_xdg_path_load(leaf, PROJECT, SITE);
287 g_free(leaf);
290 if (load_path && access(load_path, F_OK) == 0)
292 char *saved_side;
293 xmlNodePtr root;
295 panel_doc = xmlParseFile(load_path);
296 root = xmlDocGetRootElement(panel_doc);
298 saved_side = xmlGetProp(root, "side");
299 if (saved_side)
301 PanelSide old_side;
302 old_side = panel_name_to_side(saved_side);
303 g_free(saved_side);
305 if (side == PANEL_DEFAULT_SIDE)
306 side = old_side;
307 else if (side != old_side)
308 need_resave = TRUE;
310 else
311 need_resave = TRUE;
314 if (side == PANEL_DEFAULT_SIDE)
315 side = find_free_side();
317 if (current_panel[side])
319 if (name)
320 number_of_windows++;
321 closing_panel++;
322 gtk_widget_destroy(current_panel[side]->window);
323 closing_panel--;
324 if (name)
325 number_of_windows--;
328 if (name == NULL || *name == '\0')
329 return NULL;
331 panel = g_new(Panel, 1);
332 panel->name = g_strdup(name);
333 panel->side = side;
334 panel->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
335 panel->autoscroll_speed = 0;
337 panel->style = o_panel_style.int_value;
338 panel->width = o_panel_width.int_value;
339 panel->xinerama = o_panel_xinerama.int_value;
340 panel->monitor = o_panel_monitor.int_value;
341 panel->avoid = o_panel_avoid.int_value;
342 panel_update_geometry(panel);
344 gtk_window_set_resizable(GTK_WINDOW(panel->window), FALSE);
345 gtk_window_set_wmclass(GTK_WINDOW(panel->window), "ROX-Panel", PROJECT);
346 gtk_widget_set_name(panel->window, "rox-panel");
347 gtk_widget_set_events(panel->window,
348 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
349 GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
351 /* We make the panel a drop target only so that we can auto-raise! */
352 gtk_drag_dest_set(panel->window, 0, NULL, 0, GDK_ACTION_PRIVATE);
353 g_signal_connect(panel->window, "drag_leave",
354 G_CALLBACK(panel_drag_leave), panel);
355 g_signal_connect(panel->window, "drag_motion",
356 G_CALLBACK(panel_drag_motion), panel);
358 g_signal_connect(panel->window, "delete-event",
359 G_CALLBACK(panel_delete), panel);
360 g_signal_connect(panel->window, "destroy",
361 G_CALLBACK(panel_destroyed), panel);
362 g_signal_connect(panel->window, "button_press_event",
363 G_CALLBACK(panel_button_press), panel);
364 g_signal_connect(panel->window, "button_release_event",
365 G_CALLBACK(panel_button_release), panel);
366 g_signal_connect(panel->window, "motion-notify-event",
367 G_CALLBACK(panel_motion_event), panel);
368 g_signal_connect(panel->window, "leave-notify-event",
369 G_CALLBACK(panel_leave_event), panel);
371 if (panel->side == PANEL_RIGHT)
372 align = gtk_alignment_new(1.0, 0.0, 0.0, 1.0);
373 else if (panel->side == PANEL_BOTTOM)
374 align = gtk_alignment_new(0.0, 1.0, 1.0, 0.0);
375 else if (panel->side == PANEL_TOP)
376 align = gtk_alignment_new(0.0, 0.0, 1.0, 0.0);
377 else
378 align = gtk_alignment_new(0.0, 0.0, 0.0, 1.0);
380 gtk_container_add(GTK_CONTAINER(panel->window), align);
382 vp = gtk_viewport_new(NULL, NULL);
383 gtk_container_set_resize_mode(GTK_CONTAINER(vp), GTK_RESIZE_PARENT);
384 gtk_viewport_set_shadow_type(GTK_VIEWPORT(vp), GTK_SHADOW_NONE);
385 gtk_container_add(GTK_CONTAINER(align), vp);
387 g_signal_connect(align, "expose-event",
388 G_CALLBACK(draw_panel_edge), panel);
390 if (side == PANEL_TOP || side == PANEL_BOTTOM)
392 panel->adj = gtk_viewport_get_hadjustment(GTK_VIEWPORT(vp));
393 box = gtk_hbox_new(FALSE, 0);
394 panel->before = gtk_hbox_new(FALSE, 0);
395 panel->after = gtk_hbox_new(FALSE, 0);
397 else
399 panel->adj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(vp));
400 box = gtk_vbox_new(FALSE, 0);
401 panel->before = gtk_vbox_new(FALSE, 0);
402 panel->after = gtk_vbox_new(FALSE, 0);
405 gtk_container_add(GTK_CONTAINER(vp), box);
406 gtk_box_pack_start(GTK_BOX(box), panel->before, FALSE, TRUE, 0);
407 gtk_box_pack_end(GTK_BOX(box), panel->after, FALSE, TRUE, 0);
409 frame = make_insert_frame(panel);
410 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
412 /* This is used so that we can find the middle easily! */
413 panel->gap = gtk_event_box_new();
414 gtk_box_pack_start(GTK_BOX(box), panel->gap, FALSE, FALSE, 0);
416 frame = make_insert_frame(panel);
417 g_object_set_data(G_OBJECT(frame), "after", "yes");
418 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
420 if (o_panel_is_dock.int_value)
421 gtk_window_set_type_hint(GTK_WINDOW(panel->window),
422 GDK_WINDOW_TYPE_HINT_DOCK);
424 gtk_widget_realize(panel->window);
425 make_panel_window(panel->window);
426 gtk_window_stick(GTK_WINDOW(panel->window));
428 gtk_widget_show_all(align);
430 loading_panel = panel;
431 if (panel_doc)
433 panel_load_from_xml(panel, panel_doc);
434 xmlFreeDoc(panel_doc);
436 if (need_resave)
437 panel_save(panel);
439 else if (load_path)
441 parse_file(load_path, pan_from_file);
442 info_message(_("Your old panel file has been "
443 "converted to the new XML format."));
444 panel_save(panel);
446 else
448 /* Don't scare users with an empty panel... */
449 guchar *apps;
451 panel_add_item(panel, "~", "Home", FALSE, NULL, NULL, FALSE);
453 apps = pathdup(make_path(app_dir, ".."));
454 if (apps)
456 panel_add_item(panel, apps, "Apps", FALSE, NULL, NULL, FALSE);
457 g_free(apps);
460 loading_panel = NULL;
461 g_free(load_path);
463 current_panel[side] = panel;
465 gtk_widget_queue_resize(box);
466 g_signal_connect(panel->window, "size-request",
467 G_CALLBACK(panel_post_resize), panel);
468 g_signal_connect(panel->window, "size-allocate",
469 G_CALLBACK(reposition_panel), panel);
471 number_of_windows++;
472 gdk_window_lower(panel->window->window);
473 gtk_widget_show(panel->window);
474 /* This has no effect until after window is showing; GTK+ bug? */
475 keep_below(panel->window->window, TRUE);
478 GdkWindow *pinboard;
480 pinboard = pinboard_get_window();
481 /* (if pinboard is NULL, will go right to the back) */
482 window_put_just_above(panel->window->window, pinboard);
485 return panel;
488 /* Externally visible function to add an item to a panel */
489 gboolean panel_add(PanelSide side,
490 const gchar *path, const gchar *label, gboolean after, const gchar *shortcut, const gchar *args,
491 gboolean locked)
493 g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, FALSE);
495 g_return_val_if_fail(current_panel[side] != NULL, FALSE);
497 panel_add_item(current_panel[side], path, label, after, shortcut, args, locked);
499 return TRUE;
502 /* Add the area covered by the panels to the region */
503 void panel_mark_used(GdkRegion *used)
505 int i;
507 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
509 Panel *panel = current_panel[i];
510 GdkRectangle rect;
512 if (!panel)
513 continue;
515 gdk_window_get_root_origin(panel->window->window,
516 &rect.x, &rect.y);
517 rect.width = panel->window->allocation.width;
518 rect.height = panel->window->allocation.height;
520 gdk_region_union_with_rect(used, &rect);
524 /* On xrandr screen size changes, update all panels */
525 void panel_update_size(void)
527 int i;
529 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
531 if (current_panel[i])
533 reposition_panel(current_panel[i]->window,
534 &current_panel[i]->window->allocation,
535 current_panel[i]);
536 gtk_widget_queue_resize(current_panel[i]->window);
541 /****************************************************************
542 * INTERNAL FUNCTIONS *
543 ****************************************************************/
545 /* User has tried to close the panel via the window manager - confirm */
546 static int panel_delete(GtkWidget *widget, GdkEvent *event, Panel *panel)
548 return !confirm(_("You have tried to close a panel via the window "
549 "manager - I usually find that this is accidental... "
550 "really close?"),
551 GTK_STOCK_CLOSE, NULL);
554 static void panel_destroyed(GtkWidget *widget, Panel *panel)
556 if (panel_options_dialog)
558 Panel *dlg_panel = g_object_get_data(G_OBJECT(panel_options_dialog),
559 "rox-panel");
561 if (dlg_panel == panel)
562 gtk_widget_destroy(panel_options_dialog);
565 if (current_panel[panel->side] == panel)
566 current_panel[panel->side] = NULL;
568 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
570 if (current_panel[PANEL_RIGHT])
571 gtk_widget_queue_resize(
572 current_panel[PANEL_RIGHT]->window);
573 if (current_panel[PANEL_LEFT])
574 gtk_widget_queue_resize(
575 current_panel[PANEL_LEFT]->window);
578 if (panel->autoscroll_speed)
579 g_source_remove(panel->autoscroll_to);
581 g_free(panel->name);
582 g_free(panel);
584 one_less_window();
587 static void panel_load_side(Panel *panel, xmlNodePtr side, gboolean after)
589 xmlNodePtr node;
590 char *label, *path, *shortcut, *args, *tmp;
591 gboolean locked;
593 for (node = side->xmlChildrenNode; node; node = node->next)
595 if (node->type != XML_ELEMENT_NODE)
596 continue;
597 if (strcmp(node->name, "icon") != 0)
598 continue;
600 label = xmlGetProp(node, "label");
601 if (!label)
602 label = g_strdup("<missing label>");
603 path = xmlNodeGetContent(node);
604 if (!path)
605 path = g_strdup("<missing path>");
606 shortcut = xmlGetProp(node, "shortcut");
607 args = xmlGetProp(node, "args");
608 tmp = xmlGetProp(node, "locked");
609 if (tmp)
611 locked = text_to_boolean(tmp, FALSE);
612 g_free(tmp);
614 else
615 locked = FALSE;
617 panel_add_item(panel, path, label, after, shortcut, args, locked);
619 g_free(path);
620 g_free(label);
621 g_free(shortcut);
622 g_free(args);
626 /* Create one panel icon for each icon in the doc */
627 static void panel_load_from_xml(Panel *panel, xmlDocPtr doc)
629 xmlNodePtr root;
631 root = xmlDocGetRootElement(doc);
632 panel_load_side(panel, get_subnode(root, NULL, "start"), FALSE);
633 panel_load_side(panel, get_subnode(root, NULL, "end"), TRUE);
636 /* Called for each line in the config file while loading a new panel */
637 static const char *pan_from_file(gchar *line)
639 gchar *sep, *leaf;
641 g_return_val_if_fail(line != NULL, NULL);
642 g_return_val_if_fail(loading_panel != NULL, NULL);
644 if (*line == '\0')
645 return NULL;
647 sep = strpbrk(line, "<>");
648 if (!sep)
649 return _("Missing < or > in panel config file");
651 if (sep != line)
652 leaf = g_strndup(line, sep - line);
653 else
654 leaf = NULL;
656 panel_add_item(loading_panel, sep + 1, leaf, sep[0] == '>',
657 NULL, NULL, FALSE);
659 g_free(leaf);
661 return NULL;
664 static gboolean icon_pointer_in(GtkWidget *widget,
665 GdkEventCrossing *event,
666 Icon *icon)
668 gtk_widget_set_state(widget,
669 icon->selected ? GTK_STATE_SELECTED : GTK_STATE_PRELIGHT);
671 return 0;
674 static gboolean icon_pointer_out(GtkWidget *widget,
675 GdkEventCrossing *event,
676 Icon *icon)
678 gtk_widget_set_state(widget,
679 icon->selected ? GTK_STATE_SELECTED : GTK_STATE_NORMAL);
681 return 0;
684 static void panel_icon_destroyed(PanelIcon *pi)
686 g_return_if_fail(pi->widget != NULL);
688 pi->widget = NULL;
690 g_object_unref(pi);
693 /* Set the tooltip AND hide/show the label */
694 static void panel_icon_set_tip(PanelIcon *pi)
696 XMLwrapper *ai;
697 xmlNode *node;
698 Icon *icon = (Icon *) pi;
700 g_return_if_fail(pi != NULL);
702 if (pi->label)
704 if (panel_want_show_text(pi))
705 gtk_widget_show(pi->label);
706 else
707 gtk_widget_hide(pi->label);
710 if (pi->socket)
711 ai = NULL;
712 else
713 ai = appinfo_get(icon->path, icon->item);
715 if (ai && ((node = xml_get_section(ai, NULL, "Summary"))))
717 guchar *str;
718 str = xmlNodeListGetString(node->doc,
719 node->xmlChildrenNode, 1);
720 if (str)
722 gtk_tooltips_set_tip(tooltips, pi->widget, str, NULL);
723 g_free(str);
726 else if ((!panel_want_show_text(pi)) && !pi->socket)
728 if (icon->item->leafname && icon->item->leafname[0])
729 gtk_tooltips_set_tip(tooltips, pi->widget,
730 icon->item->leafname, NULL);
732 else
733 gtk_tooltips_set_tip(tooltips, pi->widget, NULL, NULL);
735 if (ai)
736 g_object_unref(ai);
739 /* Add an icon with this path to the panel. If after is TRUE then the
740 * icon is added to the right/bottom end of the panel.
742 * If name is NULL a suitable name is taken from path.
744 static void panel_add_item(Panel *panel,
745 const gchar *path,
746 const gchar *name,
747 gboolean after,
748 const gchar *shortcut,
749 const gchar *args,
750 gboolean locked)
752 GtkWidget *widget;
753 PanelIcon *pi;
754 Icon *icon;
756 g_return_if_fail(panel != NULL);
757 g_return_if_fail(path != NULL);
759 widget = gtk_event_box_new();
760 gtk_widget_set_events(widget,
761 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
762 GDK_BUTTON3_MOTION_MASK |
763 GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
764 GDK_BUTTON_RELEASE_MASK);
766 gtk_box_pack_start(GTK_BOX(after ? panel->after : panel->before),
767 widget, FALSE, TRUE, 0);
768 if (after)
769 gtk_box_reorder_child(GTK_BOX(panel->after), widget, 0);
771 gtk_widget_realize(widget);
773 pi = panel_icon_new(panel, path, name);
774 icon = (Icon *) pi;
776 /* Widget takes the initial ref of Icon */
777 g_object_set_data(G_OBJECT(widget), "icon", pi);
779 pi->widget = widget;
780 g_object_ref(widget);
782 gtk_widget_set_name(pi->widget, "panel-icon");
784 g_signal_connect_swapped(widget, "destroy",
785 G_CALLBACK(panel_icon_destroyed), pi);
787 if (icon->item->base_type == TYPE_DIRECTORY)
788 run_applet(pi);
790 g_signal_connect(widget, "button_release_event",
791 G_CALLBACK(icon_button_release), pi);
792 g_signal_connect(widget, "button_press_event",
793 G_CALLBACK(icon_button_press), pi);
794 g_signal_connect(widget, "motion-notify-event",
795 G_CALLBACK(icon_motion_event), pi);
796 g_signal_connect(widget, "enter-notify-event",
797 G_CALLBACK(icon_pointer_in), pi);
798 g_signal_connect(widget, "leave-notify-event",
799 G_CALLBACK(icon_pointer_out), pi);
801 if (!pi->socket)
803 g_signal_connect(widget, "enter-notify-event",
804 G_CALLBACK(enter_icon), pi);
805 g_signal_connect_after(widget, "expose_event",
806 G_CALLBACK(expose_icon), pi);
807 g_signal_connect(widget, "drag_data_get",
808 G_CALLBACK(drag_data_get), NULL);
810 g_signal_connect(widget, "size_request",
811 G_CALLBACK(size_request), pi);
813 drag_set_panel_dest(pi);
815 pi->label = gtk_label_new(icon->item->leafname);
816 gtk_container_add(GTK_CONTAINER(pi->widget), pi->label);
817 gtk_misc_set_alignment(GTK_MISC(pi->label), 0.5, 1);
818 gtk_misc_set_padding(GTK_MISC(pi->label), 1, 2);
821 icon_set_shortcut(icon, shortcut);
822 icon_set_arguments(icon, args);
823 icon->locked = locked;
825 if (!loading_panel)
826 panel_save(panel);
828 panel_icon_set_tip(pi);
829 gtk_widget_show(widget);
832 static gboolean remove_item_from_side(GtkWidget *container, const gchar *path,
833 const gchar *label)
835 GList *kids, *next;
836 gboolean found = FALSE;
838 kids = gtk_container_get_children(GTK_CONTAINER(container));
840 for (next = kids; next; next = next->next)
842 Icon *icon;
843 icon = g_object_get_data(G_OBJECT(next->data), "icon");
844 if (!icon)
845 continue;
847 if ((!path || strcmp(path, icon->src_path) == 0) &&
848 (!label || strcmp(label, icon->item->leafname)==0))
850 icon->locked = FALSE;
851 icon_destroy(icon);
852 found = TRUE;
853 break;
857 g_list_free(kids);
859 return found;
862 /* Remove an item with this path. If more than one item matches, only
863 * one is removed. If label is not NULL then it must also match the item.
864 * Returns TRUE if an item was successfully removed.
866 gboolean panel_remove_item(PanelSide side, const gchar *path,
867 const gchar *label)
869 Panel *panel;
871 g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, FALSE);
873 g_return_val_if_fail(path != NULL || label != NULL, FALSE);
875 panel = current_panel[side];
876 if (!panel)
878 g_warning("No panel on this side of the screen!");
879 return FALSE;
882 if (remove_item_from_side(panel->before, path, label) ||
883 remove_item_from_side(panel->after, path, label))
885 panel_save(panel);
886 panel_update(panel);
887 return TRUE;
890 g_warning("Panel item path='%s', label='%s' not found", path, label);
891 return FALSE;
894 /* Called when Gtk+ wants to know how much space an icon needs.
895 * 'req' is already big enough for the label, if shown.
897 static void size_request(GtkWidget *widget, GtkRequisition *req, PanelIcon *pi)
899 Icon *icon = (Icon *) pi;
900 gboolean horz = (pi->panel->side == PANEL_TOP ||
901 pi->panel->side == PANEL_BOTTOM);
902 int max_width = 100;
903 int max_height = 100;
904 int image_width, image_height;
905 Panel *panel = pi->panel;
907 if (horz)
908 max_height = panel->width - req->height;
909 else
910 max_width = MAX(panel->width, req->width);
912 /* TODO: really need to recreate? */
913 if (pi->image)
914 g_object_unref(pi->image);
916 pi->image = scale_pixbuf(di_image(icon->item)->src_pixbuf,
917 MAX(20, max_width), MAX(20, max_height));
919 image_width = gdk_pixbuf_get_width(pi->image);
920 image_height = gdk_pixbuf_get_height(pi->image);
922 if (req->height > 0 && max_height < req->height)
924 pi->style = TEXT_BESIDE_ICON;
925 req->width += image_width;
926 req->height = MAX(req->height, image_height);
927 gtk_misc_set_alignment(GTK_MISC(pi->label), 1, 0.5);
929 else
931 pi->style = TEXT_UNDER_ICON;
932 req->width = MAX(req->width, image_width);
933 req->height += image_height;
934 gtk_misc_set_alignment(GTK_MISC(pi->label), 0.5, 1);
937 if (horz)
938 req->width += PANEL_ICON_SPACING;
939 else
940 req->height += PANEL_ICON_SPACING;
943 static gint expose_icon(GtkWidget *widget,
944 GdkEventExpose *event,
945 PanelIcon *pi)
947 return draw_icon(widget, &event->area, pi);
950 static gint draw_icon(GtkWidget *widget, GdkRectangle *badarea, PanelIcon *pi)
952 GdkRectangle area;
953 int width, height;
954 Icon *icon = (Icon *) pi;
955 int image_x;
956 int image_y;
957 GdkPixbuf *image;
958 int text_height = 0;
960 gdk_drawable_get_size(widget->window, &area.width, &area.height);
962 if (panel_want_show_text(pi))
963 text_height = pi->label->requisition.height;
965 g_return_val_if_fail(pi->image != NULL, FALSE);
967 image = pi->image;
969 width = gdk_pixbuf_get_width(image);
970 height = gdk_pixbuf_get_height(image);
972 if (pi->style == TEXT_UNDER_ICON)
974 image_x = (area.width - width) >> 1;
975 image_y = (area.height - height - text_height) >> 1;
977 else
979 image_x = PANEL_ICON_SPACING - 2;
980 image_y = (area.height - height) >> 1;
983 gdk_pixbuf_render_to_drawable_alpha(
984 image,
985 widget->window,
986 0, 0, /* src */
987 image_x, image_y, /* dest */
988 width, height,
989 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
990 GDK_RGB_DITHER_NORMAL, 0, 0);
992 if (icon->item->flags & ITEM_FLAG_SYMLINK)
994 gdk_pixbuf_render_to_drawable_alpha(im_symlink->pixbuf,
995 widget->window,
996 0, 0, /* src */
997 image_x, image_y + 2, /* dest */
998 -1, -1,
999 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
1000 GDK_RGB_DITHER_NORMAL, 0, 0);
1002 if (icon->item->flags & ITEM_FLAG_MOUNT_POINT)
1004 MaskedPixmap *mp = icon->item->flags & ITEM_FLAG_MOUNTED
1005 ? im_mounted
1006 : im_unmounted;
1008 gdk_pixbuf_render_to_drawable_alpha(mp->pixbuf,
1009 widget->window,
1010 0, 0, /* src */
1011 image_x, image_y + 2, /* dest */
1012 -1, -1,
1013 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
1014 GDK_RGB_DITHER_NORMAL, 0, 0);
1016 return FALSE;
1019 static void panel_icon_wink(Icon *icon)
1021 PanelIcon *pi = (PanelIcon *) icon;
1023 wink_widget(pi->widget);
1026 /* icon may be NULL if the event is on the background */
1027 static void perform_action(Panel *panel, PanelIcon *pi, GdkEventButton *event)
1029 BindAction action;
1030 Icon *icon = (Icon *) pi;
1032 action = bind_lookup_bev(icon ? BIND_PANEL_ICON : BIND_PANEL, event);
1034 if (pi && pi->socket)
1035 if (action != ACT_POPUP_MENU && action != ACT_MOVE_ICON)
1036 return;
1038 switch (action)
1040 case ACT_OPEN_ITEM:
1041 dnd_motion_ungrab();
1042 wink_widget(pi->widget);
1043 icon_run(icon);
1044 break;
1045 case ACT_EDIT_ITEM:
1046 dnd_motion_ungrab();
1047 wink_widget(pi->widget);
1048 run_diritem(icon->path, icon->item, NULL, NULL, TRUE);
1049 break;
1050 case ACT_POPUP_MENU:
1051 dnd_motion_ungrab();
1052 panel_show_menu(event, pi, panel);
1053 break;
1054 case ACT_MOVE_ICON:
1055 dnd_motion_start(MOTION_REPOSITION);
1056 break;
1057 case ACT_PRIME_AND_SELECT:
1058 if (!icon->selected)
1059 icon_select_only(icon);
1060 dnd_motion_start(MOTION_READY_FOR_DND);
1061 break;
1062 case ACT_PRIME_AND_TOGGLE:
1063 icon_set_selected(icon, !icon->selected);
1064 dnd_motion_start(MOTION_READY_FOR_DND);
1065 break;
1066 case ACT_PRIME_FOR_DND:
1067 dnd_motion_start(MOTION_READY_FOR_DND);
1068 break;
1069 case ACT_TOGGLE_SELECTED:
1070 icon_set_selected(icon, !icon->selected);
1071 break;
1072 case ACT_SELECT_EXCL:
1073 icon_set_selected(icon, TRUE);
1074 break;
1075 case ACT_IGNORE:
1076 break;
1077 case ACT_CLEAR_SELECTION:
1078 dnd_motion_ungrab();
1079 icon_select_only(NULL);
1080 break;
1081 default:
1082 g_warning("Unsupported action : %d\n", action);
1083 break;
1087 static gint panel_button_release(GtkWidget *widget,
1088 GdkEventButton *event,
1089 Panel *panel)
1091 if (dnd_motion_release(event))
1092 return TRUE;
1094 perform_action(panel, NULL, event);
1096 return TRUE;
1099 static gint panel_button_press(GtkWidget *widget,
1100 GdkEventButton *event,
1101 Panel *panel)
1103 if (dnd_motion_press(panel->window, event))
1104 perform_action(panel, NULL, event);
1106 return TRUE;
1109 static gint icon_button_release(GtkWidget *widget,
1110 GdkEventButton *event,
1111 PanelIcon *pi)
1113 if (pi->socket && event->button == 1)
1114 return FALSE; /* Restart button */
1116 if (dnd_motion_release(event))
1117 return TRUE;
1119 perform_action(pi->panel, pi, event);
1121 return TRUE;
1124 static gint icon_button_press(GtkWidget *widget,
1125 GdkEventButton *event,
1126 PanelIcon *pi)
1128 if (pi->socket && event->button == 1)
1129 return FALSE; /* Restart button */
1131 if (dnd_motion_press(widget, event))
1132 perform_action(pi->panel, pi, event);
1134 return TRUE;
1137 static void reposition_panel(GtkWidget *window,
1138 GtkAllocation *alloc, Panel *panel)
1140 int x = panel->geometry.x;
1141 int y = panel->geometry.y;
1142 int thickness;
1143 PanelSide side = panel->side;
1145 if (side == PANEL_LEFT || side == PANEL_RIGHT)
1147 if (side == PANEL_RIGHT)
1148 x += panel->geometry.width - alloc->width;
1150 if (current_panel[PANEL_TOP])
1152 GtkWidget *win = current_panel[PANEL_TOP]->window;
1153 y += win->allocation.height;
1157 if (side == PANEL_BOTTOM)
1158 y += panel->geometry.height - alloc->height;
1160 gtk_window_move(GTK_WINDOW(panel->window), x, y);
1161 gdk_window_move(panel->window->window, x, y);
1163 if (side == PANEL_BOTTOM || side == PANEL_TOP)
1165 if (current_panel[PANEL_RIGHT])
1166 gtk_widget_queue_resize(
1167 current_panel[PANEL_RIGHT]->window);
1168 if (current_panel[PANEL_LEFT])
1169 gtk_widget_queue_resize(
1170 current_panel[PANEL_LEFT]->window);
1173 /* Stop windows from maximising over all/part of us */
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));
1294 /* Same as drag_set_dest(), but for panel icons */
1295 static void drag_set_panel_dest(PanelIcon *pi)
1297 GtkWidget *obj = pi->widget;
1299 make_drop_target(pi->widget, 0);
1301 g_signal_connect(obj, "drag_motion", G_CALLBACK(drag_motion), pi);
1302 g_signal_connect(obj, "drag_leave", G_CALLBACK(drag_leave), pi);
1303 g_signal_connect(obj, "drag_end", G_CALLBACK(drag_end), pi);
1306 static gboolean drag_motion(GtkWidget *widget,
1307 GdkDragContext *context,
1308 gint x,
1309 gint y,
1310 guint time,
1311 PanelIcon *pi)
1313 GdkDragAction action = context->suggested_action;
1314 const char *type = NULL;
1315 Icon *icon = (Icon *) pi;
1316 DirItem *item = icon->item;
1317 int panel_x, panel_y;
1319 gdk_window_get_pointer(pi->panel->window->window,
1320 &panel_x, &panel_y, NULL);
1321 motion_may_raise(pi->panel, panel_x, panel_y);
1323 /* Should we scroll the panel when dragging? */
1324 if (motion_state != MOTION_REPOSITION)
1325 if (pi->panel->autoscroll_speed == 0)
1326 may_autoscroll(pi->panel);
1328 if (icon->selected)
1329 goto out; /* Can't drag a selection to itself */
1331 type = dnd_motion_item(context, &item);
1333 if ((context->actions & GDK_ACTION_ASK) && o_dnd_left_menu.int_value
1334 && type != drop_dest_prog)
1336 guint state;
1337 gdk_window_get_pointer(NULL, NULL, NULL, &state);
1338 if (state & GDK_BUTTON1_MASK)
1339 action = GDK_ACTION_ASK;
1342 if (!item)
1343 type = NULL;
1344 out:
1345 /* We actually must pretend to accept the drop, even if the
1346 * directory isn't writeable, so that the spring-opening
1347 * thing works.
1350 /* Don't allow drops to non-writeable directories */
1351 if (o_dnd_spring_open.int_value == FALSE &&
1352 type == drop_dest_dir &&
1353 access(icon->path, W_OK) != 0)
1355 type = NULL;
1358 g_dataset_set_data(context, "drop_dest_type", (gpointer) type);
1359 if (type)
1361 gdk_drag_status(context, action, time);
1362 g_dataset_set_data_full(context, "drop_dest_path",
1363 g_strdup(icon->path), g_free);
1364 if (type == drop_dest_dir)
1365 dnd_spring_load(context, NULL);
1367 if (dnd_highlight && dnd_highlight != pi->widget)
1369 gtk_drag_unhighlight(dnd_highlight);
1370 dnd_highlight = NULL;
1373 if (dnd_highlight == NULL)
1375 gtk_drag_highlight(pi->widget);
1376 dnd_highlight = pi->widget;
1380 return type != NULL;
1384 static void add_uri_list(GtkWidget *widget,
1385 GdkDragContext *context,
1386 gint x,
1387 gint y,
1388 GtkSelectionData *selection_data,
1389 guint info,
1390 guint32 time,
1391 Panel *panel)
1393 gboolean after = FALSE;
1394 GList *uris, *next;
1396 if (!selection_data->data)
1397 return;
1399 g_return_if_fail(selection_data->data[selection_data->length] == '\0');
1401 if (g_object_get_data(G_OBJECT(widget), "after"))
1402 after = TRUE;
1404 uris = uri_list_to_glist(selection_data->data);
1406 for (next = uris; next; next = next->next)
1408 guchar *path;
1410 path = get_local_path((EscapedPath *) next->data);
1412 if (path) {
1413 panel_add_item(panel, path, NULL, after, NULL, NULL, FALSE);
1414 g_free(path);
1418 g_list_free(uris);
1421 static void drag_end(GtkWidget *widget,
1422 GdkDragContext *context,
1423 Icon *icon)
1425 if (tmp_icon_selected)
1427 icon_select_only(NULL);
1428 tmp_icon_selected = FALSE;
1432 static void drag_leave(GtkWidget *widget,
1433 GdkDragContext *context,
1434 guint32 time,
1435 Icon *icon)
1437 panel_drag_leave(widget, context, time, ((PanelIcon *) icon)->panel);
1439 if (dnd_highlight && dnd_highlight == widget)
1441 gtk_drag_unhighlight(dnd_highlight);
1442 dnd_highlight = NULL;
1445 dnd_spring_abort();
1448 /* Create XML icon nodes for these widgets.
1449 * Always frees the widgets list.
1451 static void make_widgets(xmlNodePtr side, GList *widgets)
1453 GList *next;
1455 for (next = widgets; next; next = next->next)
1457 Icon *icon;
1458 xmlNodePtr tree;
1460 icon = g_object_get_data(G_OBJECT(next->data), "icon");
1462 if (!icon)
1464 g_warning("Can't find Icon from widget\n");
1465 continue;
1468 tree = xmlNewTextChild(side, NULL, "icon", icon->src_path);
1470 xmlSetProp(tree, "label", icon->item->leafname);
1471 if (icon->shortcut)
1472 xmlSetProp(tree, "shortcut", icon->shortcut);
1473 if (icon->args)
1474 xmlSetProp(tree, "args", icon->args);
1475 if (icon->locked)
1476 xmlSetProp(tree, "locked", "true");
1479 if (widgets)
1480 g_list_free(widgets);
1483 void panel_save(Panel *panel)
1485 xmlDocPtr doc;
1486 xmlNodePtr root;
1487 guchar *save = NULL;
1488 guchar *save_new = NULL;
1490 g_return_if_fail(panel != NULL);
1492 if (strchr(panel->name, '/'))
1493 save = g_strdup(panel->name);
1494 else
1496 guchar *leaf;
1498 leaf = g_strconcat("pan_", panel->name, NULL);
1499 save = choices_find_xdg_path_save(leaf, PROJECT, SITE, TRUE);
1500 g_free(leaf);
1503 if (!save)
1504 return;
1506 doc = xmlNewDoc("1.0");
1507 xmlDocSetRootElement(doc, xmlNewDocNode(doc, NULL, "panel", NULL));
1509 root = xmlDocGetRootElement(doc);
1511 xmlSetProp(root, "side",
1512 panel->side == PANEL_TOP ? "Top" :
1513 panel->side == PANEL_BOTTOM ? "Bottom" :
1514 panel->side == PANEL_LEFT ? "Left" :
1515 "Right");
1517 make_widgets(xmlNewChild(root, NULL, "start", NULL),
1518 gtk_container_get_children(GTK_CONTAINER(panel->before)));
1520 make_widgets(xmlNewChild(root, NULL, "end", NULL),
1521 g_list_reverse(gtk_container_get_children(
1522 GTK_CONTAINER(panel->after))));
1524 save_new = g_strconcat(save, ".new", NULL);
1525 if (save_xml_file(doc, save_new) || rename(save_new, save))
1526 delayed_error(_("Error saving panel %s: %s"),
1527 save, g_strerror(errno));
1528 g_free(save_new);
1530 g_free(save);
1531 if (doc)
1532 xmlFreeDoc(doc);
1535 /* Create a frame widget which can be used to add icons to the panel */
1536 static GtkWidget *make_insert_frame(Panel *panel)
1538 GtkWidget *frame;
1539 GtkTargetEntry target_table[] = {
1540 {"text/uri-list", 0, TARGET_URI_LIST},
1543 frame = gtk_frame_new(NULL);
1544 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
1545 gtk_widget_set_size_request(frame, 16, 16);
1547 g_signal_connect(frame, "drag-motion",
1548 G_CALLBACK(insert_drag_motion), panel);
1549 g_signal_connect(frame, "drag-leave",
1550 G_CALLBACK(panel_drag_leave), panel);
1552 g_signal_connect(frame, "drag-data-received",
1553 G_CALLBACK(add_uri_list), panel);
1554 gtk_drag_dest_set(frame,
1555 GTK_DEST_DEFAULT_ALL,
1556 target_table,
1557 sizeof(target_table) / sizeof(*target_table),
1558 GDK_ACTION_COPY);
1560 return frame;
1563 static gboolean enter_icon(GtkWidget *widget,
1564 GdkEventCrossing *event,
1565 Icon *icon)
1567 icon_may_update(icon);
1568 panel_icon_set_tip((PanelIcon *) icon);
1570 return FALSE;
1573 static gint panel_leave_event(GtkWidget *widget,
1574 GdkEventCrossing *event,
1575 Panel *panel)
1577 GdkWindow *pinboard;
1579 if (event->mode != GDK_CROSSING_NORMAL)
1580 return FALSE; /* Grab for menu, DnD, etc */
1582 keep_below(panel->window->window, TRUE);
1584 /* Shouldn't need this as well as keep_below but some WMs don't
1585 * automatically lower as soon as the hint is set */
1586 pinboard = pinboard_get_window();
1587 window_put_just_above(panel->window->window, pinboard);
1589 return FALSE;
1592 /* If (x, y) is at the edge of the panel then raise */
1593 static void motion_may_raise(Panel *panel, int x, int y)
1595 gboolean raise;
1597 if (panel->side == PANEL_TOP)
1598 raise = y == 0;
1599 else if (panel->side == PANEL_BOTTOM)
1600 raise = y == panel->window->allocation.height - 1;
1601 else if (panel->side == PANEL_LEFT)
1602 raise = x == 0;
1603 else
1604 raise = x == panel->window->allocation.width - 1;
1606 if (raise)
1608 keep_below(panel->window->window, FALSE);
1610 /* Shouldn't need this as well as keep_below but some WMs don't
1611 * automatically raise as soon as the hint is set */
1612 gdk_window_raise(panel->window->window);
1616 static gboolean may_autoscroll(Panel *panel)
1618 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1619 gint max, panel_x, panel_y, delta, new;
1621 if (panel->adj->upper <= panel->adj->page_size)
1622 goto stop_scrolling; /* Can see everything already */
1624 gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
1626 if (horz)
1628 delta = panel_x;
1629 max = panel->window->allocation.width;
1630 if (panel_y < 0 || panel_y > panel->window->allocation.height)
1631 goto stop_scrolling; /* Not over the panel */
1633 else
1635 delta = panel_y;
1636 max = panel->window->allocation.height;
1637 if (panel_x < 0 || panel_x > panel->window->allocation.width)
1638 goto stop_scrolling; /* Not over the panel */
1641 if (delta >= 20 && delta <= max - 20)
1642 goto stop_scrolling; /* Not at either end */
1644 panel->autoscroll_speed = MIN(panel->autoscroll_speed + 2, 200);
1646 new = panel->adj->value - ((delta < 20) ? panel->autoscroll_speed
1647 : -panel->autoscroll_speed);
1648 new = CLAMP(new, 0, panel->adj->upper - panel->adj->page_size);
1649 gtk_adjustment_set_value(panel->adj, new);
1651 panel->autoscroll_to = g_timeout_add(40,
1652 (GSourceFunc) may_autoscroll, panel);
1654 return FALSE;
1656 stop_scrolling:
1657 panel->autoscroll_speed = 0;
1658 return FALSE;
1661 static gint panel_motion_event(GtkWidget *widget,
1662 GdkEventMotion *event,
1663 Panel *panel)
1665 motion_may_raise(panel, event->x, event->y);
1667 if (motion_state != MOTION_REPOSITION)
1668 if (panel->autoscroll_speed == 0)
1669 may_autoscroll(panel);
1671 return FALSE;
1674 static gint icon_motion_event(GtkWidget *widget,
1675 GdkEventMotion *event,
1676 PanelIcon *pi)
1678 Panel *panel = pi->panel;
1679 GList *list, *me;
1680 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1681 int val;
1682 int dir = 0;
1684 if (motion_state == MOTION_READY_FOR_DND)
1686 if (dnd_motion_moved(event))
1687 start_drag(pi, event);
1688 return TRUE;
1690 else if (motion_state != MOTION_REPOSITION)
1691 return FALSE;
1693 list = gtk_container_get_children(GTK_CONTAINER(panel->before));
1694 list = g_list_append(list, NULL); /* The gap in the middle */
1695 list = g_list_concat(list,
1696 gtk_container_get_children(GTK_CONTAINER(panel->after)));
1697 me = g_list_find(list, widget);
1699 g_return_val_if_fail(me != NULL, TRUE);
1701 val = horz ? event->x_root : event->y_root;
1703 if (me->prev)
1705 GtkWidget *prev;
1706 int x, y;
1708 if (me->prev->data)
1709 prev = GTK_WIDGET(me->prev->data);
1710 else
1711 prev = panel->gap;
1713 gdk_window_get_origin(prev->window, &x, &y);
1715 if (val <= (horz ? x : y))
1716 dir = -1;
1719 if (dir == 0 && me->next)
1721 GtkWidget *next;
1722 int x, y, w, h;
1724 if (me->next->data)
1725 next = GTK_WIDGET(me->next->data);
1726 else
1727 next = panel->gap;
1729 gdk_window_get_origin(next->window, &x, &y);
1731 gdk_drawable_get_size(next->window, &w, &h);
1733 x += w;
1734 y += h;
1736 if (val >= (horz ? x : y)-1)
1738 if (next == panel->gap)
1739 dir = +2;
1740 else
1741 dir = +1;
1745 if (dir)
1746 reposition_icon(pi, g_list_index(list, widget) + dir);
1748 return TRUE;
1751 static void reposition_icon_on_side(GtkWidget *side, GtkWidget *widget,
1752 int index)
1754 GList *list;
1756 list = gtk_container_get_children(GTK_CONTAINER(side));
1758 /* Want to move icon to the list in the given 'side'. Is it there
1759 * already?
1762 if (!g_list_find(list, widget))
1764 /* No, reparent */
1765 gtk_grab_remove(widget);
1766 gtk_widget_reparent(widget, side);
1767 dnd_motion_grab_pointer();
1768 gtk_grab_add(widget);
1771 gtk_box_reorder_child(GTK_BOX(side), widget, index);
1773 g_list_free(list);
1776 /* Move icon to this index in the complete widget list.
1777 * 0 makes the icon the left-most icon. The gap in the middle has
1778 * an index number, which allows you to specify that the icon should
1779 * go on the left or right side.
1781 static void reposition_icon(PanelIcon *pi, int index)
1783 Panel *panel = pi->panel;
1784 GtkWidget *widget = pi->widget;
1785 GList *list;
1786 int before_len;
1788 list = gtk_container_get_children(GTK_CONTAINER(panel->before));
1789 before_len = g_list_length(list);
1790 g_list_free(list);
1792 if (index <= before_len)
1793 reposition_icon_on_side(panel->before, widget, index);
1794 else
1795 reposition_icon_on_side(panel->after, widget,
1796 index - (before_len + 1));
1798 panel_save(panel);
1801 static void start_drag(PanelIcon *pi, GdkEventMotion *event)
1803 GtkWidget *widget = pi->widget;
1804 Icon *icon = (Icon *) pi;
1806 if (!icon->selected)
1808 if (event->state & GDK_BUTTON1_MASK)
1810 /* Select just this one */
1811 icon_select_only(icon);
1812 tmp_icon_selected = TRUE;
1814 else
1815 icon_set_selected(icon, TRUE);
1818 g_return_if_fail(icon_selection != NULL);
1820 if (icon_selection->next == NULL)
1821 drag_one_item(widget, event, icon->path, icon->item, NULL);
1822 else
1824 guchar *uri_list;
1826 uri_list = icon_create_uri_list();
1827 drag_selection(widget, event, uri_list);
1828 g_free(uri_list);
1832 static void applet_died(GtkWidget *socket)
1834 gboolean never_plugged;
1836 never_plugged = (!g_object_get_data(G_OBJECT(socket), "lost_plug"))
1837 && !GTK_SOCKET(socket)->plug_window;
1839 if (never_plugged)
1841 report_error(
1842 _("Applet quit without ever creating a widget!"));
1843 gtk_widget_destroy(socket);
1846 gtk_widget_unref(socket);
1849 static void socket_destroyed(GtkWidget *socket, GtkWidget *widget)
1851 g_object_set_data(G_OBJECT(socket), "lost_plug", "yes");
1853 gtk_widget_unref(socket);
1855 gtk_widget_destroy(widget); /* Remove from panel */
1857 if (!closing_panel)
1858 panel_save(g_object_get_data(G_OBJECT(socket), "panel"));
1861 /* Try to run this applet.
1862 * Cases:
1864 * - No executable AppletRun:
1865 * icon->socket == NULL (unchanged) on return.
1867 * Otherwise, create socket (setting icon->socket) and ref it twice.
1869 * - AppletRun quits without connecting a plug:
1870 * On child death lost_plug is unset and socket is empty.
1871 * Unref socket.
1872 * Report error and destroy widget (to 'socket destroyed').
1874 * - AppletRun quits while plug is in socket:
1875 * Unref socket once. Socket will be destroyed later.
1877 * - Socket is destroyed.
1878 * Set lost_plug = "yes" and remove widget from panel.
1879 * Unref socket.
1881 static void run_applet(PanelIcon *pi)
1883 GError *error = NULL;
1884 char *argv[3];
1885 gint pid;
1886 Icon *icon = (Icon *) pi;
1888 argv[0] = (char *) make_path(icon->path, "AppletRun");
1890 if (access(argv[0], X_OK) != 0)
1891 return;
1893 pi->socket = gtk_socket_new();
1895 gtk_container_add(GTK_CONTAINER(pi->widget), pi->socket);
1896 gtk_widget_show_all(pi->socket);
1897 gtk_widget_realize(pi->socket);
1899 /* Always get button-2 events so we can drag */
1900 XGrabButton(gdk_display, Button2, AnyModifier,
1901 GDK_WINDOW_XWINDOW(pi->socket->window),
1902 False,
1903 ButtonPressMask | ButtonReleaseMask | Button2MotionMask,
1904 GrabModeAsync, /* Pointer */
1905 GrabModeAsync, /* Keyboard */
1906 None, None);
1909 gchar *pos;
1910 PanelSide side = pi->panel->side;
1912 /* Set a hint to let applets position their menus correctly */
1913 pos = g_strdup_printf("%s,%d",
1914 side == PANEL_TOP ? "Top" :
1915 side == PANEL_BOTTOM ? "Bottom" :
1916 side == PANEL_LEFT ? "Left" :
1917 "Right", MENU_MARGIN(side));
1918 gdk_property_change(pi->socket->window,
1919 gdk_atom_intern("_ROX_PANEL_MENU_POS", FALSE),
1920 gdk_atom_intern("STRING", FALSE),
1921 8, GDK_PROP_MODE_REPLACE,
1922 pos, strlen(pos));
1923 g_free(pos);
1925 /* Ensure that the properties are set before starting the
1926 * applet.
1928 gdk_flush();
1931 g_object_set_data(G_OBJECT(pi->widget), "icon", pi);
1932 g_object_set_data(G_OBJECT(pi->socket), "panel", pi->panel);
1934 argv[1] = g_strdup_printf("%ld",
1935 GDK_WINDOW_XWINDOW(pi->socket->window));
1936 argv[2] = NULL;
1938 if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
1939 NULL, NULL, &pid, &error))
1941 delayed_error(_("Error running applet:\n%s"), error->message);
1942 g_error_free(error);
1943 gtk_widget_destroy(pi->socket);
1944 pi->socket = NULL;
1946 else
1948 gtk_widget_ref(pi->socket);
1949 on_child_death(pid, (CallbackFn) applet_died, pi->socket);
1951 gtk_widget_ref(pi->socket);
1952 g_signal_connect(pi->socket, "destroy",
1953 G_CALLBACK(socket_destroyed), pi->widget);
1956 g_free(argv[1]);
1959 static void panel_post_resize(GtkWidget *win, GtkRequisition *req, Panel *panel)
1961 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
1963 req->width = panel->geometry.width;
1964 req->height += EDGE_WIDTH;
1966 else
1968 int h = panel->geometry.height;
1970 if (current_panel[PANEL_TOP])
1972 GtkWidget *win = current_panel[PANEL_TOP]->window;
1973 h -= win->allocation.height;
1976 if (current_panel[PANEL_BOTTOM])
1978 GtkWidget *win = current_panel[PANEL_BOTTOM]->window;
1979 h -= win->allocation.height;
1982 req->height = h;
1983 req->width += EDGE_WIDTH;
1987 static void update_side(GtkWidget *side)
1989 GList *kids, *next;
1991 kids = gtk_container_get_children(GTK_CONTAINER(side));
1992 for (next = kids; next; next = next->next)
1994 PanelIcon *pi;
1995 pi = g_object_get_data(next->data, "icon");
1996 panel_icon_set_tip(pi);
1998 g_list_free(kids);
2001 /* Tips or style has changed -- update everything on this panel */
2002 static void panel_set_style(Panel *panel)
2004 update_side(panel->before);
2005 update_side(panel->after);
2006 gtk_widget_queue_resize(panel->window);
2009 static gboolean recreate_panels(char **names)
2011 int i;
2013 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
2015 if (names[i])
2017 panel_new(names[i], i);
2018 g_free(names[i]);
2022 g_free(names);
2024 return FALSE;
2027 static void update_side_size(GtkWidget *side)
2029 GList *kids, *next;
2031 kids = gtk_container_get_children(GTK_CONTAINER(side));
2032 for (next = kids; next; next = next->next)
2034 PanelIcon *pi;
2035 pi = g_object_get_data(next->data, "icon");
2036 gtk_widget_queue_resize(pi->widget);
2038 g_list_free(kids);
2041 /* Update panel size and redraw */
2042 static void panel_update(Panel *panel)
2044 update_side_size(panel->before);
2045 update_side_size(panel->after);
2046 gtk_widget_queue_resize(panel->window);
2047 gtk_widget_queue_draw(panel->window);
2050 static void panel_style_changed(void)
2052 int i;
2054 if (o_override_redirect.has_changed)
2056 gchar **names;
2058 names = g_new(char *, PANEL_NUMBER_OF_SIDES);
2060 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
2062 Panel *panel = current_panel[i];
2063 names[i] = panel ? g_strdup(panel->name) : NULL;
2064 panel_new(NULL, i);
2067 g_idle_add((GtkFunction) recreate_panels, names);
2070 if (o_panel_style.has_changed)
2072 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
2074 if (current_panel[i])
2075 panel_set_style(current_panel[i]);
2078 if (o_panel_width.has_changed)
2080 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
2082 if (current_panel[i])
2083 panel_update(current_panel[i]);
2087 if (o_panel_xinerama.has_changed || o_panel_monitor.has_changed ||
2088 o_panel_avoid.has_changed)
2090 //if (panel_check_xinerama() || o_panel_avoid.has_changed)
2092 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
2094 if (current_panel[i])
2096 reposition_panel(
2097 current_panel[i]->window,
2098 &current_panel[i]->
2099 window->allocation,
2100 current_panel[i]);
2101 gtk_widget_queue_resize(
2102 current_panel[i]->window);
2109 static gboolean draw_panel_edge(GtkWidget *widget, GdkEventExpose *event,
2110 Panel *panel)
2112 int x, y, width, height;
2114 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
2116 width = panel->geometry.width;
2117 height = EDGE_WIDTH;
2119 x = 0;
2120 if (panel->side == PANEL_BOTTOM)
2121 y = 0;
2122 else
2123 y = widget->allocation.height - EDGE_WIDTH;
2125 else
2127 width = EDGE_WIDTH;
2128 height = panel->geometry.height;
2130 y = 0;
2131 if (panel->side == PANEL_RIGHT)
2132 x = 0;
2133 else
2134 x = widget->allocation.width - EDGE_WIDTH;
2137 gdk_draw_rectangle(widget->window,
2138 widget->style->fg_gc[GTK_STATE_NORMAL], TRUE,
2139 x, y, width, height);
2141 return FALSE;
2144 static gpointer parent_class;
2146 static void panel_icon_destroy(Icon *icon)
2148 PanelIcon *pi = (PanelIcon *) icon;
2150 g_return_if_fail(pi != NULL);
2152 if (pi->image)
2153 g_object_unref(pi->image);
2155 g_return_if_fail(pi->widget != NULL);
2157 gtk_widget_destroy(pi->widget);
2160 static void panel_remove_items(void)
2162 Panel *panel;
2164 g_return_if_fail(icon_selection != NULL);
2166 panel = ((PanelIcon *) icon_selection->data)->panel;
2168 while (icon_selection)
2169 icon_destroy((Icon *) icon_selection->data);
2171 panel_save(panel);
2174 /* Icon's size, shape or appearance has changed - update the display */
2175 static void panel_icon_redraw(Icon *icon)
2177 PanelIcon *pi = (PanelIcon *) icon;
2179 gtk_widget_set_state(pi->widget,
2180 icon->selected ? GTK_STATE_SELECTED
2181 : GTK_STATE_NORMAL);
2183 /* Will regenerate the scaled icon from the new image */
2184 gtk_widget_queue_resize(pi->widget);
2186 panel_icon_set_tip((PanelIcon *) icon);
2189 static void panel_icon_update(Icon *icon)
2191 PanelIcon *pi = (PanelIcon *) icon;
2193 gtk_widget_queue_draw(pi->widget);
2194 gtk_label_set_text(GTK_LABEL(pi->label), icon->item->leafname);
2195 panel_save(pi->panel);
2198 /* The point of this is to clear the selection if the existing icons
2199 * aren't from the same panel...
2201 static gboolean panel_icon_same_group(Icon *icon, Icon *other)
2203 if (IS_PANEL_ICON(other))
2205 PanelIcon *a = (PanelIcon *) icon;
2206 PanelIcon *b = (PanelIcon *) other;
2208 return a->panel == b->panel;
2210 else
2211 return FALSE;
2214 static void panel_icon_class_init(gpointer gclass, gpointer data)
2216 IconClass *icon = (IconClass *) gclass;
2218 parent_class = g_type_class_peek_parent(gclass);
2220 icon->destroy = panel_icon_destroy;
2221 icon->redraw = panel_icon_redraw;
2222 icon->update = panel_icon_update;
2223 icon->remove_items = panel_remove_items;
2224 icon->same_group = panel_icon_same_group;
2225 icon->wink = panel_icon_wink;
2228 static void panel_icon_init(GTypeInstance *object, gpointer gclass)
2230 PanelIcon *pi = (PanelIcon *) object;
2232 pi->widget = NULL;
2233 pi->image = NULL;
2234 pi->label = NULL;
2235 pi->socket = NULL;
2236 pi->style = TEXT_UNDER_ICON;
2239 static GType panel_icon_get_type(void)
2241 static GType type = 0;
2243 if (!type)
2245 static const GTypeInfo info =
2247 sizeof (PanelIconClass),
2248 NULL, /* base_init */
2249 NULL, /* base_finalise */
2250 panel_icon_class_init,
2251 NULL, /* class_finalise */
2252 NULL, /* class_data */
2253 sizeof(PanelIcon),
2254 0, /* n_preallocs */
2255 panel_icon_init
2258 type = g_type_register_static(icon_get_type(),
2259 "PanelIcon", &info, 0);
2262 return type;
2265 static PanelIcon *panel_icon_new(Panel *panel,
2266 const char *pathname,
2267 const char *name)
2269 PanelIcon *pi;
2270 Icon *icon;
2272 pi = g_object_new(panel_icon_get_type(), NULL);
2273 icon = (Icon *) pi;
2275 icon_set_path(icon, pathname, name);
2276 pi->panel = panel;
2278 return pi;
2281 static gboolean panel_want_show_text(PanelIcon *pi)
2283 Icon *icon = (Icon *) pi;
2284 Panel *panel = pi->panel;
2286 if (!icon->item->leafname[0])
2287 return FALSE;
2289 if (panel->style == SHOW_BOTH)
2290 return TRUE;
2291 if (panel->style == SHOW_ICON)
2292 return FALSE;
2294 if (icon->item->flags & ITEM_FLAG_APPDIR)
2295 return FALSE;
2297 if (EXECUTABLE_FILE(icon->item))
2298 return FALSE;
2300 return TRUE;
2303 static void panel_show_options(Panel *panel)
2305 GtkWidget *dialog;
2306 gboolean already_showing = FALSE;
2307 GladeXML *glade = NULL;
2309 if (panel_options_dialog)
2311 dialog = panel_options_dialog;
2312 already_showing = TRUE;
2313 glade = glade_get_widget_tree(dialog);
2315 else
2317 glade = get_glade_xml("Panel Options");
2318 dialog = glade_xml_get_widget(glade, "Panel Options");
2319 panel_options_dialog = dialog;
2320 g_signal_connect(dialog, "destroy",
2321 G_CALLBACK(gtk_widget_destroyed),
2322 &panel_options_dialog);
2323 glade_xml_signal_autoconnect(glade);
2324 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CLOSE);
2326 g_object_set_data(G_OBJECT(panel_options_dialog), "rox-panel", panel);
2328 gtk_spin_button_set_adjustment(GTK_SPIN_BUTTON(
2329 glade_xml_get_widget(glade, "panel_xinerama_monitor")),
2330 GTK_ADJUSTMENT(gtk_adjustment_new(MAX(0, panel->monitor),
2331 0, n_monitors - 1, 1, 10, 1)));
2333 if (already_showing)
2335 GtkWindow *win = GTK_WINDOW(dialog);
2337 gtk_widget_hide(dialog);
2338 /* This extra set_position() should ensure it moves to new position
2339 * under pointer */
2340 gtk_window_set_position(win, GTK_WIN_POS_CENTER_ALWAYS);
2341 gtk_window_set_position(win, GTK_WIN_POS_MOUSE);
2342 gtk_window_present(win);
2344 else
2346 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
2347 gtk_widget_show_all(dialog);
2351 static void panel_position_menu(GtkMenu *menu, gint *x, gint *y,
2352 gboolean *push_in, gpointer data)
2354 int *pos = (int *) data;
2355 GtkRequisition requisition;
2356 int margin = pos[2];
2357 int mon = pos[3];
2358 int mon_right = monitor_geom[mon].x +
2359 monitor_geom[mon].width;
2360 int mon_bottom = monitor_geom[mon].y +
2361 monitor_geom[mon].height;
2363 gtk_widget_size_request(GTK_WIDGET(menu), &requisition);
2365 if (pos[0] == -1)
2366 *x = mon_right - margin - requisition.width;
2367 else if (pos[0] == -2)
2368 *x = monitor_geom[mon].x + margin;
2369 else
2370 *x = pos[0] - (requisition.width >> 2);
2372 if (pos[1] == -1)
2373 *y = mon_bottom - margin - requisition.height;
2374 else if (pos[1] == -2)
2375 *y = monitor_geom[mon].y + margin;
2376 else
2377 *y = pos[1] - (requisition.height >> 2);
2379 *x = CLAMP(*x, 0, mon_right - requisition.width);
2380 *y = CLAMP(*y, 0, mon_bottom - requisition.height);
2382 *push_in = FALSE;
2385 static void side_radio_toggled(GtkCheckMenuItem *item, Panel *panel)
2387 PanelSide new_side;
2388 PanelSide old_side;
2389 char *name, *other_side_name;
2391 if (!gtk_check_menu_item_get_active(item))
2392 return;
2393 new_side = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(item),
2394 "rox-panel-side"));
2395 old_side = panel->side;
2396 if (new_side == old_side)
2397 return;
2399 name = g_strdup(panel->name);
2400 other_side_name = current_panel[new_side]
2401 ? g_strdup(current_panel[new_side]->name)
2402 : NULL;
2404 panel_new(name, new_side);
2405 panel_new(other_side_name, old_side);
2407 g_free(name);
2408 g_free(other_side_name);
2411 static void append_pos_to_menu(GtkWidget *menu, const char *label,
2412 PanelSide side, PanelSide current_side, GSList **pgroup, Panel *panel)
2414 GtkWidget *item = gtk_radio_menu_item_new_with_label(*pgroup, label);
2416 *pgroup = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item));
2417 if (side == current_side)
2418 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
2419 g_object_set_data(G_OBJECT(item), "rox-panel-side", GINT_TO_POINTER(side));
2420 g_signal_connect(item, "toggled", G_CALLBACK(side_radio_toggled), panel);
2421 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2422 gtk_widget_show(item);
2425 static void panel_show_menu(GdkEventButton *event, PanelIcon *pi, Panel *panel)
2427 GtkWidget *option_item;
2428 PanelSide side = panel->side;
2429 int pos[4];
2430 GtkWidget *pos_submenu;
2431 GtkWidget *change_side_item;
2432 GSList *pos_radio_group = NULL;
2434 pos[0] = event->x_root;
2435 pos[1] = event->y_root;
2436 pos[2] = MENU_MARGIN(side);
2437 /* FIXME: Should we read screen from event's window rather than
2438 * using default? */
2439 pos[3] = gdk_screen_get_monitor_at_point(
2440 gdk_screen_get_default(),
2441 event->x_root, event->y_root);
2443 option_item = gtk_image_menu_item_new_with_label(_("Panel Options..."));
2444 g_signal_connect_swapped(option_item, "activate",
2445 G_CALLBACK(panel_show_options), panel);
2447 pos_submenu = gtk_menu_new();
2448 append_pos_to_menu(pos_submenu, _("Top Edge"), PANEL_TOP, side,
2449 &pos_radio_group, panel);
2450 append_pos_to_menu(pos_submenu, _("Bottom Edge"), PANEL_BOTTOM, side,
2451 &pos_radio_group, panel);
2452 append_pos_to_menu(pos_submenu, _("Left Edge"), PANEL_LEFT, side,
2453 &pos_radio_group, panel);
2454 append_pos_to_menu(pos_submenu, _("Right Edge"), PANEL_RIGHT, side,
2455 &pos_radio_group, panel);
2456 change_side_item = gtk_menu_item_new_with_label(_("Change panel side"));
2457 gtk_menu_item_set_submenu(GTK_MENU_ITEM(change_side_item), pos_submenu);
2459 icon_prepare_menu((Icon *) pi, option_item, change_side_item, NULL);
2461 if (side == PANEL_LEFT)
2462 pos[0] = -2;
2463 else if (side == PANEL_RIGHT)
2464 pos[0] = -1;
2466 if (side == PANEL_TOP)
2467 pos[1] = -2;
2468 else if (side == PANEL_BOTTOM)
2469 pos[1] = -1;
2471 gtk_menu_popup(GTK_MENU(icon_menu), NULL, NULL,
2472 panel_position_menu,
2473 (gpointer) pos, event->button, event->time);
2476 /* Note: also called from icon handler */
2477 static gboolean panel_drag_motion(GtkWidget *widget,
2478 GdkDragContext *context,
2479 gint x,
2480 gint y,
2481 guint time,
2482 Panel *panel)
2484 int panel_x, panel_y;
2486 gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
2488 motion_may_raise(panel, panel_x, panel_y);
2489 gdk_drag_status(context, 0, time);
2491 return TRUE;
2494 static gboolean insert_drag_motion(GtkWidget *widget,
2495 GdkDragContext *context,
2496 gint x,
2497 gint y,
2498 guint time,
2499 Panel *panel)
2501 int panel_x, panel_y;
2503 gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
2504 motion_may_raise(panel, panel_x, panel_y);
2506 return FALSE;
2509 /* Note: also called from icon handler */
2510 static void panel_drag_leave(GtkWidget *widget,
2511 GdkDragContext *context,
2512 guint32 time,
2513 Panel *panel)
2515 GdkWindow *pinboard, *window;
2516 GtkAllocation *alloc = &panel->window->allocation;
2517 int x, y;
2519 window = panel->window->window;
2520 gdk_window_get_pointer(window, &x, &y, NULL);
2521 if (x < 0 || y < 0 || x > alloc->width || y > alloc->height)
2523 keep_below(panel->window->window, TRUE);
2525 /* Shouldn't need this as well as keep_below but some WMs don't
2526 * automatically lower as soon as the hint is set */
2527 pinboard = pinboard_get_window();
2528 window_put_just_above(panel->window->window, pinboard);
2532 static void panel_update_geometry(Panel *panel)
2534 if (panel->xinerama && panel->monitor >= n_monitors)
2536 g_warning(_("Xinerama monitor %d unavailable"), panel->monitor);
2537 panel->xinerama = FALSE;
2540 if (panel->xinerama)
2542 panel->geometry = monitor_geom[panel->monitor];
2544 else
2546 panel->geometry.x = panel->geometry.y = 0;
2547 panel->geometry.width = screen_width;
2548 panel->geometry.height = screen_height;
2552 static GList *build_monitor_number(Option *option, xmlNode *node, guchar *label)
2554 GtkObject *adj;
2556 adj = gtk_adjustment_new(MAX(0, panel_monitor),
2557 0, n_monitors - 1, 1, 10, 1);
2558 return build_numentry_base(option, node, label, GTK_ADJUSTMENT(adj));
2561 /* Returns PANEL_NUMBER_OF_SIDES if name is invalid */
2562 PanelSide panel_name_to_side(gchar *side)
2564 if (strcmp(side, "Top") == 0)
2565 return PANEL_TOP;
2566 else if (strcmp(side, "Bottom") == 0)
2567 return PANEL_BOTTOM;
2568 else if (strcmp(side, "Left") == 0)
2569 return PANEL_LEFT;
2570 else if (strcmp(side, "Right") == 0)
2571 return PANEL_RIGHT;
2572 else
2573 g_warning("Unknown panel side '%s'", side);
2574 return PANEL_NUMBER_OF_SIDES;
2577 inline static Panel *panel_from_opts_widget(GtkWidget *widget)
2579 return g_object_get_data(G_OBJECT(gtk_widget_get_toplevel(widget)),
2580 "rox-panel");
2583 static void panel_style_radio_toggled(GtkToggleButton *widget, int which)
2585 Panel *panel;
2587 if (!gtk_toggle_button_get_active(widget))
2588 return;
2589 panel = panel_from_opts_widget(GTK_WIDGET(widget));
2592 /* Handlers autoconnected by glade can't be static but aren't called from
2593 * elsewhere so declare prototypes first to prevent warnings */
2594 void panel_style_radio_0_toggled_cb(GtkToggleButton *widget);
2595 void panel_style_radio_1_toggled_cb(GtkToggleButton *widget);
2596 void panel_style_radio_2_toggled_cb(GtkToggleButton *widget);
2597 void panel_width_changed_cb(GtkSpinButton *widget);
2598 void panel_dont_cover_toggled_cb(GtkToggleButton *widget);
2599 void panel_xinerama_confine_toggled_cb(GtkToggleButton *widget);
2600 void panel_xinerama_monitor_changed_cb(GtkSpinButton *widget);
2602 void panel_style_radio_0_toggled_cb(GtkToggleButton *widget)
2604 panel_style_radio_toggled(widget, 0);
2607 void panel_style_radio_1_toggled_cb(GtkToggleButton *widget)
2609 panel_style_radio_toggled(widget, 1);
2612 void panel_style_radio_2_toggled_cb(GtkToggleButton *widget)
2614 panel_style_radio_toggled(widget, 2);
2617 void panel_width_changed_cb(GtkSpinButton *widget)
2619 Panel *panel = panel_from_opts_widget(GTK_WIDGET(widget));
2622 void panel_dont_cover_toggled_cb(GtkToggleButton *widget)
2624 Panel *panel = panel_from_opts_widget(GTK_WIDGET(widget));
2627 void panel_xinerama_confine_toggled_cb(GtkToggleButton *widget)
2629 Panel *panel = panel_from_opts_widget(GTK_WIDGET(widget));
2632 void panel_xinerama_monitor_changed_cb(GtkSpinButton *widget)
2634 Panel *panel = panel_from_opts_widget(GTK_WIDGET(widget));