r3598: Bugfix: Panel icons were no longer shown highlighted when selected.
[rox-filer/dt.git] / ROX-Filer / src / panel.c
blob4462d5a659d2b29714fb0b08326f784e10825b6b
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2003, the ROX-Filer team.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* panel.c - code for dealing with panel windows */
24 #include "config.h"
26 #include <stdio.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <ctype.h>
30 #include <unistd.h>
31 #include <libxml/parser.h>
33 #include <gtk/gtk.h>
34 #include <gdk/gdkx.h>
36 #include "global.h"
38 #include "panel.h"
39 #include "options.h"
40 #include "choices.h"
41 #include "main.h"
42 #include "type.h"
43 #include "gui_support.h"
44 #include "diritem.h"
45 #include "pixmaps.h"
46 #include "filer.h"
47 #include "display.h"
48 #include "bind.h"
49 #include "dnd.h"
50 #include "support.h"
51 #include "icon.h"
52 #include "run.h"
53 #include "appinfo.h"
54 #include "pixmaps.h"
55 #include "xml.h"
56 #include "pinboard.h" /* For pinboard_get_window() */
58 /* The width of the separator at the inner edge of the panel */
59 #define EDGE_WIDTH 2
61 /* The gap between panel icons */
62 #define PANEL_ICON_SPACING 8
64 enum {TEXT_BESIDE_ICON, TEXT_UNDER_ICON};
66 static gboolean tmp_icon_selected = FALSE; /* When dragging */
68 typedef struct _PanelIconClass PanelIconClass;
69 typedef struct _PanelIcon PanelIcon;
71 struct _PanelIconClass {
72 IconClass parent;
75 struct _PanelIcon {
76 Icon icon;
77 GdkPixbuf *image;
79 Panel *panel;
80 GtkWidget *widget; /* The drawing area for the icon */
81 GtkWidget *label;
82 GtkWidget *socket; /* For applets */
84 int style;
87 #define PANEL_ICON(obj) GTK_CHECK_CAST((obj), panel_icon_get_type(), PanelIcon)
88 #define IS_PANEL_ICON(obj) \
89 G_TYPE_CHECK_INSTANCE_TYPE((obj), panel_icon_get_type())
91 Panel *current_panel[PANEL_NUMBER_OF_SIDES];
93 /* NULL => Not loading a panel */
94 static Panel *loading_panel = 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_background(GtkWidget *widget,
109 GdkEventExpose *event,
110 PanelIcon *pi);
111 static gint expose_icon(GtkWidget *widget,
112 GdkEventExpose *event,
113 PanelIcon *pi);
114 static gint draw_icon(GtkWidget *widget,
115 GdkRectangle *badarea,
116 PanelIcon *pi);
117 static gint panel_button_release(GtkWidget *widget,
118 GdkEventButton *event,
119 Panel *panel);
120 static gint panel_button_press(GtkWidget *widget,
121 GdkEventButton *event,
122 Panel *panel);
123 static void panel_post_resize(GtkWidget *box,
124 GtkRequisition *req, Panel *panel);
125 static void drag_set_panel_dest(PanelIcon *pi);
126 static void add_uri_list(GtkWidget *widget,
127 GdkDragContext *context,
128 gint x,
129 gint y,
130 GtkSelectionData *selection_data,
131 guint info,
132 guint32 time,
133 Panel *panel);
134 static void panel_add_item(Panel *panel,
135 const gchar *path,
136 const gchar *name,
137 gboolean after,
138 const gchar *shortcut);
139 static gboolean panel_drag_motion(GtkWidget *widget,
140 GdkDragContext *context,
141 gint x,
142 gint y,
143 guint time,
144 Panel *panel);
145 static gboolean insert_drag_motion(GtkWidget *widget,
146 GdkDragContext *context,
147 gint x,
148 gint y,
149 guint time,
150 Panel *panel);
151 static gboolean drag_motion(GtkWidget *widget,
152 GdkDragContext *context,
153 gint x,
154 gint y,
155 guint time,
156 PanelIcon *pi);
157 static void panel_drag_leave(GtkWidget *widget,
158 GdkDragContext *context,
159 guint32 time,
160 Panel *panel);
161 static void drag_leave(GtkWidget *widget,
162 GdkDragContext *context,
163 guint32 time,
164 Icon *icon);
165 static GtkWidget *make_insert_frame(Panel *panel);
166 static gboolean enter_icon(GtkWidget *widget,
167 GdkEventCrossing *event,
168 Icon *icon);
169 static gint icon_motion_event(GtkWidget *widget,
170 GdkEventMotion *event,
171 PanelIcon *pi);
172 static gint panel_leave_event(GtkWidget *widget,
173 GdkEventCrossing *event,
174 Panel *panel);
175 static gint panel_motion_event(GtkWidget *widget,
176 GdkEventMotion *event,
177 Panel *panel);
178 static void reposition_icon(PanelIcon *pi, int index);
179 static void start_drag(PanelIcon *pi, GdkEventMotion *event);
180 static void drag_end(GtkWidget *widget,
181 GdkDragContext *context,
182 Icon *icon);
183 static void perform_action(Panel *panel,
184 PanelIcon *pi,
185 GdkEventButton *event);
186 static void run_applet(PanelIcon *pi);
187 static void size_request(GtkWidget *widget, GtkRequisition *req, PanelIcon *pi);
188 static void panel_load_from_xml(Panel *panel, xmlDocPtr doc);
189 static gboolean draw_panel_edge(GtkWidget *widget, GdkEventExpose *event,
190 Panel *panel);
191 static PanelIcon *panel_icon_new(Panel *panel,
192 const char *pathname,
193 const char *name);
194 static GType panel_icon_get_type(void);
195 static gboolean panel_want_show_text(PanelIcon *pi);
196 static void panel_show_menu(GdkEventButton *event, PanelIcon *pi, Panel *panel);
197 static void panel_style_changed(void);
198 static void motion_may_raise(Panel *panel, int x, int y);
199 static void panel_update(Panel *panel);
200 static gboolean panel_check_xinerama(void);
201 static GList *build_monitor_number(Option *option,
202 xmlNode *node, guchar *label);
203 static gboolean may_autoscroll(Panel *panel);
206 static GtkWidget *dnd_highlight = NULL; /* (stops flickering) */
208 #define SHOW_BOTH 0
209 #define SHOW_APPS_SMALL 1
210 #define SHOW_ICON 2
211 static Option o_panel_style;
212 static Option o_panel_width;
213 static Option o_panel_xinerama;
214 static Option o_panel_monitor;
215 static Option o_panel_avoid;
217 static gint panel_monitor = -1;
218 GdkRectangle panel_geometry;
220 static int closing_panel = 0; /* Don't panel_save; destroying! */
222 /****************************************************************
223 * EXTERNAL INTERFACE *
224 ****************************************************************/
226 void panel_init(void)
228 option_add_int(&o_panel_style, "panel_style", SHOW_APPS_SMALL);
229 option_add_int(&o_panel_width, "panel_width", 52);
231 option_add_int(&o_panel_xinerama, "panel_xinerama", 0);
232 option_add_int(&o_panel_monitor, "panel_monitor", 0);
234 option_add_int(&o_panel_avoid, "panel_avoid", TRUE);
236 option_add_notify(panel_style_changed);
238 option_register_widget("monitor-number", build_monitor_number);
240 panel_check_xinerama();
243 /* 'name' may be NULL or "" to remove the panel */
244 Panel *panel_new(const gchar *name, PanelSide side)
246 guchar *load_path;
247 Panel *panel;
248 GtkWidget *vp, *box, *frame, *align;
250 g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, NULL);
251 g_return_val_if_fail(loading_panel == NULL, NULL);
253 if (name && *name == '\0')
254 name = NULL;
256 if (current_panel[side])
258 if (name)
259 number_of_windows++;
260 closing_panel++;
261 gtk_widget_destroy(current_panel[side]->window);
262 closing_panel--;
263 if (name)
264 number_of_windows--;
267 if (name == NULL || *name == '\0')
268 return NULL;
270 panel = g_new(Panel, 1);
271 panel->name = g_strdup(name);
272 panel->side = side;
273 panel->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
274 panel->autoscroll_speed = 0;
275 gtk_window_set_resizable(GTK_WINDOW(panel->window), FALSE);
276 gtk_window_set_wmclass(GTK_WINDOW(panel->window), "ROX-Panel", PROJECT);
277 gtk_widget_set_name(panel->window, "rox-panel");
278 gtk_widget_set_events(panel->window,
279 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
280 GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
282 /* We make the panel a drop target only so that we can auto-raise! */
283 gtk_drag_dest_set(panel->window, 0, NULL, 0, GDK_ACTION_PRIVATE);
284 g_signal_connect(panel->window, "drag_leave",
285 G_CALLBACK(panel_drag_leave), panel);
286 g_signal_connect(panel->window, "drag_motion",
287 G_CALLBACK(panel_drag_motion), panel);
289 g_signal_connect(panel->window, "delete-event",
290 G_CALLBACK(panel_delete), panel);
291 g_signal_connect(panel->window, "destroy",
292 G_CALLBACK(panel_destroyed), panel);
293 g_signal_connect(panel->window, "button_press_event",
294 G_CALLBACK(panel_button_press), panel);
295 g_signal_connect(panel->window, "button_release_event",
296 G_CALLBACK(panel_button_release), panel);
297 g_signal_connect(panel->window, "motion-notify-event",
298 G_CALLBACK(panel_motion_event), panel);
299 g_signal_connect(panel->window, "leave-notify-event",
300 G_CALLBACK(panel_leave_event), panel);
302 if (strchr(name, '/'))
303 load_path = g_strdup(name);
304 else
306 guchar *leaf;
308 leaf = g_strconcat("pan_", name, NULL);
309 load_path = choices_find_path_load(leaf, PROJECT);
310 g_free(leaf);
313 if (panel->side == PANEL_RIGHT)
314 align = gtk_alignment_new(1.0, 0.0, 0.0, 1.0);
315 else if (panel->side == PANEL_BOTTOM)
316 align = gtk_alignment_new(0.0, 1.0, 1.0, 0.0);
317 else if (panel->side == PANEL_TOP)
318 align = gtk_alignment_new(0.0, 0.0, 1.0, 0.0);
319 else
320 align = gtk_alignment_new(0.0, 0.0, 0.0, 1.0);
322 gtk_container_add(GTK_CONTAINER(panel->window), align);
324 vp = gtk_viewport_new(NULL, NULL);
325 gtk_container_set_resize_mode(GTK_CONTAINER(vp), GTK_RESIZE_PARENT);
326 gtk_viewport_set_shadow_type(GTK_VIEWPORT(vp), GTK_SHADOW_NONE);
327 gtk_container_add(GTK_CONTAINER(align), vp);
329 g_signal_connect(align, "expose-event",
330 G_CALLBACK(draw_panel_edge), panel);
332 if (side == PANEL_TOP || side == PANEL_BOTTOM)
334 panel->adj = gtk_viewport_get_hadjustment(GTK_VIEWPORT(vp));
335 box = gtk_hbox_new(FALSE, 0);
336 panel->before = gtk_hbox_new(FALSE, 0);
337 panel->after = gtk_hbox_new(FALSE, 0);
339 else
341 panel->adj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(vp));
342 box = gtk_vbox_new(FALSE, 0);
343 panel->before = gtk_vbox_new(FALSE, 0);
344 panel->after = gtk_vbox_new(FALSE, 0);
347 gtk_container_add(GTK_CONTAINER(vp), box);
348 gtk_box_pack_start(GTK_BOX(box), panel->before, FALSE, TRUE, 0);
349 gtk_box_pack_end(GTK_BOX(box), panel->after, FALSE, TRUE, 0);
351 frame = make_insert_frame(panel);
352 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
354 /* This is used so that we can find the middle easily! */
355 panel->gap = gtk_event_box_new();
356 gtk_box_pack_start(GTK_BOX(box), panel->gap, FALSE, FALSE, 0);
358 frame = make_insert_frame(panel);
359 g_object_set_data(G_OBJECT(frame), "after", "yes");
360 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
362 gtk_widget_realize(panel->window);
363 make_panel_window(panel->window);
364 if (!(gtk_major_version == 2 && gtk_minor_version == 0))
365 gtk_window_stick(GTK_WINDOW(panel->window));
367 gtk_widget_show_all(align);
369 loading_panel = panel;
370 if (load_path && access(load_path, F_OK) == 0)
372 xmlDocPtr doc;
373 doc = xmlParseFile(load_path);
374 if (doc)
376 panel_load_from_xml(panel, doc);
377 xmlFreeDoc(doc);
379 else
381 parse_file(load_path, pan_from_file);
382 info_message(_("Your old panel file has been "
383 "converted to the new XML format."));
384 panel_save(panel);
387 else
389 /* Don't scare users with an empty panel... */
390 guchar *apps;
392 panel_add_item(panel, "~", "Home", FALSE, NULL);
394 apps = pathdup(make_path(app_dir, ".."));
395 if (apps)
397 panel_add_item(panel, apps, "Apps", FALSE, NULL);
398 g_free(apps);
401 loading_panel = NULL;
402 g_free(load_path);
404 current_panel[side] = panel;
406 gtk_widget_queue_resize(box);
407 g_signal_connect(panel->window, "size-request",
408 G_CALLBACK(panel_post_resize), panel);
409 g_signal_connect(panel->window, "size-allocate",
410 G_CALLBACK(reposition_panel), panel);
412 number_of_windows++;
413 gdk_window_lower(panel->window->window);
414 gtk_widget_show(panel->window);
417 GdkWindow *pinboard;
419 pinboard = pinboard_get_window();
420 /* (if pinboard is NULL, will go right to the back) */
421 window_put_just_above(panel->window->window, pinboard);
424 return panel;
427 /* Externally visible function to add an item to a panel */
428 gboolean panel_add(PanelSide side,
429 const gchar *path, const gchar *label, gboolean after)
431 g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, FALSE);
433 g_return_val_if_fail(current_panel[side] != NULL, FALSE);
435 panel_add_item(current_panel[side], path, label, after, NULL);
437 return TRUE;
440 /* Add the area covered by the panels to the region */
441 void panel_mark_used(GdkRegion *used)
443 int i;
445 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
447 Panel *panel = current_panel[i];
448 GdkRectangle rect;
450 if (!panel)
451 continue;
453 gdk_window_get_root_origin(panel->window->window,
454 &rect.x, &rect.y);
455 rect.width = panel->window->allocation.width;
456 rect.height = panel->window->allocation.height;
458 gdk_region_union_with_rect(used, &rect);
462 /* On xrandr screen size changes, update all panels */
463 void panel_update_size(void)
465 int i;
467 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
469 if (current_panel[i])
470 panel_update(current_panel[i]);
474 /****************************************************************
475 * INTERNAL FUNCTIONS *
476 ****************************************************************/
478 /* User has tried to close the panel via the window manager - confirm */
479 static int panel_delete(GtkWidget *widget, GdkEvent *event, Panel *panel)
481 return !confirm(_("You have tried to close a panel via the window "
482 "manager - I usually find that this is accidental... "
483 "really close?"),
484 GTK_STOCK_CLOSE, NULL);
487 static void panel_destroyed(GtkWidget *widget, Panel *panel)
489 if (current_panel[panel->side] == panel)
490 current_panel[panel->side] = NULL;
492 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
494 if (current_panel[PANEL_RIGHT])
495 gtk_widget_queue_resize(
496 current_panel[PANEL_RIGHT]->window);
497 if (current_panel[PANEL_LEFT])
498 gtk_widget_queue_resize(
499 current_panel[PANEL_LEFT]->window);
502 if (panel->autoscroll_speed)
503 g_source_remove(panel->autoscroll_to);
505 g_free(panel->name);
506 g_free(panel);
508 one_less_window();
511 static void panel_load_side(Panel *panel, xmlNodePtr side, gboolean after)
513 xmlNodePtr node;
514 char *label, *path, *shortcut;
516 for (node = side->xmlChildrenNode; node; node = node->next)
518 if (node->type != XML_ELEMENT_NODE)
519 continue;
520 if (strcmp(node->name, "icon") != 0)
521 continue;
523 label = xmlGetProp(node, "label");
524 if (!label)
525 label = g_strdup("<missing label>");
526 path = xmlNodeGetContent(node);
527 if (!path)
528 path = g_strdup("<missing path>");
529 shortcut = xmlGetProp(node, "shortcut");
531 panel_add_item(panel, path, label, after, shortcut);
533 g_free(path);
534 g_free(label);
535 g_free(shortcut);
539 /* Create one panel icon for each icon in the doc */
540 static void panel_load_from_xml(Panel *panel, xmlDocPtr doc)
542 xmlNodePtr root;
544 root = xmlDocGetRootElement(doc);
545 panel_load_side(panel, get_subnode(root, NULL, "start"), FALSE);
546 panel_load_side(panel, get_subnode(root, NULL, "end"), TRUE);
549 /* Called for each line in the config file while loading a new panel */
550 static const char *pan_from_file(gchar *line)
552 gchar *sep, *leaf;
554 g_return_val_if_fail(line != NULL, NULL);
555 g_return_val_if_fail(loading_panel != NULL, NULL);
557 if (*line == '\0')
558 return NULL;
560 sep = strpbrk(line, "<>");
561 if (!sep)
562 return _("Missing < or > in panel config file");
564 if (sep != line)
565 leaf = g_strndup(line, sep - line);
566 else
567 leaf = NULL;
569 panel_add_item(loading_panel, sep + 1, leaf, sep[0] == '>', NULL);
571 g_free(leaf);
573 return NULL;
576 static gboolean icon_pointer_in(GtkWidget *widget,
577 GdkEventCrossing *event,
578 Icon *icon)
580 gtk_widget_set_state(widget,
581 icon->selected ? GTK_STATE_SELECTED : GTK_STATE_PRELIGHT);
583 return 0;
586 static gboolean icon_pointer_out(GtkWidget *widget,
587 GdkEventCrossing *event,
588 Icon *icon)
590 gtk_widget_set_state(widget,
591 icon->selected ? GTK_STATE_SELECTED : GTK_STATE_NORMAL);
593 return 0;
596 static void panel_icon_destroyed(PanelIcon *pi)
598 g_return_if_fail(pi->widget != NULL);
600 pi->widget = NULL;
602 g_object_unref(pi);
605 /* Set the tooltip AND hide/show the label */
606 static void panel_icon_set_tip(PanelIcon *pi)
608 XMLwrapper *ai;
609 xmlNode *node;
610 Icon *icon = (Icon *) pi;
612 g_return_if_fail(pi != NULL);
614 if (pi->label)
616 if (panel_want_show_text(pi))
617 gtk_widget_show(pi->label);
618 else
619 gtk_widget_hide(pi->label);
622 if (pi->socket)
623 ai = NULL;
624 else
625 ai = appinfo_get(icon->path, icon->item);
627 if (ai && ((node = xml_get_section(ai, NULL, "Summary"))))
629 guchar *str;
630 str = xmlNodeListGetString(node->doc,
631 node->xmlChildrenNode, 1);
632 if (str)
634 gtk_tooltips_set_tip(tooltips, pi->widget, str, NULL);
635 g_free(str);
638 else if ((!panel_want_show_text(pi)) && !pi->socket)
640 gtk_tooltips_set_tip(tooltips, pi->widget,
641 icon->item->leafname, NULL);
643 else
644 gtk_tooltips_set_tip(tooltips, pi->widget, NULL, NULL);
646 if (ai)
647 g_object_unref(ai);
650 /* Add an icon with this path to the panel. If after is TRUE then the
651 * icon is added to the right/bottom end of the panel.
653 * If name is NULL a suitable name is taken from path.
655 static void panel_add_item(Panel *panel,
656 const gchar *path,
657 const gchar *name,
658 gboolean after,
659 const gchar *shortcut)
661 GtkWidget *widget;
662 PanelIcon *pi;
663 Icon *icon;
665 g_return_if_fail(panel != NULL);
666 g_return_if_fail(path != NULL);
668 widget = gtk_event_box_new();
669 gtk_widget_set_events(widget,
670 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
671 GDK_BUTTON3_MOTION_MASK |
672 GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
673 GDK_BUTTON_RELEASE_MASK);
675 gtk_box_pack_start(GTK_BOX(after ? panel->after : panel->before),
676 widget, FALSE, TRUE, 0);
677 if (after)
678 gtk_box_reorder_child(GTK_BOX(panel->after), widget, 0);
680 gtk_widget_realize(widget);
682 pi = panel_icon_new(panel, path, name);
683 icon = (Icon *) pi;
685 /* Widget takes the initial ref of Icon */
686 g_object_set_data(G_OBJECT(widget), "icon", pi);
688 pi->widget = widget;
689 g_object_ref(widget);
691 gtk_widget_set_name(pi->widget, "panel-icon");
693 g_signal_connect_swapped(widget, "destroy",
694 G_CALLBACK(panel_icon_destroyed), pi);
696 if (icon->item->base_type == TYPE_DIRECTORY)
697 run_applet(pi);
699 g_signal_connect(widget, "button_release_event",
700 G_CALLBACK(icon_button_release), pi);
701 g_signal_connect(widget, "button_press_event",
702 G_CALLBACK(icon_button_press), pi);
703 g_signal_connect(widget, "motion-notify-event",
704 G_CALLBACK(icon_motion_event), pi);
705 g_signal_connect(widget, "enter-notify-event",
706 G_CALLBACK(icon_pointer_in), pi);
707 g_signal_connect(widget, "leave-notify-event",
708 G_CALLBACK(icon_pointer_out), pi);
710 if (!pi->socket)
712 g_signal_connect(widget, "enter-notify-event",
713 G_CALLBACK(enter_icon), pi);
714 g_signal_connect(widget, "expose_event",
715 G_CALLBACK(expose_icon_background), pi);
716 g_signal_connect_after(widget, "expose_event",
717 G_CALLBACK(expose_icon), pi);
718 g_signal_connect(widget, "drag_data_get",
719 G_CALLBACK(drag_data_get), NULL);
721 g_signal_connect(widget, "size_request",
722 G_CALLBACK(size_request), pi);
724 drag_set_panel_dest(pi);
726 pi->label = gtk_label_new(icon->item->leafname);
727 gtk_container_add(GTK_CONTAINER(pi->widget), pi->label);
728 gtk_misc_set_alignment(GTK_MISC(pi->label), 0.5, 1);
729 gtk_misc_set_padding(GTK_MISC(pi->label), 1, 2);
732 icon_set_shortcut(icon, shortcut);
734 if (!loading_panel)
735 panel_save(panel);
737 panel_icon_set_tip(pi);
738 gtk_widget_show(widget);
741 /* Called when Gtk+ wants to know how much space an icon needs.
742 * 'req' is already big enough for the label, if shown.
744 static void size_request(GtkWidget *widget, GtkRequisition *req, PanelIcon *pi)
746 Icon *icon = (Icon *) pi;
747 gboolean horz = (pi->panel->side == PANEL_TOP ||
748 pi->panel->side == PANEL_BOTTOM);
749 int max_width = 100;
750 int max_height = 100;
751 int image_width, image_height;
753 if (horz)
754 max_height = o_panel_width.int_value - req->height;
755 else
756 max_width = MAX(o_panel_width.int_value, req->width);
758 /* TODO: really need to recreate? */
759 if (pi->image)
760 g_object_unref(pi->image);
762 pi->image = scale_pixbuf(icon->item->image->src_pixbuf,
763 MAX(20, max_width), MAX(20, max_height));
765 image_width = gdk_pixbuf_get_width(pi->image);
766 image_height = gdk_pixbuf_get_height(pi->image);
768 if (req->height > 0 && max_height < req->height)
770 pi->style = TEXT_BESIDE_ICON;
771 req->width += image_width;
772 req->height = MAX(req->height, image_height);
773 gtk_misc_set_alignment(GTK_MISC(pi->label), 1, 0.5);
775 else
777 pi->style = TEXT_UNDER_ICON;
778 req->width = MAX(req->width, image_width);
779 req->height += image_height;
780 gtk_misc_set_alignment(GTK_MISC(pi->label), 0.5, 1);
783 if (horz)
784 req->width += PANEL_ICON_SPACING;
785 else
786 req->height += PANEL_ICON_SPACING;
789 static gint expose_icon_background(GtkWidget *widget,
790 GdkEventExpose *event,
791 PanelIcon *pi)
793 if (((Icon *) pi)->selected)
794 gtk_paint_flat_box(widget->style, widget->window,
795 GTK_STATE_SELECTED, GTK_SHADOW_NONE,
796 &event->area, widget, NULL, 0, 0,
797 widget->allocation.width,
798 widget->allocation.height);
800 return FALSE;
803 static gint expose_icon(GtkWidget *widget,
804 GdkEventExpose *event,
805 PanelIcon *pi)
807 return draw_icon(widget, &event->area, pi);
810 static gint draw_icon(GtkWidget *widget, GdkRectangle *badarea, PanelIcon *pi)
812 GdkRectangle area;
813 int width, height;
814 Icon *icon = (Icon *) pi;
815 int image_x;
816 int image_y;
817 GdkPixbuf *image;
818 int text_height = 0;
820 gdk_drawable_get_size(widget->window, &area.width, &area.height);
822 if (panel_want_show_text(pi))
823 text_height = pi->label->requisition.height;
825 g_return_val_if_fail(pi->image != NULL, FALSE);
827 image = pi->image;
829 width = gdk_pixbuf_get_width(image);
830 height = gdk_pixbuf_get_height(image);
832 if (pi->style == TEXT_UNDER_ICON)
834 image_x = (area.width - width) >> 1;
835 image_y = (area.height - height - text_height) >> 1;
837 else
839 image_x = PANEL_ICON_SPACING - 2;
840 image_y = (area.height - height) >> 1;
843 gdk_pixbuf_render_to_drawable_alpha(
844 image,
845 widget->window,
846 0, 0, /* src */
847 image_x, image_y, /* dest */
848 width, height,
849 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
850 GDK_RGB_DITHER_NORMAL, 0, 0);
852 if (icon->item->flags & ITEM_FLAG_SYMLINK)
854 gdk_pixbuf_render_to_drawable_alpha(im_symlink->pixbuf,
855 widget->window,
856 0, 0, /* src */
857 image_x, image_y + 2, /* dest */
858 -1, -1,
859 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
860 GDK_RGB_DITHER_NORMAL, 0, 0);
862 if (icon->item->flags & ITEM_FLAG_MOUNT_POINT)
864 MaskedPixmap *mp = icon->item->flags & ITEM_FLAG_MOUNTED
865 ? im_mounted
866 : im_unmounted;
868 gdk_pixbuf_render_to_drawable_alpha(mp->pixbuf,
869 widget->window,
870 0, 0, /* src */
871 image_x, image_y + 2, /* dest */
872 -1, -1,
873 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
874 GDK_RGB_DITHER_NORMAL, 0, 0);
876 return FALSE;
879 static void panel_icon_wink(Icon *icon)
881 PanelIcon *pi = (PanelIcon *) icon;
883 wink_widget(pi->widget);
886 /* icon may be NULL if the event is on the background */
887 static void perform_action(Panel *panel, PanelIcon *pi, GdkEventButton *event)
889 BindAction action;
890 Icon *icon = (Icon *) pi;
892 action = bind_lookup_bev(icon ? BIND_PANEL_ICON : BIND_PANEL, event);
894 if (pi && pi->socket)
895 if (action != ACT_POPUP_MENU && action != ACT_MOVE_ICON)
896 return;
898 switch (action)
900 case ACT_OPEN_ITEM:
901 dnd_motion_ungrab();
902 wink_widget(pi->widget);
903 run_diritem(icon->path, icon->item, NULL, NULL, FALSE);
904 break;
905 case ACT_EDIT_ITEM:
906 dnd_motion_ungrab();
907 wink_widget(pi->widget);
908 run_diritem(icon->path, icon->item, NULL, NULL, TRUE);
909 break;
910 case ACT_POPUP_MENU:
911 dnd_motion_ungrab();
912 panel_show_menu(event, pi, panel);
913 break;
914 case ACT_MOVE_ICON:
915 dnd_motion_start(MOTION_REPOSITION);
916 break;
917 case ACT_PRIME_AND_SELECT:
918 if (!icon->selected)
919 icon_select_only(icon);
920 dnd_motion_start(MOTION_READY_FOR_DND);
921 break;
922 case ACT_PRIME_AND_TOGGLE:
923 icon_set_selected(icon, !icon->selected);
924 dnd_motion_start(MOTION_READY_FOR_DND);
925 break;
926 case ACT_PRIME_FOR_DND:
927 dnd_motion_start(MOTION_READY_FOR_DND);
928 break;
929 case ACT_TOGGLE_SELECTED:
930 icon_set_selected(icon, !icon->selected);
931 break;
932 case ACT_SELECT_EXCL:
933 icon_set_selected(icon, TRUE);
934 break;
935 case ACT_IGNORE:
936 break;
937 case ACT_CLEAR_SELECTION:
938 dnd_motion_ungrab();
939 icon_select_only(NULL);
940 break;
941 default:
942 g_warning("Unsupported action : %d\n", action);
943 break;
947 static gint panel_button_release(GtkWidget *widget,
948 GdkEventButton *event,
949 Panel *panel)
951 if (dnd_motion_release(event))
952 return TRUE;
954 perform_action(panel, NULL, event);
956 return TRUE;
959 static gint panel_button_press(GtkWidget *widget,
960 GdkEventButton *event,
961 Panel *panel)
963 if (dnd_motion_press(panel->window, event))
964 perform_action(panel, NULL, event);
966 return TRUE;
969 static gint icon_button_release(GtkWidget *widget,
970 GdkEventButton *event,
971 PanelIcon *pi)
973 if (pi->socket && event->button == 1)
974 return FALSE; /* Restart button */
976 if (dnd_motion_release(event))
977 return TRUE;
979 perform_action(pi->panel, pi, event);
981 return TRUE;
984 static gint icon_button_press(GtkWidget *widget,
985 GdkEventButton *event,
986 PanelIcon *pi)
988 if (pi->socket && event->button == 1)
989 return FALSE; /* Restart button */
991 if (dnd_motion_press(widget, event))
992 perform_action(pi->panel, pi, event);
994 return TRUE;
997 static void reposition_panel(GtkWidget *window,
998 GtkAllocation *alloc, Panel *panel)
1000 int x = panel_geometry.x;
1001 int y = panel_geometry.y;
1002 int thickness;
1003 PanelSide side = panel->side;
1005 if (side == PANEL_LEFT || side == PANEL_RIGHT)
1007 if (side == PANEL_RIGHT)
1008 x += panel_geometry.width - alloc->width;
1010 if (current_panel[PANEL_TOP])
1012 GtkWidget *win = current_panel[PANEL_TOP]->window;
1013 y += win->allocation.height;
1017 if (side == PANEL_BOTTOM)
1018 y += panel_geometry.height - alloc->height;
1020 gtk_window_move(GTK_WINDOW(panel->window), x, y);
1021 gdk_window_move(panel->window->window, x, y);
1023 if (side == PANEL_BOTTOM || side == PANEL_TOP)
1025 if (current_panel[PANEL_RIGHT])
1026 gtk_widget_queue_resize(
1027 current_panel[PANEL_RIGHT]->window);
1028 if (current_panel[PANEL_LEFT])
1029 gtk_widget_queue_resize(
1030 current_panel[PANEL_LEFT]->window);
1033 /* Stop windows from maximising over all/part of us */
1035 struct {
1036 guint32 left, right, top, bottom;
1037 guint32 left_start_y, left_end_y;
1038 guint32 right_start_y, right_end_y;
1039 guint32 top_start_x, top_end_x;
1040 guint32 bottom_start_x, bottom_end_x;
1041 } strut = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1043 if (o_panel_avoid.int_value == FALSE)
1044 thickness = 2;
1045 else if (panel->side == PANEL_TOP ||
1046 panel->side == PANEL_BOTTOM)
1047 thickness = alloc->height;
1048 else
1049 thickness = alloc->width;
1051 switch (panel->side)
1053 case PANEL_LEFT:
1054 if (!o_panel_xinerama.int_value ||
1055 !monitor_adjacent
1056 [o_panel_monitor.int_value].left)
1058 strut.left = panel_geometry.x +
1059 thickness;
1060 strut.left_start_y = panel_geometry.y;
1061 strut.left_end_y = panel_geometry.y +
1062 panel_geometry.height - 1;
1064 /* else there is (part of) a monitor
1065 * to the left */
1066 else
1068 thickness = 0;
1070 break;
1071 case PANEL_RIGHT:
1072 if (!o_panel_xinerama.int_value ||
1073 !monitor_adjacent
1074 [o_panel_monitor.int_value].right)
1076 /* RHS of monitor might not abut edge
1077 * of total virtual screen */
1078 strut.right = screen_width -
1079 panel_geometry.x -
1080 panel_geometry.width +
1081 thickness;
1082 strut.right_start_y = panel_geometry.y;
1083 strut.right_end_y = panel_geometry.y +
1084 panel_geometry.height - 1;
1086 /* else there is (part of) a monitor
1087 * to the right */
1088 else
1090 thickness = 0;
1092 break;
1093 case PANEL_TOP:
1094 if (!o_panel_xinerama.int_value ||
1095 !monitor_adjacent
1096 [o_panel_monitor.int_value].top)
1098 strut.top = panel_geometry.y +
1099 thickness;
1100 strut.top_start_x = panel_geometry.x;
1101 strut.top_end_x = panel_geometry.x +
1102 panel_geometry.width - 1;
1104 /* else there is (part of) a monitor above */
1105 else
1107 thickness = 0;
1109 break;
1110 default: /* PANEL_BOTTOM */
1111 if (!o_panel_xinerama.int_value ||
1112 !monitor_adjacent
1113 [o_panel_monitor.int_value].bottom)
1115 /* Bottom of monitor might not abut
1116 * edge of total virtual screen */
1117 strut.bottom = screen_height -
1118 panel_geometry.y -
1119 panel_geometry.height +
1120 thickness;
1121 strut.bottom_start_x = panel_geometry.x;
1122 strut.bottom_end_x = panel_geometry.x +
1123 panel_geometry.width - 1;
1125 /* else there is (part of) a monitor below */
1126 else
1128 thickness = 0;
1130 break;
1133 if (thickness)
1135 /* Set full-width strut as well as partial in case
1136 * partial isn't supported by wm */
1137 gdk_property_change(panel->window->window,
1138 gdk_atom_intern("_NET_WM_STRUT",
1139 FALSE),
1140 gdk_atom_intern("CARDINAL", FALSE),
1141 32, GDK_PROP_MODE_REPLACE,
1142 (gchar *) &strut, 4);
1143 gdk_property_change(panel->window->window,
1144 gdk_atom_intern("_NET_WM_STRUT_PARTIAL",
1145 FALSE),
1146 gdk_atom_intern("CARDINAL", FALSE),
1147 32, GDK_PROP_MODE_REPLACE,
1148 (gchar *) &strut, 12);
1150 else
1152 gdk_property_delete(panel->window->window,
1153 gdk_atom_intern("_NET_WM_STRUT_PARTIAL",
1154 FALSE));
1155 gdk_property_delete(panel->window->window,
1156 gdk_atom_intern("_NET_WM_STRUT",
1157 FALSE));
1163 /* Same as drag_set_dest(), but for panel icons */
1164 static void drag_set_panel_dest(PanelIcon *pi)
1166 GtkWidget *obj = pi->widget;
1168 make_drop_target(pi->widget, 0);
1170 g_signal_connect(obj, "drag_motion", G_CALLBACK(drag_motion), pi);
1171 g_signal_connect(obj, "drag_leave", G_CALLBACK(drag_leave), pi);
1172 g_signal_connect(obj, "drag_end", G_CALLBACK(drag_end), pi);
1175 static gboolean drag_motion(GtkWidget *widget,
1176 GdkDragContext *context,
1177 gint x,
1178 gint y,
1179 guint time,
1180 PanelIcon *pi)
1182 GdkDragAction action = context->suggested_action;
1183 const char *type = NULL;
1184 Icon *icon = (Icon *) pi;
1185 DirItem *item = icon->item;
1186 int panel_x, panel_y;
1188 gdk_window_get_pointer(pi->panel->window->window,
1189 &panel_x, &panel_y, NULL);
1190 motion_may_raise(pi->panel, panel_x, panel_y);
1192 /* Should we scroll the panel when dragging? */
1193 if (motion_state != MOTION_REPOSITION)
1194 if (pi->panel->autoscroll_speed == 0)
1195 may_autoscroll(pi->panel);
1197 if (icon->selected)
1198 goto out; /* Can't drag a selection to itself */
1200 type = dnd_motion_item(context, &item);
1202 if ((context->actions & GDK_ACTION_ASK) && o_dnd_left_menu.int_value
1203 && type != drop_dest_prog)
1205 guint state;
1206 gdk_window_get_pointer(NULL, NULL, NULL, &state);
1207 if (state & GDK_BUTTON1_MASK)
1208 action = GDK_ACTION_ASK;
1211 if (!item)
1212 type = NULL;
1213 out:
1214 /* We actually must pretend to accept the drop, even if the
1215 * directory isn't writeable, so that the spring-opening
1216 * thing works.
1219 /* Don't allow drops to non-writeable directories */
1220 if (o_dnd_spring_open.int_value == FALSE &&
1221 type == drop_dest_dir &&
1222 access(icon->path, W_OK) != 0)
1224 type = NULL;
1227 g_dataset_set_data(context, "drop_dest_type", (gpointer) type);
1228 if (type)
1230 gdk_drag_status(context, action, time);
1231 g_dataset_set_data_full(context, "drop_dest_path",
1232 g_strdup(icon->path), g_free);
1233 if (type == drop_dest_dir)
1234 dnd_spring_load(context, NULL);
1236 if (dnd_highlight && dnd_highlight != pi->widget)
1238 gtk_drag_unhighlight(dnd_highlight);
1239 dnd_highlight = NULL;
1242 if (dnd_highlight == NULL)
1244 gtk_drag_highlight(pi->widget);
1245 dnd_highlight = pi->widget;
1249 return type != NULL;
1253 static void add_uri_list(GtkWidget *widget,
1254 GdkDragContext *context,
1255 gint x,
1256 gint y,
1257 GtkSelectionData *selection_data,
1258 guint info,
1259 guint32 time,
1260 Panel *panel)
1262 gboolean after = FALSE;
1263 GList *uris, *next;
1265 if (!selection_data->data)
1266 return;
1268 g_return_if_fail(selection_data->data[selection_data->length] == '\0');
1270 if (g_object_get_data(G_OBJECT(widget), "after"))
1271 after = TRUE;
1273 uris = uri_list_to_glist(selection_data->data);
1275 for (next = uris; next; next = next->next)
1277 guchar *path;
1279 path = get_local_path((guchar *) next->data);
1281 if (path) {
1282 panel_add_item(panel, path, NULL, after, NULL);
1283 g_free(path);
1287 g_list_free(uris);
1290 static void drag_end(GtkWidget *widget,
1291 GdkDragContext *context,
1292 Icon *icon)
1294 if (tmp_icon_selected)
1296 icon_select_only(NULL);
1297 tmp_icon_selected = FALSE;
1301 static void drag_leave(GtkWidget *widget,
1302 GdkDragContext *context,
1303 guint32 time,
1304 Icon *icon)
1306 panel_drag_leave(widget, context, time, ((PanelIcon *) icon)->panel);
1308 if (dnd_highlight && dnd_highlight == widget)
1310 gtk_drag_unhighlight(dnd_highlight);
1311 dnd_highlight = NULL;
1314 dnd_spring_abort();
1317 /* Create XML icon nodes for these widgets.
1318 * Always frees the widgets list.
1320 static void make_widgets(xmlNodePtr side, GList *widgets)
1322 GList *next;
1324 for (next = widgets; next; next = next->next)
1326 Icon *icon;
1327 xmlNodePtr tree;
1329 icon = g_object_get_data(G_OBJECT(next->data), "icon");
1331 if (!icon)
1333 g_warning("Can't find Icon from widget\n");
1334 continue;
1337 tree = xmlNewTextChild(side, NULL, "icon", icon->src_path);
1339 xmlSetProp(tree, "label", icon->item->leafname);
1340 if (icon->shortcut)
1341 xmlSetProp(tree, "shortcut", icon->shortcut);
1344 if (widgets)
1345 g_list_free(widgets);
1348 void panel_save(Panel *panel)
1350 xmlDocPtr doc;
1351 xmlNodePtr root;
1352 guchar *save = NULL;
1353 guchar *save_new = NULL;
1355 g_return_if_fail(panel != NULL);
1357 if (strchr(panel->name, '/'))
1358 save = g_strdup(panel->name);
1359 else
1361 guchar *leaf;
1363 leaf = g_strconcat("pan_", panel->name, NULL);
1364 save = choices_find_path_save(leaf, PROJECT, TRUE);
1365 g_free(leaf);
1368 if (!save)
1369 return;
1371 doc = xmlNewDoc("1.0");
1372 xmlDocSetRootElement(doc, xmlNewDocNode(doc, NULL, "panel", NULL));
1374 root = xmlDocGetRootElement(doc);
1375 make_widgets(xmlNewChild(root, NULL, "start", NULL),
1376 gtk_container_get_children(GTK_CONTAINER(panel->before)));
1378 make_widgets(xmlNewChild(root, NULL, "end", NULL),
1379 g_list_reverse(gtk_container_get_children(
1380 GTK_CONTAINER(panel->after))));
1382 save_new = g_strconcat(save, ".new", NULL);
1383 if (save_xml_file(doc, save_new) || rename(save_new, save))
1384 delayed_error(_("Error saving panel %s: %s"),
1385 save, g_strerror(errno));
1386 g_free(save_new);
1388 g_free(save);
1389 if (doc)
1390 xmlFreeDoc(doc);
1393 /* Create a frame widget which can be used to add icons to the panel */
1394 static GtkWidget *make_insert_frame(Panel *panel)
1396 GtkWidget *frame;
1397 GtkTargetEntry target_table[] = {
1398 {"text/uri-list", 0, TARGET_URI_LIST},
1401 frame = gtk_frame_new(NULL);
1402 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
1403 gtk_widget_set_size_request(frame, 16, 16);
1405 g_signal_connect(frame, "drag-motion",
1406 G_CALLBACK(insert_drag_motion), panel);
1407 g_signal_connect(frame, "drag-leave",
1408 G_CALLBACK(panel_drag_leave), panel);
1410 g_signal_connect(frame, "drag-data-received",
1411 G_CALLBACK(add_uri_list), panel);
1412 gtk_drag_dest_set(frame,
1413 GTK_DEST_DEFAULT_ALL,
1414 target_table,
1415 sizeof(target_table) / sizeof(*target_table),
1416 GDK_ACTION_COPY);
1418 return frame;
1421 static gboolean enter_icon(GtkWidget *widget,
1422 GdkEventCrossing *event,
1423 Icon *icon)
1425 icon_may_update(icon);
1426 panel_icon_set_tip((PanelIcon *) icon);
1428 return FALSE;
1431 static gint panel_leave_event(GtkWidget *widget,
1432 GdkEventCrossing *event,
1433 Panel *panel)
1435 GdkWindow *pinboard;
1437 if (event->mode != GDK_CROSSING_NORMAL)
1438 return FALSE; /* Grab for menu, DnD, etc */
1440 pinboard = pinboard_get_window();
1441 window_put_just_above(panel->window->window, pinboard);
1443 return FALSE;
1446 /* If (x, y) is at the edge of the panel then raise */
1447 static void motion_may_raise(Panel *panel, int x, int y)
1449 gboolean raise;
1451 if (panel->side == PANEL_TOP)
1452 raise = y == 0;
1453 else if (panel->side == PANEL_BOTTOM)
1454 raise = y == panel->window->allocation.height - 1;
1455 else if (panel->side == PANEL_LEFT)
1456 raise = x == 0;
1457 else
1458 raise = x == panel->window->allocation.width - 1;
1460 if (raise)
1461 gdk_window_raise(panel->window->window);
1464 static gboolean may_autoscroll(Panel *panel)
1466 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1467 gint max, panel_x, panel_y, delta, new;
1469 if (panel->adj->upper <= panel->adj->page_size)
1470 goto stop_scrolling; /* Can see everything already */
1472 gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
1474 if (horz)
1476 delta = panel_x;
1477 max = panel->window->allocation.width;
1478 if (panel_y < 0 || panel_y > panel->window->allocation.height)
1479 goto stop_scrolling; /* Not over the panel */
1481 else
1483 delta = panel_y;
1484 max = panel->window->allocation.height;
1485 if (panel_x < 0 || panel_x > panel->window->allocation.width)
1486 goto stop_scrolling; /* Not over the panel */
1489 if (delta >= 20 && delta <= max - 20)
1490 goto stop_scrolling; /* Not at either end */
1492 panel->autoscroll_speed = MIN(panel->autoscroll_speed + 2, 200);
1494 new = panel->adj->value - ((delta < 20) ? panel->autoscroll_speed
1495 : -panel->autoscroll_speed);
1496 new = CLAMP(new, 0, panel->adj->upper - panel->adj->page_size);
1497 gtk_adjustment_set_value(panel->adj, new);
1499 panel->autoscroll_to = g_timeout_add(40,
1500 (GSourceFunc) may_autoscroll, panel);
1502 return FALSE;
1504 stop_scrolling:
1505 panel->autoscroll_speed = 0;
1506 return FALSE;
1509 static gint panel_motion_event(GtkWidget *widget,
1510 GdkEventMotion *event,
1511 Panel *panel)
1513 motion_may_raise(panel, event->x, event->y);
1515 if (motion_state != MOTION_REPOSITION)
1516 if (panel->autoscroll_speed == 0)
1517 may_autoscroll(panel);
1519 return FALSE;
1522 static gint icon_motion_event(GtkWidget *widget,
1523 GdkEventMotion *event,
1524 PanelIcon *pi)
1526 Panel *panel = pi->panel;
1527 GList *list, *me;
1528 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1529 int val;
1530 int dir = 0;
1532 if (motion_state == MOTION_READY_FOR_DND)
1534 if (dnd_motion_moved(event))
1535 start_drag(pi, event);
1536 return TRUE;
1538 else if (motion_state != MOTION_REPOSITION)
1539 return FALSE;
1541 list = gtk_container_get_children(GTK_CONTAINER(panel->before));
1542 list = g_list_append(list, NULL); /* The gap in the middle */
1543 list = g_list_concat(list,
1544 gtk_container_get_children(GTK_CONTAINER(panel->after)));
1545 me = g_list_find(list, widget);
1547 g_return_val_if_fail(me != NULL, TRUE);
1549 val = horz ? event->x_root : event->y_root;
1551 if (me->prev)
1553 GtkWidget *prev;
1554 int x, y;
1556 if (me->prev->data)
1557 prev = GTK_WIDGET(me->prev->data);
1558 else
1559 prev = panel->gap;
1561 gdk_window_get_origin(prev->window, &x, &y);
1563 if (val <= (horz ? x : y))
1564 dir = -1;
1567 if (dir == 0 && me->next)
1569 GtkWidget *next;
1570 int x, y, w, h;
1572 if (me->next->data)
1573 next = GTK_WIDGET(me->next->data);
1574 else
1575 next = panel->gap;
1577 gdk_window_get_origin(next->window, &x, &y);
1579 gdk_drawable_get_size(next->window, &w, &h);
1581 x += w;
1582 y += h;
1584 if (val >= (horz ? x : y)-1)
1586 if (next == panel->gap)
1587 dir = +2;
1588 else
1589 dir = +1;
1593 if (dir)
1594 reposition_icon(pi, g_list_index(list, widget) + dir);
1596 return TRUE;
1599 static void reposition_icon_on_side(GtkWidget *side, GtkWidget *widget,
1600 int index)
1602 GList *list;
1604 list = gtk_container_get_children(GTK_CONTAINER(side));
1606 /* Want to move icon to the list in the given 'side'. Is it there
1607 * already?
1610 if (!g_list_find(list, widget))
1612 /* No, reparent */
1613 gtk_grab_remove(widget);
1614 gtk_widget_reparent(widget, side);
1615 dnd_motion_grab_pointer();
1616 gtk_grab_add(widget);
1619 gtk_box_reorder_child(GTK_BOX(side), widget, index);
1621 g_list_free(list);
1624 /* Move icon to this index in the complete widget list.
1625 * 0 makes the icon the left-most icon. The gap in the middle has
1626 * an index number, which allows you to specify that the icon should
1627 * go on the left or right side.
1629 static void reposition_icon(PanelIcon *pi, int index)
1631 Panel *panel = pi->panel;
1632 GtkWidget *widget = pi->widget;
1633 GList *list;
1634 int before_len;
1636 list = gtk_container_get_children(GTK_CONTAINER(panel->before));
1637 before_len = g_list_length(list);
1638 g_list_free(list);
1640 if (index <= before_len)
1641 reposition_icon_on_side(panel->before, widget, index);
1642 else
1643 reposition_icon_on_side(panel->after, widget,
1644 index - (before_len + 1));
1646 panel_save(panel);
1649 static void start_drag(PanelIcon *pi, GdkEventMotion *event)
1651 GtkWidget *widget = pi->widget;
1652 Icon *icon = (Icon *) pi;
1654 if (!icon->selected)
1656 if (event->state & GDK_BUTTON1_MASK)
1658 /* Select just this one */
1659 icon_select_only(icon);
1660 tmp_icon_selected = TRUE;
1662 else
1663 icon_set_selected(icon, TRUE);
1666 g_return_if_fail(icon_selection != NULL);
1668 if (icon_selection->next == NULL)
1669 drag_one_item(widget, event, icon->path, icon->item, NULL);
1670 else
1672 guchar *uri_list;
1674 uri_list = icon_create_uri_list();
1675 drag_selection(widget, event, uri_list);
1676 g_free(uri_list);
1680 static void applet_died(GtkWidget *socket)
1682 gboolean never_plugged;
1684 never_plugged = (!g_object_get_data(G_OBJECT(socket), "lost_plug"))
1685 && !GTK_SOCKET(socket)->plug_window;
1687 if (never_plugged)
1689 report_error(
1690 _("Applet quit without ever creating a widget!"));
1691 gtk_widget_destroy(socket);
1694 gtk_widget_unref(socket);
1697 static void socket_destroyed(GtkWidget *socket, GtkWidget *widget)
1699 g_object_set_data(G_OBJECT(socket), "lost_plug", "yes");
1701 gtk_widget_unref(socket);
1703 gtk_widget_destroy(widget); /* Remove from panel */
1705 if (!closing_panel)
1706 panel_save(g_object_get_data(G_OBJECT(socket), "panel"));
1709 /* Try to run this applet.
1710 * Cases:
1712 * - No executable AppletRun:
1713 * icon->socket == NULL (unchanged) on return.
1715 * Otherwise, create socket (setting icon->socket) and ref it twice.
1717 * - AppletRun quits without connecting a plug:
1718 * On child death lost_plug is unset and socket is empty.
1719 * Unref socket.
1720 * Report error and destroy widget (to 'socket destroyed').
1722 * - AppletRun quits while plug is in socket:
1723 * Unref socket once. Socket will be destroyed later.
1725 * - Socket is destroyed.
1726 * Set lost_plug = "yes" and remove widget from panel.
1727 * Unref socket.
1729 static void run_applet(PanelIcon *pi)
1731 GError *error = NULL;
1732 char *argv[3];
1733 gint pid;
1734 Icon *icon = (Icon *) pi;
1736 argv[0] = (char *) make_path(icon->path, "AppletRun");
1738 if (access(argv[0], X_OK) != 0)
1739 return;
1741 pi->socket = gtk_socket_new();
1743 gtk_container_add(GTK_CONTAINER(pi->widget), pi->socket);
1744 gtk_widget_show_all(pi->socket);
1745 gtk_widget_realize(pi->socket);
1747 /* Always get button-2 events so we can drag */
1748 XGrabButton(gdk_display, Button2, AnyModifier,
1749 GDK_WINDOW_XWINDOW(pi->socket->window),
1750 False,
1751 ButtonPressMask | ButtonReleaseMask | Button2MotionMask,
1752 GrabModeAsync, /* Pointer */
1753 GrabModeAsync, /* Keyboard */
1754 None, None);
1757 gchar *pos;
1758 PanelSide side = pi->panel->side;
1760 /* Set a hint to let applets position their menus correctly */
1761 pos = g_strdup_printf("%s,%d",
1762 side == PANEL_TOP ? "Top" :
1763 side == PANEL_BOTTOM ? "Bottom" :
1764 side == PANEL_LEFT ? "Left" :
1765 "Right", MENU_MARGIN(side));
1766 gdk_property_change(pi->socket->window,
1767 gdk_atom_intern("_ROX_PANEL_MENU_POS", FALSE),
1768 gdk_atom_intern("STRING", FALSE),
1769 8, GDK_PROP_MODE_REPLACE,
1770 pos, strlen(pos));
1771 g_free(pos);
1774 g_object_set_data(G_OBJECT(pi->widget), "icon", pi);
1775 g_object_set_data(G_OBJECT(pi->socket), "panel", pi->panel);
1777 argv[1] = g_strdup_printf("%ld",
1778 GDK_WINDOW_XWINDOW(pi->socket->window));
1779 argv[2] = NULL;
1781 if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
1782 NULL, NULL, &pid, &error))
1784 delayed_error(_("Error running applet:\n%s"), error->message);
1785 g_error_free(error);
1786 gtk_widget_destroy(pi->socket);
1787 pi->socket = NULL;
1789 else
1791 gtk_widget_ref(pi->socket);
1792 on_child_death(pid, (CallbackFn) applet_died, pi->socket);
1794 gtk_widget_ref(pi->socket);
1795 g_signal_connect(pi->socket, "destroy",
1796 G_CALLBACK(socket_destroyed), pi->widget);
1799 g_free(argv[1]);
1802 static void panel_post_resize(GtkWidget *win, GtkRequisition *req, Panel *panel)
1804 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
1806 req->width = panel_geometry.width;
1807 req->height += EDGE_WIDTH;
1809 else
1811 int h = panel_geometry.height;
1813 if (current_panel[PANEL_TOP])
1815 GtkWidget *win = current_panel[PANEL_TOP]->window;
1816 h -= win->allocation.height;
1819 if (current_panel[PANEL_BOTTOM])
1821 GtkWidget *win = current_panel[PANEL_BOTTOM]->window;
1822 h -= win->allocation.height;
1825 req->height = h;
1826 req->width += EDGE_WIDTH;
1830 static void update_side(GtkWidget *side)
1832 GList *kids, *next;
1834 kids = gtk_container_get_children(GTK_CONTAINER(side));
1835 for (next = kids; next; next = next->next)
1837 PanelIcon *pi;
1838 pi = g_object_get_data(next->data, "icon");
1839 panel_icon_set_tip(pi);
1841 g_list_free(kids);
1844 /* Tips or style has changed -- update everything on this panel */
1845 static void panel_set_style(Panel *panel)
1847 update_side(panel->before);
1848 update_side(panel->after);
1849 gtk_widget_queue_resize(panel->window);
1852 static gboolean recreate_panels(char **names)
1854 int i;
1856 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
1858 if (names[i])
1860 panel_new(names[i], i);
1861 g_free(names[i]);
1865 g_free(names);
1867 return FALSE;
1870 static void update_side_size(GtkWidget *side)
1872 GList *kids, *next;
1874 kids = gtk_container_get_children(GTK_CONTAINER(side));
1875 for (next = kids; next; next = next->next)
1877 PanelIcon *pi;
1878 pi = g_object_get_data(next->data, "icon");
1879 gtk_widget_queue_resize(pi->widget);
1881 g_list_free(kids);
1884 /* Update panel size and redraw */
1885 static void panel_update(Panel *panel)
1887 update_side_size(panel->before);
1888 update_side_size(panel->after);
1889 gtk_widget_queue_resize(panel->window);
1890 gtk_widget_queue_draw(panel->window);
1893 static void panel_style_changed(void)
1895 int i;
1897 if (o_override_redirect.has_changed)
1899 gchar **names;
1901 names = g_new(char *, PANEL_NUMBER_OF_SIDES);
1903 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
1905 Panel *panel = current_panel[i];
1906 names[i] = panel ? g_strdup(panel->name) : NULL;
1907 panel_new(NULL, i);
1910 g_idle_add((GtkFunction) recreate_panels, names);
1913 if (o_panel_style.has_changed)
1915 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
1917 if (current_panel[i])
1918 panel_set_style(current_panel[i]);
1921 if (o_panel_width.has_changed)
1923 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
1925 if (current_panel[i])
1926 panel_update(current_panel[i]);
1930 if (o_panel_xinerama.has_changed || o_panel_monitor.has_changed ||
1931 o_panel_avoid.has_changed)
1933 if (panel_check_xinerama() || o_panel_avoid.has_changed)
1935 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
1937 if (current_panel[i])
1939 reposition_panel(
1940 current_panel[i]->window,
1941 &current_panel[i]->
1942 window->allocation,
1943 current_panel[i]);
1944 gtk_widget_queue_resize(
1945 current_panel[i]->window);
1952 static gboolean draw_panel_edge(GtkWidget *widget, GdkEventExpose *event,
1953 Panel *panel)
1955 int x, y, width, height;
1957 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
1959 width = panel_geometry.width;
1960 height = EDGE_WIDTH;
1962 x = 0;
1963 if (panel->side == PANEL_BOTTOM)
1964 y = 0;
1965 else
1966 y = widget->allocation.height - EDGE_WIDTH;
1968 else
1970 width = EDGE_WIDTH;
1971 height = panel_geometry.height;
1973 y = 0;
1974 if (panel->side == PANEL_RIGHT)
1975 x = 0;
1976 else
1977 x = widget->allocation.width - EDGE_WIDTH;
1980 gdk_draw_rectangle(widget->window,
1981 widget->style->fg_gc[GTK_STATE_NORMAL], TRUE,
1982 x, y, width, height);
1984 return FALSE;
1987 static gpointer parent_class;
1989 static void panel_icon_destroy(Icon *icon)
1991 PanelIcon *pi = (PanelIcon *) icon;
1993 g_return_if_fail(pi != NULL);
1995 if (pi->image)
1996 g_object_unref(pi->image);
1998 g_return_if_fail(pi->widget != NULL);
2000 gtk_widget_destroy(pi->widget);
2003 static void panel_remove_items(void)
2005 Panel *panel;
2007 g_return_if_fail(icon_selection != NULL);
2009 panel = ((PanelIcon *) icon_selection->data)->panel;
2011 while (icon_selection)
2012 icon_destroy((Icon *) icon_selection->data);
2014 panel_save(panel);
2017 static void panel_icon_redraw(Icon *icon)
2019 gtk_widget_set_state(((PanelIcon *) icon)->widget,
2020 icon->selected ? GTK_STATE_SELECTED
2021 : GTK_STATE_NORMAL);
2022 gtk_widget_queue_draw(PANEL_ICON(icon)->widget);
2025 static void panel_icon_update(Icon *icon)
2027 PanelIcon *pi = (PanelIcon *) icon;
2029 gtk_widget_queue_draw(pi->widget);
2030 gtk_label_set_text(GTK_LABEL(pi->label), icon->item->leafname);
2031 panel_icon_set_tip(pi);
2032 panel_save(pi->panel);
2035 /* The point of this is to clear the selection if the existing icons
2036 * aren't from the same panel...
2038 static gboolean panel_icon_same_group(Icon *icon, Icon *other)
2040 if (IS_PANEL_ICON(other))
2042 PanelIcon *a = (PanelIcon *) icon;
2043 PanelIcon *b = (PanelIcon *) other;
2045 return a->panel == b->panel;
2047 else
2048 return FALSE;
2051 static void panel_icon_class_init(gpointer gclass, gpointer data)
2053 IconClass *icon = (IconClass *) gclass;
2055 parent_class = g_type_class_peek_parent(gclass);
2057 icon->destroy = panel_icon_destroy;
2058 icon->redraw = panel_icon_redraw;
2059 icon->update = panel_icon_update;
2060 icon->remove_items = panel_remove_items;
2061 icon->same_group = panel_icon_same_group;
2062 icon->wink = panel_icon_wink;
2065 static void panel_icon_init(GTypeInstance *object, gpointer gclass)
2067 PanelIcon *pi = (PanelIcon *) object;
2069 pi->widget = NULL;
2070 pi->image = NULL;
2071 pi->label = NULL;
2072 pi->socket = NULL;
2073 pi->style = TEXT_UNDER_ICON;
2076 static GType panel_icon_get_type(void)
2078 static GType type = 0;
2080 if (!type)
2082 static const GTypeInfo info =
2084 sizeof (PanelIconClass),
2085 NULL, /* base_init */
2086 NULL, /* base_finalise */
2087 panel_icon_class_init,
2088 NULL, /* class_finalise */
2089 NULL, /* class_data */
2090 sizeof(PanelIcon),
2091 0, /* n_preallocs */
2092 panel_icon_init
2095 type = g_type_register_static(icon_get_type(),
2096 "PanelIcon", &info, 0);
2099 return type;
2102 static PanelIcon *panel_icon_new(Panel *panel,
2103 const char *pathname,
2104 const char *name)
2106 PanelIcon *pi;
2107 Icon *icon;
2109 pi = g_object_new(panel_icon_get_type(), NULL);
2110 icon = (Icon *) pi;
2112 icon_set_path(icon, pathname, name);
2113 pi->panel = panel;
2115 return pi;
2118 static gboolean panel_want_show_text(PanelIcon *pi)
2120 Icon *icon = (Icon *) pi;
2122 if (!icon->item->leafname[0])
2123 return FALSE;
2125 if (o_panel_style.int_value == SHOW_BOTH)
2126 return TRUE;
2127 if (o_panel_style.int_value == SHOW_ICON)
2128 return FALSE;
2130 if (icon->item->flags & ITEM_FLAG_APPDIR)
2131 return FALSE;
2133 return TRUE;
2136 static void panel_position_menu(GtkMenu *menu, gint *x, gint *y,
2137 gboolean *push_in, gpointer data)
2139 int *pos = (int *) data;
2140 GtkRequisition requisition;
2141 int margin = pos[2];
2143 gtk_widget_size_request(GTK_WIDGET(menu), &requisition);
2145 if (pos[0] == -1)
2146 *x = screen_width - margin - requisition.width;
2147 else if (pos[0] == -2)
2148 *x = margin;
2149 else
2150 *x = pos[0] - (requisition.width >> 2);
2152 if (pos[1] == -1)
2153 *y = screen_height - margin - requisition.height;
2154 else if (pos[1] == -2)
2155 *y = margin;
2156 else
2157 *y = pos[1] - (requisition.height >> 2);
2159 *x = CLAMP(*x, 0, screen_width - requisition.width);
2160 *y = CLAMP(*y, 0, screen_height - requisition.height);
2162 *push_in = FALSE;
2165 static void panel_show_menu(GdkEventButton *event, PanelIcon *pi, Panel *panel)
2167 PanelSide side = panel->side;
2168 int pos[3];
2170 pos[0] = event->x_root;
2171 pos[1] = event->y_root;
2172 pos[2] = MENU_MARGIN(side);
2174 icon_prepare_menu((Icon *) pi, FALSE);
2176 if (side == PANEL_LEFT)
2177 pos[0] = -2;
2178 else if (side == PANEL_RIGHT)
2179 pos[0] = -1;
2181 if (side == PANEL_TOP)
2182 pos[1] = -2;
2183 else if (side == PANEL_BOTTOM)
2184 pos[1] = -1;
2186 gtk_menu_popup(GTK_MENU(icon_menu), NULL, NULL,
2187 panel_position_menu,
2188 (gpointer) pos, event->button, event->time);
2191 /* Note: also called from icon handler */
2192 static gboolean panel_drag_motion(GtkWidget *widget,
2193 GdkDragContext *context,
2194 gint x,
2195 gint y,
2196 guint time,
2197 Panel *panel)
2199 int panel_x, panel_y;
2201 gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
2203 motion_may_raise(panel, panel_x, panel_y);
2204 gdk_drag_status(context, 0, time);
2206 return TRUE;
2209 static gboolean insert_drag_motion(GtkWidget *widget,
2210 GdkDragContext *context,
2211 gint x,
2212 gint y,
2213 guint time,
2214 Panel *panel)
2216 int panel_x, panel_y;
2218 gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
2219 motion_may_raise(panel, panel_x, panel_y);
2221 return FALSE;
2224 /* Note: also called from icon handler */
2225 static void panel_drag_leave(GtkWidget *widget,
2226 GdkDragContext *context,
2227 guint32 time,
2228 Panel *panel)
2230 GdkWindow *pinboard, *window;
2231 GtkAllocation *alloc = &panel->window->allocation;
2232 int x, y;
2234 window = panel->window->window;
2235 gdk_window_get_pointer(window, &x, &y, NULL);
2236 if (x < 0 || y < 0 || x > alloc->width || y > alloc->height)
2238 pinboard = pinboard_get_window();
2239 window_put_just_above(panel->window->window, pinboard);
2243 static gboolean panel_check_xinerama(void)
2245 gint old_monitor = panel_monitor;
2247 panel_monitor = -1;
2249 if (o_panel_xinerama.int_value)
2251 if (o_panel_monitor.int_value < n_monitors)
2253 panel_monitor = o_panel_monitor.int_value;
2255 else
2257 g_warning(_("Xinerama monitor %d unavailable"),
2258 o_panel_monitor.int_value);
2262 if (panel_monitor == -1)
2264 panel_geometry.x = panel_geometry.y = 0;
2265 panel_geometry.width = screen_width;
2266 panel_geometry.height = screen_height;
2268 else
2270 panel_geometry = monitor_geom[panel_monitor];
2273 return old_monitor != panel_monitor;
2276 static GList *build_monitor_number(Option *option, xmlNode *node, guchar *label)
2278 GtkObject *adj;
2280 adj = gtk_adjustment_new(MAX(0, panel_monitor),
2281 0, n_monitors - 1, 1, 10, 1);
2282 return build_numentry_base(option, node, label, GTK_ADJUSTMENT(adj));