r4654: Allow pinboard and panel icons to be locked (Dennis Tomas, suggested by
[rox-filer.git] / ROX-Filer / src / panel.c
blobf3a9694b86bd070f61cae24a99df14b4f220a967
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 2
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 gboolean panel_check_xinerama(void);
200 static GList *build_monitor_number(Option *option,
201 xmlNode *node, guchar *label);
202 static gboolean may_autoscroll(Panel *panel);
203 static void panel_show_options(gpointer data);
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;
219 GdkRectangle panel_geometry;
221 static int closing_panel = 0; /* Don't panel_save; destroying! */
223 /****************************************************************
224 * EXTERNAL INTERFACE *
225 ****************************************************************/
227 void panel_init(void)
229 option_add_int(&o_panel_style, "panel_style", SHOW_APPS_SMALL);
230 option_add_int(&o_panel_width, "panel_width", 52);
232 option_add_int(&o_panel_xinerama, "panel_xinerama", 0);
233 option_add_int(&o_panel_monitor, "panel_monitor", 0);
235 option_add_int(&o_panel_avoid, "panel_avoid", TRUE);
236 option_add_int(&o_panel_is_dock, "panel_is_dock", FALSE);
238 option_add_notify(panel_style_changed);
240 option_register_widget("monitor-number", build_monitor_number);
242 panel_check_xinerama();
245 /* Return a free edge for a new panel.
246 * If no edge is free, returns PANEL_BOTTOM.
248 static PanelSide find_free_side()
250 if (!current_panel[PANEL_BOTTOM])
251 return PANEL_BOTTOM;
253 if (!current_panel[PANEL_TOP])
254 return PANEL_TOP;
256 if (!current_panel[PANEL_LEFT])
257 return PANEL_LEFT;
259 if (!current_panel[PANEL_RIGHT])
260 return PANEL_RIGHT;
262 return PANEL_BOTTOM;
265 /* 'name' may be NULL or "" to remove the panel */
266 Panel *panel_new(const gchar *name, PanelSide side)
268 guchar *load_path;
269 Panel *panel;
270 GtkWidget *vp, *box, *frame, *align;
271 xmlDocPtr panel_doc = NULL;
272 gboolean need_resave = FALSE;
274 g_return_val_if_fail(side == PANEL_DEFAULT_SIDE ||
275 (side >= 0 && side < PANEL_NUMBER_OF_SIDES), NULL);
276 g_return_val_if_fail(loading_panel == NULL, NULL);
278 if (name && *name == '\0')
279 name = NULL;
281 if (!name)
282 load_path = NULL;
283 else if (strchr(name, '/'))
284 load_path = g_strdup(name);
285 else
287 guchar *leaf;
289 leaf = g_strconcat("pan_", name, NULL);
290 load_path = choices_find_xdg_path_load(leaf, PROJECT, SITE);
291 g_free(leaf);
294 if (load_path && access(load_path, F_OK) == 0)
296 char *saved_side;
297 xmlNodePtr root;
299 panel_doc = xmlParseFile(load_path);
300 root = xmlDocGetRootElement(panel_doc);
302 saved_side = xmlGetProp(root, "side");
303 if (saved_side)
305 PanelSide old_side;
306 old_side = panel_name_to_side(saved_side);
307 g_free(saved_side);
309 if (side == PANEL_DEFAULT_SIDE)
310 side = old_side;
311 else if (side != old_side)
312 need_resave = TRUE;
314 else
315 need_resave = TRUE;
318 if (side == PANEL_DEFAULT_SIDE)
319 side = find_free_side();
321 if (current_panel[side])
323 if (name)
324 number_of_windows++;
325 closing_panel++;
326 gtk_widget_destroy(current_panel[side]->window);
327 closing_panel--;
328 if (name)
329 number_of_windows--;
332 if (name == NULL || *name == '\0')
333 return NULL;
335 panel = g_new(Panel, 1);
336 panel->name = g_strdup(name);
337 panel->side = side;
338 panel->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
339 panel->autoscroll_speed = 0;
340 gtk_window_set_resizable(GTK_WINDOW(panel->window), FALSE);
341 gtk_window_set_wmclass(GTK_WINDOW(panel->window), "ROX-Panel", PROJECT);
342 gtk_widget_set_name(panel->window, "rox-panel");
343 gtk_widget_set_events(panel->window,
344 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
345 GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
347 /* We make the panel a drop target only so that we can auto-raise! */
348 gtk_drag_dest_set(panel->window, 0, NULL, 0, GDK_ACTION_PRIVATE);
349 g_signal_connect(panel->window, "drag_leave",
350 G_CALLBACK(panel_drag_leave), panel);
351 g_signal_connect(panel->window, "drag_motion",
352 G_CALLBACK(panel_drag_motion), panel);
354 g_signal_connect(panel->window, "delete-event",
355 G_CALLBACK(panel_delete), panel);
356 g_signal_connect(panel->window, "destroy",
357 G_CALLBACK(panel_destroyed), panel);
358 g_signal_connect(panel->window, "button_press_event",
359 G_CALLBACK(panel_button_press), panel);
360 g_signal_connect(panel->window, "button_release_event",
361 G_CALLBACK(panel_button_release), panel);
362 g_signal_connect(panel->window, "motion-notify-event",
363 G_CALLBACK(panel_motion_event), panel);
364 g_signal_connect(panel->window, "leave-notify-event",
365 G_CALLBACK(panel_leave_event), panel);
367 if (panel->side == PANEL_RIGHT)
368 align = gtk_alignment_new(1.0, 0.0, 0.0, 1.0);
369 else if (panel->side == PANEL_BOTTOM)
370 align = gtk_alignment_new(0.0, 1.0, 1.0, 0.0);
371 else if (panel->side == PANEL_TOP)
372 align = gtk_alignment_new(0.0, 0.0, 1.0, 0.0);
373 else
374 align = gtk_alignment_new(0.0, 0.0, 0.0, 1.0);
376 gtk_container_add(GTK_CONTAINER(panel->window), align);
378 vp = gtk_viewport_new(NULL, NULL);
379 gtk_container_set_resize_mode(GTK_CONTAINER(vp), GTK_RESIZE_PARENT);
380 gtk_viewport_set_shadow_type(GTK_VIEWPORT(vp), GTK_SHADOW_NONE);
381 gtk_container_add(GTK_CONTAINER(align), vp);
383 g_signal_connect(align, "expose-event",
384 G_CALLBACK(draw_panel_edge), panel);
386 if (side == PANEL_TOP || side == PANEL_BOTTOM)
388 panel->adj = gtk_viewport_get_hadjustment(GTK_VIEWPORT(vp));
389 box = gtk_hbox_new(FALSE, 0);
390 panel->before = gtk_hbox_new(FALSE, 0);
391 panel->after = gtk_hbox_new(FALSE, 0);
393 else
395 panel->adj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(vp));
396 box = gtk_vbox_new(FALSE, 0);
397 panel->before = gtk_vbox_new(FALSE, 0);
398 panel->after = gtk_vbox_new(FALSE, 0);
401 gtk_container_add(GTK_CONTAINER(vp), box);
402 gtk_box_pack_start(GTK_BOX(box), panel->before, FALSE, TRUE, 0);
403 gtk_box_pack_end(GTK_BOX(box), panel->after, FALSE, TRUE, 0);
405 frame = make_insert_frame(panel);
406 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
408 /* This is used so that we can find the middle easily! */
409 panel->gap = gtk_event_box_new();
410 gtk_box_pack_start(GTK_BOX(box), panel->gap, FALSE, FALSE, 0);
412 frame = make_insert_frame(panel);
413 g_object_set_data(G_OBJECT(frame), "after", "yes");
414 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
416 if (o_panel_is_dock.int_value)
417 gtk_window_set_type_hint(GTK_WINDOW(panel->window),
418 GDK_WINDOW_TYPE_HINT_DOCK);
420 gtk_widget_realize(panel->window);
421 make_panel_window(panel->window);
422 gtk_window_stick(GTK_WINDOW(panel->window));
424 gtk_widget_show_all(align);
426 loading_panel = panel;
427 if (panel_doc)
429 panel_load_from_xml(panel, panel_doc);
430 xmlFreeDoc(panel_doc);
432 if (need_resave)
433 panel_save(panel);
435 else if (load_path)
437 parse_file(load_path, pan_from_file);
438 info_message(_("Your old panel file has been "
439 "converted to the new XML format."));
440 panel_save(panel);
442 else
444 /* Don't scare users with an empty panel... */
445 guchar *apps;
447 panel_add_item(panel, "~", "Home", FALSE, NULL, NULL, FALSE);
449 apps = pathdup(make_path(app_dir, ".."));
450 if (apps)
452 panel_add_item(panel, apps, "Apps", FALSE, NULL, NULL, FALSE);
453 g_free(apps);
456 loading_panel = NULL;
457 g_free(load_path);
459 current_panel[side] = panel;
461 gtk_widget_queue_resize(box);
462 g_signal_connect(panel->window, "size-request",
463 G_CALLBACK(panel_post_resize), panel);
464 g_signal_connect(panel->window, "size-allocate",
465 G_CALLBACK(reposition_panel), panel);
467 number_of_windows++;
468 gdk_window_lower(panel->window->window);
469 gtk_widget_show(panel->window);
470 /* This has no effect until after window is showing; GTK+ bug? */
471 keep_below(panel->window->window, TRUE);
474 GdkWindow *pinboard;
476 pinboard = pinboard_get_window();
477 /* (if pinboard is NULL, will go right to the back) */
478 window_put_just_above(panel->window->window, pinboard);
481 return panel;
484 /* Externally visible function to add an item to a panel */
485 gboolean panel_add(PanelSide side,
486 const gchar *path, const gchar *label, gboolean after, const gchar *shortcut, const gchar *args,
487 gboolean locked)
489 g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, FALSE);
491 g_return_val_if_fail(current_panel[side] != NULL, FALSE);
493 panel_add_item(current_panel[side], path, label, after, shortcut, args, locked);
495 return TRUE;
498 /* Add the area covered by the panels to the region */
499 void panel_mark_used(GdkRegion *used)
501 int i;
503 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
505 Panel *panel = current_panel[i];
506 GdkRectangle rect;
508 if (!panel)
509 continue;
511 gdk_window_get_root_origin(panel->window->window,
512 &rect.x, &rect.y);
513 rect.width = panel->window->allocation.width;
514 rect.height = panel->window->allocation.height;
516 gdk_region_union_with_rect(used, &rect);
520 /* On xrandr screen size changes, update all panels */
521 void panel_update_size(void)
523 int i;
525 panel_check_xinerama();
527 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
529 if (current_panel[i])
531 reposition_panel(current_panel[i]->window,
532 &current_panel[i]->window->allocation,
533 current_panel[i]);
534 gtk_widget_queue_resize(current_panel[i]->window);
539 /****************************************************************
540 * INTERNAL FUNCTIONS *
541 ****************************************************************/
543 /* User has tried to close the panel via the window manager - confirm */
544 static int panel_delete(GtkWidget *widget, GdkEvent *event, Panel *panel)
546 return !confirm(_("You have tried to close a panel via the window "
547 "manager - I usually find that this is accidental... "
548 "really close?"),
549 GTK_STOCK_CLOSE, NULL);
552 static void panel_destroyed(GtkWidget *widget, Panel *panel)
554 if (panel_options_dialog)
556 PanelSide side;
557 side = GPOINTER_TO_INT(g_object_get_data(
558 G_OBJECT(panel_options_dialog),
559 "rox-panel-side"));
560 if (side == panel->side)
561 gtk_widget_destroy(panel_options_dialog);
564 if (current_panel[panel->side] == panel)
565 current_panel[panel->side] = NULL;
567 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
569 if (current_panel[PANEL_RIGHT])
570 gtk_widget_queue_resize(
571 current_panel[PANEL_RIGHT]->window);
572 if (current_panel[PANEL_LEFT])
573 gtk_widget_queue_resize(
574 current_panel[PANEL_LEFT]->window);
577 if (panel->autoscroll_speed)
578 g_source_remove(panel->autoscroll_to);
580 g_free(panel->name);
581 g_free(panel);
583 one_less_window();
586 static void panel_load_side(Panel *panel, xmlNodePtr side, gboolean after)
588 xmlNodePtr node;
589 char *label, *path, *shortcut, *args, *tmp;
590 gboolean locked;
592 for (node = side->xmlChildrenNode; node; node = node->next)
594 if (node->type != XML_ELEMENT_NODE)
595 continue;
596 if (strcmp(node->name, "icon") != 0)
597 continue;
599 label = xmlGetProp(node, "label");
600 if (!label)
601 label = g_strdup("<missing label>");
602 path = xmlNodeGetContent(node);
603 if (!path)
604 path = g_strdup("<missing path>");
605 shortcut = xmlGetProp(node, "shortcut");
606 args = xmlGetProp(node, "args");
607 tmp = xmlGetProp(node, "locked");
608 if (tmp)
610 locked = text_to_boolean(tmp, FALSE);
611 g_free(tmp);
613 else
614 locked = FALSE;
616 panel_add_item(panel, path, label, after, shortcut, args, locked);
618 g_free(path);
619 g_free(label);
620 g_free(shortcut);
621 g_free(args);
625 /* Create one panel icon for each icon in the doc */
626 static void panel_load_from_xml(Panel *panel, xmlDocPtr doc)
628 xmlNodePtr root;
630 root = xmlDocGetRootElement(doc);
631 panel_load_side(panel, get_subnode(root, NULL, "start"), FALSE);
632 panel_load_side(panel, get_subnode(root, NULL, "end"), TRUE);
635 /* Called for each line in the config file while loading a new panel */
636 static const char *pan_from_file(gchar *line)
638 gchar *sep, *leaf;
640 g_return_val_if_fail(line != NULL, NULL);
641 g_return_val_if_fail(loading_panel != NULL, NULL);
643 if (*line == '\0')
644 return NULL;
646 sep = strpbrk(line, "<>");
647 if (!sep)
648 return _("Missing < or > in panel config file");
650 if (sep != line)
651 leaf = g_strndup(line, sep - line);
652 else
653 leaf = NULL;
655 panel_add_item(loading_panel, sep + 1, leaf, sep[0] == '>',
656 NULL, NULL, FALSE);
658 g_free(leaf);
660 return NULL;
663 static gboolean icon_pointer_in(GtkWidget *widget,
664 GdkEventCrossing *event,
665 Icon *icon)
667 gtk_widget_set_state(widget,
668 icon->selected ? GTK_STATE_SELECTED : GTK_STATE_PRELIGHT);
670 return 0;
673 static gboolean icon_pointer_out(GtkWidget *widget,
674 GdkEventCrossing *event,
675 Icon *icon)
677 gtk_widget_set_state(widget,
678 icon->selected ? GTK_STATE_SELECTED : GTK_STATE_NORMAL);
680 return 0;
683 static void panel_icon_destroyed(PanelIcon *pi)
685 g_return_if_fail(pi->widget != NULL);
687 pi->widget = NULL;
689 g_object_unref(pi);
692 /* Set the tooltip AND hide/show the label */
693 static void panel_icon_set_tip(PanelIcon *pi)
695 XMLwrapper *ai;
696 xmlNode *node;
697 Icon *icon = (Icon *) pi;
699 g_return_if_fail(pi != NULL);
701 if (pi->label)
703 if (panel_want_show_text(pi))
704 gtk_widget_show(pi->label);
705 else
706 gtk_widget_hide(pi->label);
709 if (pi->socket)
710 ai = NULL;
711 else
712 ai = appinfo_get(icon->path, icon->item);
714 if (ai && ((node = xml_get_section(ai, NULL, "Summary"))))
716 guchar *str;
717 str = xmlNodeListGetString(node->doc,
718 node->xmlChildrenNode, 1);
719 if (str)
721 gtk_tooltips_set_tip(tooltips, pi->widget, str, NULL);
722 g_free(str);
725 else if ((!panel_want_show_text(pi)) && !pi->socket)
727 if (icon->item->leafname && icon->item->leafname[0])
728 gtk_tooltips_set_tip(tooltips, pi->widget,
729 icon->item->leafname, NULL);
731 else
732 gtk_tooltips_set_tip(tooltips, pi->widget, NULL, NULL);
734 if (ai)
735 g_object_unref(ai);
738 /* Add an icon with this path to the panel. If after is TRUE then the
739 * icon is added to the right/bottom end of the panel.
741 * If name is NULL a suitable name is taken from path.
743 static void panel_add_item(Panel *panel,
744 const gchar *path,
745 const gchar *name,
746 gboolean after,
747 const gchar *shortcut,
748 const gchar *args,
749 gboolean locked)
751 GtkWidget *widget;
752 PanelIcon *pi;
753 Icon *icon;
755 g_return_if_fail(panel != NULL);
756 g_return_if_fail(path != NULL);
758 widget = gtk_event_box_new();
759 gtk_widget_set_events(widget,
760 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
761 GDK_BUTTON3_MOTION_MASK |
762 GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
763 GDK_BUTTON_RELEASE_MASK);
765 gtk_box_pack_start(GTK_BOX(after ? panel->after : panel->before),
766 widget, FALSE, TRUE, 0);
767 if (after)
768 gtk_box_reorder_child(GTK_BOX(panel->after), widget, 0);
770 gtk_widget_realize(widget);
772 pi = panel_icon_new(panel, path, name);
773 icon = (Icon *) pi;
775 /* Widget takes the initial ref of Icon */
776 g_object_set_data(G_OBJECT(widget), "icon", pi);
778 pi->widget = widget;
779 g_object_ref(widget);
781 gtk_widget_set_name(pi->widget, "panel-icon");
783 g_signal_connect_swapped(widget, "destroy",
784 G_CALLBACK(panel_icon_destroyed), pi);
786 if (icon->item->base_type == TYPE_DIRECTORY)
787 run_applet(pi);
789 g_signal_connect(widget, "button_release_event",
790 G_CALLBACK(icon_button_release), pi);
791 g_signal_connect(widget, "button_press_event",
792 G_CALLBACK(icon_button_press), pi);
793 g_signal_connect(widget, "motion-notify-event",
794 G_CALLBACK(icon_motion_event), pi);
795 g_signal_connect(widget, "enter-notify-event",
796 G_CALLBACK(icon_pointer_in), pi);
797 g_signal_connect(widget, "leave-notify-event",
798 G_CALLBACK(icon_pointer_out), pi);
800 if (!pi->socket)
802 g_signal_connect(widget, "enter-notify-event",
803 G_CALLBACK(enter_icon), pi);
804 g_signal_connect_after(widget, "expose_event",
805 G_CALLBACK(expose_icon), pi);
806 g_signal_connect(widget, "drag_data_get",
807 G_CALLBACK(drag_data_get), NULL);
809 g_signal_connect(widget, "size_request",
810 G_CALLBACK(size_request), pi);
812 drag_set_panel_dest(pi);
814 pi->label = gtk_label_new(icon->item->leafname);
815 gtk_container_add(GTK_CONTAINER(pi->widget), pi->label);
816 gtk_misc_set_alignment(GTK_MISC(pi->label), 0.5, 1);
817 gtk_misc_set_padding(GTK_MISC(pi->label), 1, 2);
820 icon_set_shortcut(icon, shortcut);
821 icon_set_arguments(icon, args);
822 icon->locked = locked;
824 if (!loading_panel)
825 panel_save(panel);
827 panel_icon_set_tip(pi);
828 gtk_widget_show(widget);
831 static gboolean remove_item_from_side(GtkWidget *container, const gchar *path,
832 const gchar *label)
834 GList *kids, *next;
835 gboolean found = FALSE;
837 kids = gtk_container_get_children(GTK_CONTAINER(container));
839 for (next = kids; next; next = next->next)
841 Icon *icon;
842 icon = g_object_get_data(G_OBJECT(next->data), "icon");
843 if (!icon)
844 continue;
846 if ((!path || strcmp(path, icon->src_path) == 0) &&
847 (!label || strcmp(label, icon->item->leafname)==0))
849 icon->locked = FALSE;
850 icon_destroy(icon);
851 found = TRUE;
852 break;
856 g_list_free(kids);
858 return found;
861 /* Remove an item with this path. If more than one item matches, only
862 * one is removed. If label is not NULL then it must also match the item.
863 * Returns TRUE if an item was successfully removed.
865 gboolean panel_remove_item(PanelSide side, const gchar *path,
866 const gchar *label)
868 Panel *panel;
870 g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, FALSE);
872 g_return_val_if_fail(path != NULL || label != NULL, FALSE);
874 panel = current_panel[side];
875 if (!panel)
877 g_warning("No panel on this side of the screen!");
878 return FALSE;
881 if (remove_item_from_side(panel->before, path, label) ||
882 remove_item_from_side(panel->after, path, label))
884 panel_save(panel);
885 panel_update(panel);
886 return TRUE;
889 g_warning("Panel item path='%s', label='%s' not found", path, label);
890 return FALSE;
893 /* Called when Gtk+ wants to know how much space an icon needs.
894 * 'req' is already big enough for the label, if shown.
896 static void size_request(GtkWidget *widget, GtkRequisition *req, PanelIcon *pi)
898 Icon *icon = (Icon *) pi;
899 gboolean horz = (pi->panel->side == PANEL_TOP ||
900 pi->panel->side == PANEL_BOTTOM);
901 int max_width = 100;
902 int max_height = 100;
903 int image_width, image_height;
905 if (horz)
906 max_height = o_panel_width.int_value - req->height;
907 else
908 max_width = MAX(o_panel_width.int_value, req->width);
910 /* TODO: really need to recreate? */
911 if (pi->image)
912 g_object_unref(pi->image);
914 pi->image = scale_pixbuf(di_image(icon->item)->src_pixbuf,
915 MAX(20, max_width), MAX(20, max_height));
917 image_width = gdk_pixbuf_get_width(pi->image);
918 image_height = gdk_pixbuf_get_height(pi->image);
920 if (req->height > 0 && max_height < req->height)
922 pi->style = TEXT_BESIDE_ICON;
923 req->width += image_width;
924 req->height = MAX(req->height, image_height);
925 gtk_misc_set_alignment(GTK_MISC(pi->label), 1, 0.5);
927 else
929 pi->style = TEXT_UNDER_ICON;
930 req->width = MAX(req->width, image_width);
931 req->height += image_height;
932 gtk_misc_set_alignment(GTK_MISC(pi->label), 0.5, 1);
935 if (horz)
936 req->width += PANEL_ICON_SPACING;
937 else
938 req->height += PANEL_ICON_SPACING;
941 static gint expose_icon(GtkWidget *widget,
942 GdkEventExpose *event,
943 PanelIcon *pi)
945 return draw_icon(widget, &event->area, pi);
948 static gint draw_icon(GtkWidget *widget, GdkRectangle *badarea, PanelIcon *pi)
950 GdkRectangle area;
951 int width, height;
952 Icon *icon = (Icon *) pi;
953 int image_x;
954 int image_y;
955 GdkPixbuf *image;
956 int text_height = 0;
958 gdk_drawable_get_size(widget->window, &area.width, &area.height);
960 if (panel_want_show_text(pi))
961 text_height = pi->label->requisition.height;
963 g_return_val_if_fail(pi->image != NULL, FALSE);
965 image = pi->image;
967 width = gdk_pixbuf_get_width(image);
968 height = gdk_pixbuf_get_height(image);
970 if (pi->style == TEXT_UNDER_ICON)
972 image_x = (area.width - width) >> 1;
973 image_y = (area.height - height - text_height) >> 1;
975 else
977 image_x = PANEL_ICON_SPACING - 2;
978 image_y = (area.height - height) >> 1;
981 gdk_pixbuf_render_to_drawable_alpha(
982 image,
983 widget->window,
984 0, 0, /* src */
985 image_x, image_y, /* dest */
986 width, height,
987 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
988 GDK_RGB_DITHER_NORMAL, 0, 0);
990 if (icon->item->flags & ITEM_FLAG_SYMLINK)
992 gdk_pixbuf_render_to_drawable_alpha(im_symlink->pixbuf,
993 widget->window,
994 0, 0, /* src */
995 image_x, image_y + 2, /* dest */
996 -1, -1,
997 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
998 GDK_RGB_DITHER_NORMAL, 0, 0);
1000 if (icon->item->flags & ITEM_FLAG_MOUNT_POINT)
1002 MaskedPixmap *mp = icon->item->flags & ITEM_FLAG_MOUNTED
1003 ? im_mounted
1004 : im_unmounted;
1006 gdk_pixbuf_render_to_drawable_alpha(mp->pixbuf,
1007 widget->window,
1008 0, 0, /* src */
1009 image_x, image_y + 2, /* dest */
1010 -1, -1,
1011 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
1012 GDK_RGB_DITHER_NORMAL, 0, 0);
1014 return FALSE;
1017 static void panel_icon_wink(Icon *icon)
1019 PanelIcon *pi = (PanelIcon *) icon;
1021 wink_widget(pi->widget);
1024 /* icon may be NULL if the event is on the background */
1025 static void perform_action(Panel *panel, PanelIcon *pi, GdkEventButton *event)
1027 BindAction action;
1028 Icon *icon = (Icon *) pi;
1030 action = bind_lookup_bev(icon ? BIND_PANEL_ICON : BIND_PANEL, event);
1032 if (pi && pi->socket)
1033 if (action != ACT_POPUP_MENU && action != ACT_MOVE_ICON)
1034 return;
1036 switch (action)
1038 case ACT_OPEN_ITEM:
1039 dnd_motion_ungrab();
1040 wink_widget(pi->widget);
1041 icon_run(icon);
1042 break;
1043 case ACT_EDIT_ITEM:
1044 dnd_motion_ungrab();
1045 wink_widget(pi->widget);
1046 run_diritem(icon->path, icon->item, NULL, NULL, TRUE);
1047 break;
1048 case ACT_POPUP_MENU:
1049 dnd_motion_ungrab();
1050 panel_show_menu(event, pi, panel);
1051 break;
1052 case ACT_MOVE_ICON:
1053 dnd_motion_start(MOTION_REPOSITION);
1054 break;
1055 case ACT_PRIME_AND_SELECT:
1056 if (!icon->selected)
1057 icon_select_only(icon);
1058 dnd_motion_start(MOTION_READY_FOR_DND);
1059 break;
1060 case ACT_PRIME_AND_TOGGLE:
1061 icon_set_selected(icon, !icon->selected);
1062 dnd_motion_start(MOTION_READY_FOR_DND);
1063 break;
1064 case ACT_PRIME_FOR_DND:
1065 dnd_motion_start(MOTION_READY_FOR_DND);
1066 break;
1067 case ACT_TOGGLE_SELECTED:
1068 icon_set_selected(icon, !icon->selected);
1069 break;
1070 case ACT_SELECT_EXCL:
1071 icon_set_selected(icon, TRUE);
1072 break;
1073 case ACT_IGNORE:
1074 break;
1075 case ACT_CLEAR_SELECTION:
1076 dnd_motion_ungrab();
1077 icon_select_only(NULL);
1078 break;
1079 default:
1080 g_warning("Unsupported action : %d\n", action);
1081 break;
1085 static gint panel_button_release(GtkWidget *widget,
1086 GdkEventButton *event,
1087 Panel *panel)
1089 if (dnd_motion_release(event))
1090 return TRUE;
1092 perform_action(panel, NULL, event);
1094 return TRUE;
1097 static gint panel_button_press(GtkWidget *widget,
1098 GdkEventButton *event,
1099 Panel *panel)
1101 if (dnd_motion_press(panel->window, event))
1102 perform_action(panel, NULL, event);
1104 return TRUE;
1107 static gint icon_button_release(GtkWidget *widget,
1108 GdkEventButton *event,
1109 PanelIcon *pi)
1111 if (pi->socket && event->button == 1)
1112 return FALSE; /* Restart button */
1114 if (dnd_motion_release(event))
1115 return TRUE;
1117 perform_action(pi->panel, pi, event);
1119 return TRUE;
1122 static gint icon_button_press(GtkWidget *widget,
1123 GdkEventButton *event,
1124 PanelIcon *pi)
1126 if (pi->socket && event->button == 1)
1127 return FALSE; /* Restart button */
1129 if (dnd_motion_press(widget, event))
1130 perform_action(pi->panel, pi, event);
1132 return TRUE;
1135 static void reposition_panel(GtkWidget *window,
1136 GtkAllocation *alloc, Panel *panel)
1138 int x = panel_geometry.x;
1139 int y = panel_geometry.y;
1140 int thickness;
1141 PanelSide side = panel->side;
1143 if (side == PANEL_LEFT || side == PANEL_RIGHT)
1145 if (side == PANEL_RIGHT)
1146 x += panel_geometry.width - alloc->width;
1148 if (current_panel[PANEL_TOP])
1150 GtkWidget *win = current_panel[PANEL_TOP]->window;
1151 y += win->allocation.height;
1155 if (side == PANEL_BOTTOM)
1156 y += panel_geometry.height - alloc->height;
1158 gtk_window_move(GTK_WINDOW(panel->window), x, y);
1159 gdk_window_move(panel->window->window, x, y);
1161 if (side == PANEL_BOTTOM || side == PANEL_TOP)
1163 if (current_panel[PANEL_RIGHT])
1164 gtk_widget_queue_resize(
1165 current_panel[PANEL_RIGHT]->window);
1166 if (current_panel[PANEL_LEFT])
1167 gtk_widget_queue_resize(
1168 current_panel[PANEL_LEFT]->window);
1171 /* Stop windows from maximising over all/part of us */
1173 struct {
1174 gulong left, right, top, bottom;
1175 gulong left_start_y, left_end_y;
1176 gulong right_start_y, right_end_y;
1177 gulong top_start_x, top_end_x;
1178 gulong bottom_start_x, bottom_end_x;
1179 } strut = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1181 if (o_panel_avoid.int_value == FALSE)
1182 thickness = 2;
1183 else if (panel->side == PANEL_TOP ||
1184 panel->side == PANEL_BOTTOM)
1185 thickness = alloc->height;
1186 else
1187 thickness = alloc->width;
1189 switch (panel->side)
1191 case PANEL_LEFT:
1192 if (!o_panel_xinerama.int_value ||
1193 !monitor_adjacent
1194 [o_panel_monitor.int_value].left)
1196 strut.left = panel_geometry.x +
1197 thickness;
1198 strut.left_start_y = panel_geometry.y;
1199 strut.left_end_y = panel_geometry.y +
1200 panel_geometry.height - 1;
1202 /* else there is (part of) a monitor
1203 * to the left */
1204 else
1206 thickness = 0;
1208 break;
1209 case PANEL_RIGHT:
1210 if (!o_panel_xinerama.int_value ||
1211 !monitor_adjacent
1212 [o_panel_monitor.int_value].right)
1214 /* RHS of monitor might not abut edge
1215 * of total virtual screen */
1216 strut.right = screen_width -
1217 panel_geometry.x -
1218 panel_geometry.width +
1219 thickness;
1220 strut.right_start_y = panel_geometry.y;
1221 strut.right_end_y = panel_geometry.y +
1222 panel_geometry.height - 1;
1224 /* else there is (part of) a monitor
1225 * to the right */
1226 else
1228 thickness = 0;
1230 break;
1231 case PANEL_TOP:
1232 if (!o_panel_xinerama.int_value ||
1233 !monitor_adjacent
1234 [o_panel_monitor.int_value].top)
1236 strut.top = panel_geometry.y +
1237 thickness;
1238 strut.top_start_x = panel_geometry.x;
1239 strut.top_end_x = panel_geometry.x +
1240 panel_geometry.width - 1;
1242 /* else there is (part of) a monitor above */
1243 else
1245 thickness = 0;
1247 break;
1248 default: /* PANEL_BOTTOM */
1249 if (!o_panel_xinerama.int_value ||
1250 !monitor_adjacent
1251 [o_panel_monitor.int_value].bottom)
1253 /* Bottom of monitor might not abut
1254 * edge of total virtual screen */
1255 strut.bottom = screen_height -
1256 panel_geometry.y -
1257 panel_geometry.height +
1258 thickness;
1259 strut.bottom_start_x = panel_geometry.x;
1260 strut.bottom_end_x = panel_geometry.x +
1261 panel_geometry.width - 1;
1263 /* else there is (part of) a monitor below */
1264 else
1266 thickness = 0;
1268 break;
1271 if (thickness)
1273 /* Set full-width strut as well as partial in case
1274 * partial isn't supported by wm */
1275 gdk_property_change(panel->window->window,
1276 gdk_atom_intern("_NET_WM_STRUT",
1277 FALSE),
1278 gdk_atom_intern("CARDINAL", FALSE),
1279 32, GDK_PROP_MODE_REPLACE,
1280 (gchar *) &strut, 4);
1281 gdk_property_change(panel->window->window,
1282 gdk_atom_intern("_NET_WM_STRUT_PARTIAL",
1283 FALSE),
1284 gdk_atom_intern("CARDINAL", FALSE),
1285 32, GDK_PROP_MODE_REPLACE,
1286 (gchar *) &strut, 12);
1288 else
1290 gdk_property_delete(panel->window->window,
1291 gdk_atom_intern("_NET_WM_STRUT_PARTIAL",
1292 FALSE));
1293 gdk_property_delete(panel->window->window,
1294 gdk_atom_intern("_NET_WM_STRUT",
1295 FALSE));
1301 /* Same as drag_set_dest(), but for panel icons */
1302 static void drag_set_panel_dest(PanelIcon *pi)
1304 GtkWidget *obj = pi->widget;
1306 make_drop_target(pi->widget, 0);
1308 g_signal_connect(obj, "drag_motion", G_CALLBACK(drag_motion), pi);
1309 g_signal_connect(obj, "drag_leave", G_CALLBACK(drag_leave), pi);
1310 g_signal_connect(obj, "drag_end", G_CALLBACK(drag_end), pi);
1313 static gboolean drag_motion(GtkWidget *widget,
1314 GdkDragContext *context,
1315 gint x,
1316 gint y,
1317 guint time,
1318 PanelIcon *pi)
1320 GdkDragAction action = context->suggested_action;
1321 const char *type = NULL;
1322 Icon *icon = (Icon *) pi;
1323 DirItem *item = icon->item;
1324 int panel_x, panel_y;
1326 gdk_window_get_pointer(pi->panel->window->window,
1327 &panel_x, &panel_y, NULL);
1328 motion_may_raise(pi->panel, panel_x, panel_y);
1330 /* Should we scroll the panel when dragging? */
1331 if (motion_state != MOTION_REPOSITION)
1332 if (pi->panel->autoscroll_speed == 0)
1333 may_autoscroll(pi->panel);
1335 if (icon->selected)
1336 goto out; /* Can't drag a selection to itself */
1338 type = dnd_motion_item(context, &item);
1340 if ((context->actions & GDK_ACTION_ASK) && o_dnd_left_menu.int_value
1341 && type != drop_dest_prog)
1343 guint state;
1344 gdk_window_get_pointer(NULL, NULL, NULL, &state);
1345 if (state & GDK_BUTTON1_MASK)
1346 action = GDK_ACTION_ASK;
1349 if (!item)
1350 type = NULL;
1351 out:
1352 /* We actually must pretend to accept the drop, even if the
1353 * directory isn't writeable, so that the spring-opening
1354 * thing works.
1357 /* Don't allow drops to non-writeable directories */
1358 if (o_dnd_spring_open.int_value == FALSE &&
1359 type == drop_dest_dir &&
1360 access(icon->path, W_OK) != 0)
1362 type = NULL;
1365 g_dataset_set_data(context, "drop_dest_type", (gpointer) type);
1366 if (type)
1368 gdk_drag_status(context, action, time);
1369 g_dataset_set_data_full(context, "drop_dest_path",
1370 g_strdup(icon->path), g_free);
1371 if (type == drop_dest_dir)
1372 dnd_spring_load(context, NULL);
1374 if (dnd_highlight && dnd_highlight != pi->widget)
1376 gtk_drag_unhighlight(dnd_highlight);
1377 dnd_highlight = NULL;
1380 if (dnd_highlight == NULL)
1382 gtk_drag_highlight(pi->widget);
1383 dnd_highlight = pi->widget;
1387 return type != NULL;
1391 static void add_uri_list(GtkWidget *widget,
1392 GdkDragContext *context,
1393 gint x,
1394 gint y,
1395 GtkSelectionData *selection_data,
1396 guint info,
1397 guint32 time,
1398 Panel *panel)
1400 gboolean after = FALSE;
1401 GList *uris, *next;
1403 if (!selection_data->data)
1404 return;
1406 g_return_if_fail(selection_data->data[selection_data->length] == '\0');
1408 if (g_object_get_data(G_OBJECT(widget), "after"))
1409 after = TRUE;
1411 uris = uri_list_to_glist(selection_data->data);
1413 for (next = uris; next; next = next->next)
1415 guchar *path;
1417 path = get_local_path((EscapedPath *) next->data);
1419 if (path) {
1420 panel_add_item(panel, path, NULL, after, NULL, NULL, FALSE);
1421 g_free(path);
1425 g_list_free(uris);
1428 static void drag_end(GtkWidget *widget,
1429 GdkDragContext *context,
1430 Icon *icon)
1432 if (tmp_icon_selected)
1434 icon_select_only(NULL);
1435 tmp_icon_selected = FALSE;
1439 static void drag_leave(GtkWidget *widget,
1440 GdkDragContext *context,
1441 guint32 time,
1442 Icon *icon)
1444 panel_drag_leave(widget, context, time, ((PanelIcon *) icon)->panel);
1446 if (dnd_highlight && dnd_highlight == widget)
1448 gtk_drag_unhighlight(dnd_highlight);
1449 dnd_highlight = NULL;
1452 dnd_spring_abort();
1455 /* Create XML icon nodes for these widgets.
1456 * Always frees the widgets list.
1458 static void make_widgets(xmlNodePtr side, GList *widgets)
1460 GList *next;
1462 for (next = widgets; next; next = next->next)
1464 Icon *icon;
1465 xmlNodePtr tree;
1467 icon = g_object_get_data(G_OBJECT(next->data), "icon");
1469 if (!icon)
1471 g_warning("Can't find Icon from widget\n");
1472 continue;
1475 tree = xmlNewTextChild(side, NULL, "icon", icon->src_path);
1477 xmlSetProp(tree, "label", icon->item->leafname);
1478 if (icon->shortcut)
1479 xmlSetProp(tree, "shortcut", icon->shortcut);
1480 if (icon->args)
1481 xmlSetProp(tree, "args", icon->args);
1482 if (icon->locked)
1483 xmlSetProp(tree, "locked", "true");
1486 if (widgets)
1487 g_list_free(widgets);
1490 void panel_save(Panel *panel)
1492 xmlDocPtr doc;
1493 xmlNodePtr root;
1494 guchar *save = NULL;
1495 guchar *save_new = NULL;
1497 g_return_if_fail(panel != NULL);
1499 if (strchr(panel->name, '/'))
1500 save = g_strdup(panel->name);
1501 else
1503 guchar *leaf;
1505 leaf = g_strconcat("pan_", panel->name, NULL);
1506 save = choices_find_xdg_path_save(leaf, PROJECT, SITE, TRUE);
1507 g_free(leaf);
1510 if (!save)
1511 return;
1513 doc = xmlNewDoc("1.0");
1514 xmlDocSetRootElement(doc, xmlNewDocNode(doc, NULL, "panel", NULL));
1516 root = xmlDocGetRootElement(doc);
1518 xmlSetProp(root, "side",
1519 panel->side == PANEL_TOP ? "Top" :
1520 panel->side == PANEL_BOTTOM ? "Bottom" :
1521 panel->side == PANEL_LEFT ? "Left" :
1522 "Right");
1524 make_widgets(xmlNewChild(root, NULL, "start", NULL),
1525 gtk_container_get_children(GTK_CONTAINER(panel->before)));
1527 make_widgets(xmlNewChild(root, NULL, "end", NULL),
1528 g_list_reverse(gtk_container_get_children(
1529 GTK_CONTAINER(panel->after))));
1531 save_new = g_strconcat(save, ".new", NULL);
1532 if (save_xml_file(doc, save_new) || rename(save_new, save))
1533 delayed_error(_("Error saving panel %s: %s"),
1534 save, g_strerror(errno));
1535 g_free(save_new);
1537 g_free(save);
1538 if (doc)
1539 xmlFreeDoc(doc);
1542 /* Create a frame widget which can be used to add icons to the panel */
1543 static GtkWidget *make_insert_frame(Panel *panel)
1545 GtkWidget *frame;
1546 GtkTargetEntry target_table[] = {
1547 {"text/uri-list", 0, TARGET_URI_LIST},
1550 frame = gtk_frame_new(NULL);
1551 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
1552 gtk_widget_set_size_request(frame, 16, 16);
1554 g_signal_connect(frame, "drag-motion",
1555 G_CALLBACK(insert_drag_motion), panel);
1556 g_signal_connect(frame, "drag-leave",
1557 G_CALLBACK(panel_drag_leave), panel);
1559 g_signal_connect(frame, "drag-data-received",
1560 G_CALLBACK(add_uri_list), panel);
1561 gtk_drag_dest_set(frame,
1562 GTK_DEST_DEFAULT_ALL,
1563 target_table,
1564 sizeof(target_table) / sizeof(*target_table),
1565 GDK_ACTION_COPY);
1567 return frame;
1570 static gboolean enter_icon(GtkWidget *widget,
1571 GdkEventCrossing *event,
1572 Icon *icon)
1574 icon_may_update(icon);
1575 panel_icon_set_tip((PanelIcon *) icon);
1577 return FALSE;
1580 static gint panel_leave_event(GtkWidget *widget,
1581 GdkEventCrossing *event,
1582 Panel *panel)
1584 GdkWindow *pinboard;
1586 if (event->mode != GDK_CROSSING_NORMAL)
1587 return FALSE; /* Grab for menu, DnD, etc */
1589 keep_below(panel->window->window, TRUE);
1591 /* Shouldn't need this as well as keep_below but some WMs don't
1592 * automatically lower as soon as the hint is set */
1593 pinboard = pinboard_get_window();
1594 window_put_just_above(panel->window->window, pinboard);
1596 return FALSE;
1599 /* If (x, y) is at the edge of the panel then raise */
1600 static void motion_may_raise(Panel *panel, int x, int y)
1602 gboolean raise;
1604 if (panel->side == PANEL_TOP)
1605 raise = y == 0;
1606 else if (panel->side == PANEL_BOTTOM)
1607 raise = y == panel->window->allocation.height - 1;
1608 else if (panel->side == PANEL_LEFT)
1609 raise = x == 0;
1610 else
1611 raise = x == panel->window->allocation.width - 1;
1613 if (raise)
1615 keep_below(panel->window->window, FALSE);
1617 /* Shouldn't need this as well as keep_below but some WMs don't
1618 * automatically raise as soon as the hint is set */
1619 gdk_window_raise(panel->window->window);
1623 static gboolean may_autoscroll(Panel *panel)
1625 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1626 gint max, panel_x, panel_y, delta, new;
1628 if (panel->adj->upper <= panel->adj->page_size)
1629 goto stop_scrolling; /* Can see everything already */
1631 gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
1633 if (horz)
1635 delta = panel_x;
1636 max = panel->window->allocation.width;
1637 if (panel_y < 0 || panel_y > panel->window->allocation.height)
1638 goto stop_scrolling; /* Not over the panel */
1640 else
1642 delta = panel_y;
1643 max = panel->window->allocation.height;
1644 if (panel_x < 0 || panel_x > panel->window->allocation.width)
1645 goto stop_scrolling; /* Not over the panel */
1648 if (delta >= 20 && delta <= max - 20)
1649 goto stop_scrolling; /* Not at either end */
1651 panel->autoscroll_speed = MIN(panel->autoscroll_speed + 2, 200);
1653 new = panel->adj->value - ((delta < 20) ? panel->autoscroll_speed
1654 : -panel->autoscroll_speed);
1655 new = CLAMP(new, 0, panel->adj->upper - panel->adj->page_size);
1656 gtk_adjustment_set_value(panel->adj, new);
1658 panel->autoscroll_to = g_timeout_add(40,
1659 (GSourceFunc) may_autoscroll, panel);
1661 return FALSE;
1663 stop_scrolling:
1664 panel->autoscroll_speed = 0;
1665 return FALSE;
1668 static gint panel_motion_event(GtkWidget *widget,
1669 GdkEventMotion *event,
1670 Panel *panel)
1672 motion_may_raise(panel, event->x, event->y);
1674 if (motion_state != MOTION_REPOSITION)
1675 if (panel->autoscroll_speed == 0)
1676 may_autoscroll(panel);
1678 return FALSE;
1681 static gint icon_motion_event(GtkWidget *widget,
1682 GdkEventMotion *event,
1683 PanelIcon *pi)
1685 Panel *panel = pi->panel;
1686 GList *list, *me;
1687 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1688 int val;
1689 int dir = 0;
1691 if (motion_state == MOTION_READY_FOR_DND)
1693 if (dnd_motion_moved(event))
1694 start_drag(pi, event);
1695 return TRUE;
1697 else if (motion_state != MOTION_REPOSITION)
1698 return FALSE;
1700 list = gtk_container_get_children(GTK_CONTAINER(panel->before));
1701 list = g_list_append(list, NULL); /* The gap in the middle */
1702 list = g_list_concat(list,
1703 gtk_container_get_children(GTK_CONTAINER(panel->after)));
1704 me = g_list_find(list, widget);
1706 g_return_val_if_fail(me != NULL, TRUE);
1708 val = horz ? event->x_root : event->y_root;
1710 if (me->prev)
1712 GtkWidget *prev;
1713 int x, y;
1715 if (me->prev->data)
1716 prev = GTK_WIDGET(me->prev->data);
1717 else
1718 prev = panel->gap;
1720 gdk_window_get_origin(prev->window, &x, &y);
1722 if (val <= (horz ? x : y))
1723 dir = -1;
1726 if (dir == 0 && me->next)
1728 GtkWidget *next;
1729 int x, y, w, h;
1731 if (me->next->data)
1732 next = GTK_WIDGET(me->next->data);
1733 else
1734 next = panel->gap;
1736 gdk_window_get_origin(next->window, &x, &y);
1738 gdk_drawable_get_size(next->window, &w, &h);
1740 x += w;
1741 y += h;
1743 if (val >= (horz ? x : y)-1)
1745 if (next == panel->gap)
1746 dir = +2;
1747 else
1748 dir = +1;
1752 if (dir)
1753 reposition_icon(pi, g_list_index(list, widget) + dir);
1755 return TRUE;
1758 static void reposition_icon_on_side(GtkWidget *side, GtkWidget *widget,
1759 int index)
1761 GList *list;
1763 list = gtk_container_get_children(GTK_CONTAINER(side));
1765 /* Want to move icon to the list in the given 'side'. Is it there
1766 * already?
1769 if (!g_list_find(list, widget))
1771 /* No, reparent */
1772 gtk_grab_remove(widget);
1773 gtk_widget_reparent(widget, side);
1774 dnd_motion_grab_pointer();
1775 gtk_grab_add(widget);
1778 gtk_box_reorder_child(GTK_BOX(side), widget, index);
1780 g_list_free(list);
1783 /* Move icon to this index in the complete widget list.
1784 * 0 makes the icon the left-most icon. The gap in the middle has
1785 * an index number, which allows you to specify that the icon should
1786 * go on the left or right side.
1788 static void reposition_icon(PanelIcon *pi, int index)
1790 Panel *panel = pi->panel;
1791 GtkWidget *widget = pi->widget;
1792 GList *list;
1793 int before_len;
1795 list = gtk_container_get_children(GTK_CONTAINER(panel->before));
1796 before_len = g_list_length(list);
1797 g_list_free(list);
1799 if (index <= before_len)
1800 reposition_icon_on_side(panel->before, widget, index);
1801 else
1802 reposition_icon_on_side(panel->after, widget,
1803 index - (before_len + 1));
1805 panel_save(panel);
1808 static void start_drag(PanelIcon *pi, GdkEventMotion *event)
1810 GtkWidget *widget = pi->widget;
1811 Icon *icon = (Icon *) pi;
1813 if (!icon->selected)
1815 if (event->state & GDK_BUTTON1_MASK)
1817 /* Select just this one */
1818 icon_select_only(icon);
1819 tmp_icon_selected = TRUE;
1821 else
1822 icon_set_selected(icon, TRUE);
1825 g_return_if_fail(icon_selection != NULL);
1827 if (icon_selection->next == NULL)
1828 drag_one_item(widget, event, icon->path, icon->item, NULL);
1829 else
1831 guchar *uri_list;
1833 uri_list = icon_create_uri_list();
1834 drag_selection(widget, event, uri_list);
1835 g_free(uri_list);
1839 static void applet_died(GtkWidget *socket)
1841 gboolean never_plugged;
1843 never_plugged = (!g_object_get_data(G_OBJECT(socket), "lost_plug"))
1844 && !GTK_SOCKET(socket)->plug_window;
1846 if (never_plugged)
1848 report_error(
1849 _("Applet quit without ever creating a widget!"));
1850 gtk_widget_destroy(socket);
1853 gtk_widget_unref(socket);
1856 static void socket_destroyed(GtkWidget *socket, GtkWidget *widget)
1858 g_object_set_data(G_OBJECT(socket), "lost_plug", "yes");
1860 gtk_widget_unref(socket);
1862 gtk_widget_destroy(widget); /* Remove from panel */
1864 if (!closing_panel)
1865 panel_save(g_object_get_data(G_OBJECT(socket), "panel"));
1868 /* Try to run this applet.
1869 * Cases:
1871 * - No executable AppletRun:
1872 * icon->socket == NULL (unchanged) on return.
1874 * Otherwise, create socket (setting icon->socket) and ref it twice.
1876 * - AppletRun quits without connecting a plug:
1877 * On child death lost_plug is unset and socket is empty.
1878 * Unref socket.
1879 * Report error and destroy widget (to 'socket destroyed').
1881 * - AppletRun quits while plug is in socket:
1882 * Unref socket once. Socket will be destroyed later.
1884 * - Socket is destroyed.
1885 * Set lost_plug = "yes" and remove widget from panel.
1886 * Unref socket.
1888 static void run_applet(PanelIcon *pi)
1890 GError *error = NULL;
1891 char *argv[3];
1892 gint pid;
1893 Icon *icon = (Icon *) pi;
1895 argv[0] = (char *) make_path(icon->path, "AppletRun");
1897 if (access(argv[0], X_OK) != 0)
1898 return;
1900 pi->socket = gtk_socket_new();
1902 gtk_container_add(GTK_CONTAINER(pi->widget), pi->socket);
1903 gtk_widget_show_all(pi->socket);
1904 gtk_widget_realize(pi->socket);
1906 /* Always get button-2 events so we can drag */
1907 XGrabButton(gdk_display, Button2, AnyModifier,
1908 GDK_WINDOW_XWINDOW(pi->socket->window),
1909 False,
1910 ButtonPressMask | ButtonReleaseMask | Button2MotionMask,
1911 GrabModeAsync, /* Pointer */
1912 GrabModeAsync, /* Keyboard */
1913 None, None);
1916 gchar *pos;
1917 PanelSide side = pi->panel->side;
1919 /* Set a hint to let applets position their menus correctly */
1920 pos = g_strdup_printf("%s,%d",
1921 side == PANEL_TOP ? "Top" :
1922 side == PANEL_BOTTOM ? "Bottom" :
1923 side == PANEL_LEFT ? "Left" :
1924 "Right", MENU_MARGIN(side));
1925 gdk_property_change(pi->socket->window,
1926 gdk_atom_intern("_ROX_PANEL_MENU_POS", FALSE),
1927 gdk_atom_intern("STRING", FALSE),
1928 8, GDK_PROP_MODE_REPLACE,
1929 pos, strlen(pos));
1930 g_free(pos);
1932 /* Ensure that the properties are set before starting the
1933 * applet.
1935 gdk_flush();
1938 g_object_set_data(G_OBJECT(pi->widget), "icon", pi);
1939 g_object_set_data(G_OBJECT(pi->socket), "panel", pi->panel);
1941 argv[1] = g_strdup_printf("%ld",
1942 GDK_WINDOW_XWINDOW(pi->socket->window));
1943 argv[2] = NULL;
1945 if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
1946 NULL, NULL, &pid, &error))
1948 delayed_error(_("Error running applet:\n%s"), error->message);
1949 g_error_free(error);
1950 gtk_widget_destroy(pi->socket);
1951 pi->socket = NULL;
1953 else
1955 gtk_widget_ref(pi->socket);
1956 on_child_death(pid, (CallbackFn) applet_died, pi->socket);
1958 gtk_widget_ref(pi->socket);
1959 g_signal_connect(pi->socket, "destroy",
1960 G_CALLBACK(socket_destroyed), pi->widget);
1963 g_free(argv[1]);
1966 static void panel_post_resize(GtkWidget *win, GtkRequisition *req, Panel *panel)
1968 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
1970 req->width = panel_geometry.width;
1971 req->height += EDGE_WIDTH;
1973 else
1975 int h = panel_geometry.height;
1977 if (current_panel[PANEL_TOP])
1979 GtkWidget *win = current_panel[PANEL_TOP]->window;
1980 h -= win->allocation.height;
1983 if (current_panel[PANEL_BOTTOM])
1985 GtkWidget *win = current_panel[PANEL_BOTTOM]->window;
1986 h -= win->allocation.height;
1989 req->height = h;
1990 req->width += EDGE_WIDTH;
1994 static void update_side(GtkWidget *side)
1996 GList *kids, *next;
1998 kids = gtk_container_get_children(GTK_CONTAINER(side));
1999 for (next = kids; next; next = next->next)
2001 PanelIcon *pi;
2002 pi = g_object_get_data(next->data, "icon");
2003 panel_icon_set_tip(pi);
2005 g_list_free(kids);
2008 /* Tips or style has changed -- update everything on this panel */
2009 static void panel_set_style(Panel *panel)
2011 update_side(panel->before);
2012 update_side(panel->after);
2013 gtk_widget_queue_resize(panel->window);
2016 static gboolean recreate_panels(char **names)
2018 int i;
2020 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
2022 if (names[i])
2024 panel_new(names[i], i);
2025 g_free(names[i]);
2029 g_free(names);
2031 return FALSE;
2034 static void update_side_size(GtkWidget *side)
2036 GList *kids, *next;
2038 kids = gtk_container_get_children(GTK_CONTAINER(side));
2039 for (next = kids; next; next = next->next)
2041 PanelIcon *pi;
2042 pi = g_object_get_data(next->data, "icon");
2043 gtk_widget_queue_resize(pi->widget);
2045 g_list_free(kids);
2048 /* Update panel size and redraw */
2049 static void panel_update(Panel *panel)
2051 update_side_size(panel->before);
2052 update_side_size(panel->after);
2053 gtk_widget_queue_resize(panel->window);
2054 gtk_widget_queue_draw(panel->window);
2057 static void panel_style_changed(void)
2059 int i;
2061 if (o_override_redirect.has_changed)
2063 gchar **names;
2065 names = g_new(char *, PANEL_NUMBER_OF_SIDES);
2067 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
2069 Panel *panel = current_panel[i];
2070 names[i] = panel ? g_strdup(panel->name) : NULL;
2071 panel_new(NULL, i);
2074 g_idle_add((GtkFunction) recreate_panels, names);
2077 if (o_panel_style.has_changed)
2079 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
2081 if (current_panel[i])
2082 panel_set_style(current_panel[i]);
2085 if (o_panel_width.has_changed)
2087 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
2089 if (current_panel[i])
2090 panel_update(current_panel[i]);
2094 if (o_panel_xinerama.has_changed || o_panel_monitor.has_changed ||
2095 o_panel_avoid.has_changed)
2097 if (panel_check_xinerama() || o_panel_avoid.has_changed)
2099 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
2101 if (current_panel[i])
2103 reposition_panel(
2104 current_panel[i]->window,
2105 &current_panel[i]->
2106 window->allocation,
2107 current_panel[i]);
2108 gtk_widget_queue_resize(
2109 current_panel[i]->window);
2116 static gboolean draw_panel_edge(GtkWidget *widget, GdkEventExpose *event,
2117 Panel *panel)
2119 int x, y, width, height;
2121 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
2123 width = panel_geometry.width;
2124 height = EDGE_WIDTH;
2126 x = 0;
2127 if (panel->side == PANEL_BOTTOM)
2128 y = 0;
2129 else
2130 y = widget->allocation.height - EDGE_WIDTH;
2132 else
2134 width = EDGE_WIDTH;
2135 height = panel_geometry.height;
2137 y = 0;
2138 if (panel->side == PANEL_RIGHT)
2139 x = 0;
2140 else
2141 x = widget->allocation.width - EDGE_WIDTH;
2144 gdk_draw_rectangle(widget->window,
2145 widget->style->fg_gc[GTK_STATE_NORMAL], TRUE,
2146 x, y, width, height);
2148 return FALSE;
2151 static gpointer parent_class;
2153 static void panel_icon_destroy(Icon *icon)
2155 PanelIcon *pi = (PanelIcon *) icon;
2157 g_return_if_fail(pi != NULL);
2159 if (pi->image)
2160 g_object_unref(pi->image);
2162 g_return_if_fail(pi->widget != NULL);
2164 gtk_widget_destroy(pi->widget);
2167 static void panel_remove_items(void)
2169 Panel *panel;
2171 g_return_if_fail(icon_selection != NULL);
2173 panel = ((PanelIcon *) icon_selection->data)->panel;
2175 while (icon_selection)
2176 icon_destroy((Icon *) icon_selection->data);
2178 panel_save(panel);
2181 /* Icon's size, shape or appearance has changed - update the display */
2182 static void panel_icon_redraw(Icon *icon)
2184 PanelIcon *pi = (PanelIcon *) icon;
2186 gtk_widget_set_state(pi->widget,
2187 icon->selected ? GTK_STATE_SELECTED
2188 : GTK_STATE_NORMAL);
2190 /* Will regenerate the scaled icon from the new image */
2191 gtk_widget_queue_resize(pi->widget);
2193 panel_icon_set_tip((PanelIcon *) icon);
2196 static void panel_icon_update(Icon *icon)
2198 PanelIcon *pi = (PanelIcon *) icon;
2200 gtk_widget_queue_draw(pi->widget);
2201 gtk_label_set_text(GTK_LABEL(pi->label), icon->item->leafname);
2202 panel_save(pi->panel);
2205 /* The point of this is to clear the selection if the existing icons
2206 * aren't from the same panel...
2208 static gboolean panel_icon_same_group(Icon *icon, Icon *other)
2210 if (IS_PANEL_ICON(other))
2212 PanelIcon *a = (PanelIcon *) icon;
2213 PanelIcon *b = (PanelIcon *) other;
2215 return a->panel == b->panel;
2217 else
2218 return FALSE;
2221 static void panel_icon_class_init(gpointer gclass, gpointer data)
2223 IconClass *icon = (IconClass *) gclass;
2225 parent_class = g_type_class_peek_parent(gclass);
2227 icon->destroy = panel_icon_destroy;
2228 icon->redraw = panel_icon_redraw;
2229 icon->update = panel_icon_update;
2230 icon->remove_items = panel_remove_items;
2231 icon->same_group = panel_icon_same_group;
2232 icon->wink = panel_icon_wink;
2235 static void panel_icon_init(GTypeInstance *object, gpointer gclass)
2237 PanelIcon *pi = (PanelIcon *) object;
2239 pi->widget = NULL;
2240 pi->image = NULL;
2241 pi->label = NULL;
2242 pi->socket = NULL;
2243 pi->style = TEXT_UNDER_ICON;
2246 static GType panel_icon_get_type(void)
2248 static GType type = 0;
2250 if (!type)
2252 static const GTypeInfo info =
2254 sizeof (PanelIconClass),
2255 NULL, /* base_init */
2256 NULL, /* base_finalise */
2257 panel_icon_class_init,
2258 NULL, /* class_finalise */
2259 NULL, /* class_data */
2260 sizeof(PanelIcon),
2261 0, /* n_preallocs */
2262 panel_icon_init
2265 type = g_type_register_static(icon_get_type(),
2266 "PanelIcon", &info, 0);
2269 return type;
2272 static PanelIcon *panel_icon_new(Panel *panel,
2273 const char *pathname,
2274 const char *name)
2276 PanelIcon *pi;
2277 Icon *icon;
2279 pi = g_object_new(panel_icon_get_type(), NULL);
2280 icon = (Icon *) pi;
2282 icon_set_path(icon, pathname, name);
2283 pi->panel = panel;
2285 return pi;
2288 static gboolean panel_want_show_text(PanelIcon *pi)
2290 Icon *icon = (Icon *) pi;
2292 if (!icon->item->leafname[0])
2293 return FALSE;
2295 if (o_panel_style.int_value == SHOW_BOTH)
2296 return TRUE;
2297 if (o_panel_style.int_value == SHOW_ICON)
2298 return FALSE;
2300 if (icon->item->flags & ITEM_FLAG_APPDIR)
2301 return FALSE;
2303 if (EXECUTABLE_FILE(icon->item))
2304 return FALSE;
2306 return TRUE;
2309 static void panel_position_menu(GtkMenu *menu, gint *x, gint *y,
2310 gboolean *push_in, gpointer data)
2312 int *pos = (int *) data;
2313 GtkRequisition requisition;
2314 int margin = pos[2];
2315 int mon = pos[3];
2316 int mon_right = monitor_geom[mon].x +
2317 monitor_geom[mon].width;
2318 int mon_bottom = monitor_geom[mon].y +
2319 monitor_geom[mon].height;
2321 gtk_widget_size_request(GTK_WIDGET(menu), &requisition);
2323 if (pos[0] == -1)
2324 *x = mon_right - margin - requisition.width;
2325 else if (pos[0] == -2)
2326 *x = monitor_geom[mon].x + margin;
2327 else
2328 *x = pos[0] - (requisition.width >> 2);
2330 if (pos[1] == -1)
2331 *y = mon_bottom - margin - requisition.height;
2332 else if (pos[1] == -2)
2333 *y = monitor_geom[mon].y + margin;
2334 else
2335 *y = pos[1] - (requisition.height >> 2);
2337 *x = CLAMP(*x, 0, mon_right - requisition.width);
2338 *y = CLAMP(*y, 0, mon_bottom - requisition.height);
2340 *push_in = FALSE;
2343 static void panel_show_menu(GdkEventButton *event, PanelIcon *pi, Panel *panel)
2345 GtkWidget *option_item;
2346 PanelSide side = panel->side;
2347 int pos[4];
2349 pos[0] = event->x_root;
2350 pos[1] = event->y_root;
2351 pos[2] = MENU_MARGIN(side);
2352 /* FIXME: Should we read screen from event's window rather than
2353 * using default? */
2354 pos[3] = gdk_screen_get_monitor_at_point(
2355 gdk_screen_get_default(),
2356 event->x_root, event->y_root);
2358 option_item = gtk_image_menu_item_new_with_label(_("Panel Options..."));
2359 g_signal_connect_swapped(option_item, "activate",
2360 G_CALLBACK(panel_show_options),
2361 GINT_TO_POINTER(panel->side));
2363 icon_prepare_menu((Icon *) pi, option_item);
2365 if (side == PANEL_LEFT)
2366 pos[0] = -2;
2367 else if (side == PANEL_RIGHT)
2368 pos[0] = -1;
2370 if (side == PANEL_TOP)
2371 pos[1] = -2;
2372 else if (side == PANEL_BOTTOM)
2373 pos[1] = -1;
2375 gtk_menu_popup(GTK_MENU(icon_menu), NULL, NULL,
2376 panel_position_menu,
2377 (gpointer) pos, event->button, event->time);
2380 /* Note: also called from icon handler */
2381 static gboolean panel_drag_motion(GtkWidget *widget,
2382 GdkDragContext *context,
2383 gint x,
2384 gint y,
2385 guint time,
2386 Panel *panel)
2388 int panel_x, panel_y;
2390 gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
2392 motion_may_raise(panel, panel_x, panel_y);
2393 gdk_drag_status(context, 0, time);
2395 return TRUE;
2398 static gboolean insert_drag_motion(GtkWidget *widget,
2399 GdkDragContext *context,
2400 gint x,
2401 gint y,
2402 guint time,
2403 Panel *panel)
2405 int panel_x, panel_y;
2407 gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
2408 motion_may_raise(panel, panel_x, panel_y);
2410 return FALSE;
2413 /* Note: also called from icon handler */
2414 static void panel_drag_leave(GtkWidget *widget,
2415 GdkDragContext *context,
2416 guint32 time,
2417 Panel *panel)
2419 GdkWindow *pinboard, *window;
2420 GtkAllocation *alloc = &panel->window->allocation;
2421 int x, y;
2423 window = panel->window->window;
2424 gdk_window_get_pointer(window, &x, &y, NULL);
2425 if (x < 0 || y < 0 || x > alloc->width || y > alloc->height)
2427 keep_below(panel->window->window, TRUE);
2429 /* Shouldn't need this as well as keep_below but some WMs don't
2430 * automatically lower as soon as the hint is set */
2431 pinboard = pinboard_get_window();
2432 window_put_just_above(panel->window->window, pinboard);
2436 static gboolean panel_check_xinerama(void)
2438 gint old_monitor = panel_monitor;
2440 panel_monitor = -1;
2442 if (o_panel_xinerama.int_value)
2444 if (o_panel_monitor.int_value < n_monitors)
2446 panel_monitor = o_panel_monitor.int_value;
2448 else
2450 g_warning(_("Xinerama monitor %d unavailable"),
2451 o_panel_monitor.int_value);
2455 if (panel_monitor == -1)
2457 panel_geometry.x = panel_geometry.y = 0;
2458 panel_geometry.width = screen_width;
2459 panel_geometry.height = screen_height;
2461 else
2463 panel_geometry = monitor_geom[panel_monitor];
2466 return old_monitor != panel_monitor;
2469 static GList *build_monitor_number(Option *option, xmlNode *node, guchar *label)
2471 GtkObject *adj;
2473 adj = gtk_adjustment_new(MAX(0, panel_monitor),
2474 0, n_monitors - 1, 1, 10, 1);
2475 return build_numentry_base(option, node, label, GTK_ADJUSTMENT(adj));
2478 static void side_changed(Radios *radios, gpointer data)
2480 Panel *panel;
2481 PanelSide side, new_side;
2482 GObject *dialog = G_OBJECT(data);
2483 char *name, *other_side_name;
2485 side = GPOINTER_TO_INT(g_object_get_data(dialog, "rox-panel-side"));
2486 g_return_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES);
2487 panel = current_panel[side];
2488 g_return_if_fail(panel != NULL);
2490 new_side = radios_get_value(radios);
2492 if (new_side == side)
2493 return;
2495 name = g_strdup(panel->name);
2496 other_side_name = current_panel[new_side]
2497 ? g_strdup(current_panel[new_side]->name)
2498 : NULL;
2500 panel_new(name, new_side);
2501 g_object_set_data(G_OBJECT(dialog), "rox-panel-side",
2502 GINT_TO_POINTER(new_side));
2503 panel_new(other_side_name, side);
2505 g_free(name);
2506 g_free(other_side_name);
2509 static void panel_show_options(gpointer data)
2511 GtkWidget *dialog;
2512 GtkWidget *vbox, *label;
2513 Radios *radios;
2514 Panel *panel;
2515 PanelSide side = GPOINTER_TO_INT(data);
2516 char *tmp;
2518 g_return_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES);
2519 panel = current_panel[side];
2520 g_return_if_fail(panel != NULL);
2522 if (panel_options_dialog)
2523 gtk_widget_destroy(panel_options_dialog);
2525 dialog = gtk_dialog_new_with_buttons(_("Panel Options"), NULL,
2526 GTK_DIALOG_NO_SEPARATOR,
2527 GTK_STOCK_CLOSE, GTK_RESPONSE_OK,
2528 NULL);
2529 panel_options_dialog = dialog;
2531 g_object_set_data(G_OBJECT(dialog), "rox-panel-side",
2532 GINT_TO_POINTER(panel->side));
2534 vbox = gtk_vbox_new(FALSE, 0);
2535 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
2536 vbox, TRUE, TRUE, 0);
2538 gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
2540 tmp = g_strdup_printf(_("Panel: %s"), panel->name);
2541 label = gtk_label_new(tmp);
2542 make_heading(label, PANGO_SCALE_LARGE);
2543 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
2544 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
2545 g_free(tmp);
2547 gtk_box_pack_start(GTK_BOX(vbox),
2548 gtk_label_new(_("Select the panel's position:")),
2549 FALSE, TRUE, 4);
2551 /* Radio buttons to set the side */
2552 radios = radios_new(side_changed, dialog);
2554 radios_add(radios, _("Top-edge panel"),
2555 PANEL_TOP, _("Top edge"));
2556 radios_add(radios, _("Bottom edge panel"),
2557 PANEL_BOTTOM, _("Bottom edge"));
2558 radios_add(radios, _("Left edge panel"),
2559 PANEL_LEFT, _("Left edge"));
2560 radios_add(radios, _("Right-edge panel"),
2561 PANEL_RIGHT, _("Right edge"));
2563 radios_set_value(radios, panel->side);
2565 radios_pack(radios, GTK_BOX(vbox));
2567 g_signal_connect(dialog, "destroy",
2568 G_CALLBACK(gtk_widget_destroyed),
2569 &panel_options_dialog);
2571 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
2573 g_signal_connect(dialog, "response",
2574 G_CALLBACK(gtk_widget_destroy), NULL);
2575 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
2577 gtk_widget_show_all(dialog);
2580 /* Returns PANEL_NUMBER_OF_SIDES if name is invalid */
2581 PanelSide panel_name_to_side(gchar *side)
2583 if (strcmp(side, "Top") == 0)
2584 return PANEL_TOP;
2585 else if (strcmp(side, "Bottom") == 0)
2586 return PANEL_BOTTOM;
2587 else if (strcmp(side, "Left") == 0)
2588 return PANEL_LEFT;
2589 else if (strcmp(side, "Right") == 0)
2590 return PANEL_RIGHT;
2591 else
2592 g_warning("Unknown panel side '%s'", side);
2593 return PANEL_NUMBER_OF_SIDES;