r3768: Updated years.
[rox-filer.git] / ROX-Filer / src / panel.c
blob506dfe01887c31fc42a02c9d42879b34ea70fee9
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2005, 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(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 static gboolean panel_drag_motion(GtkWidget *widget,
138 GdkDragContext *context,
139 gint x,
140 gint y,
141 guint time,
142 Panel *panel);
143 static gboolean insert_drag_motion(GtkWidget *widget,
144 GdkDragContext *context,
145 gint x,
146 gint y,
147 guint time,
148 Panel *panel);
149 static gboolean drag_motion(GtkWidget *widget,
150 GdkDragContext *context,
151 gint x,
152 gint y,
153 guint time,
154 PanelIcon *pi);
155 static void panel_drag_leave(GtkWidget *widget,
156 GdkDragContext *context,
157 guint32 time,
158 Panel *panel);
159 static void drag_leave(GtkWidget *widget,
160 GdkDragContext *context,
161 guint32 time,
162 Icon *icon);
163 static GtkWidget *make_insert_frame(Panel *panel);
164 static gboolean enter_icon(GtkWidget *widget,
165 GdkEventCrossing *event,
166 Icon *icon);
167 static gint icon_motion_event(GtkWidget *widget,
168 GdkEventMotion *event,
169 PanelIcon *pi);
170 static gint panel_leave_event(GtkWidget *widget,
171 GdkEventCrossing *event,
172 Panel *panel);
173 static gint panel_motion_event(GtkWidget *widget,
174 GdkEventMotion *event,
175 Panel *panel);
176 static void reposition_icon(PanelIcon *pi, int index);
177 static void start_drag(PanelIcon *pi, GdkEventMotion *event);
178 static void drag_end(GtkWidget *widget,
179 GdkDragContext *context,
180 Icon *icon);
181 static void perform_action(Panel *panel,
182 PanelIcon *pi,
183 GdkEventButton *event);
184 static void run_applet(PanelIcon *pi);
185 static void size_request(GtkWidget *widget, GtkRequisition *req, PanelIcon *pi);
186 static void panel_load_from_xml(Panel *panel, xmlDocPtr doc);
187 static gboolean draw_panel_edge(GtkWidget *widget, GdkEventExpose *event,
188 Panel *panel);
189 static PanelIcon *panel_icon_new(Panel *panel,
190 const char *pathname,
191 const char *name);
192 static GType panel_icon_get_type(void);
193 static gboolean panel_want_show_text(PanelIcon *pi);
194 static void panel_show_menu(GdkEventButton *event, PanelIcon *pi, Panel *panel);
195 static void panel_style_changed(void);
196 static void motion_may_raise(Panel *panel, int x, int y);
197 static void panel_update(Panel *panel);
198 static gboolean panel_check_xinerama(void);
199 static GList *build_monitor_number(Option *option,
200 xmlNode *node, guchar *label);
201 static gboolean may_autoscroll(Panel *panel);
204 static GtkWidget *dnd_highlight = NULL; /* (stops flickering) */
206 #define SHOW_BOTH 0
207 #define SHOW_APPS_SMALL 1
208 #define SHOW_ICON 2
209 static Option o_panel_style;
210 static Option o_panel_width;
211 static Option o_panel_xinerama;
212 static Option o_panel_monitor;
213 static Option o_panel_avoid;
214 static Option o_panel_is_dock;
216 static gint panel_monitor = -1;
217 GdkRectangle panel_geometry;
219 static int closing_panel = 0; /* Don't panel_save; destroying! */
221 /****************************************************************
222 * EXTERNAL INTERFACE *
223 ****************************************************************/
225 void panel_init(void)
227 option_add_int(&o_panel_style, "panel_style", SHOW_APPS_SMALL);
228 option_add_int(&o_panel_width, "panel_width", 52);
230 option_add_int(&o_panel_xinerama, "panel_xinerama", 0);
231 option_add_int(&o_panel_monitor, "panel_monitor", 0);
233 option_add_int(&o_panel_avoid, "panel_avoid", TRUE);
234 option_add_int(&o_panel_is_dock, "panel_is_dock", FALSE);
236 option_add_notify(panel_style_changed);
238 option_register_widget("monitor-number", build_monitor_number);
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 if (o_panel_is_dock.int_value)
363 gtk_window_set_type_hint(GTK_WINDOW(panel->window),
364 GDK_WINDOW_TYPE_HINT_DOCK);
366 gtk_widget_realize(panel->window);
367 make_panel_window(panel->window);
368 gtk_window_stick(GTK_WINDOW(panel->window));
370 gtk_widget_show_all(align);
372 loading_panel = panel;
373 if (load_path && access(load_path, F_OK) == 0)
375 xmlDocPtr doc;
376 doc = xmlParseFile(load_path);
377 if (doc)
379 panel_load_from_xml(panel, doc);
380 xmlFreeDoc(doc);
382 else
384 parse_file(load_path, pan_from_file);
385 info_message(_("Your old panel file has been "
386 "converted to the new XML format."));
387 panel_save(panel);
390 else
392 /* Don't scare users with an empty panel... */
393 guchar *apps;
395 panel_add_item(panel, "~", "Home", FALSE, NULL, NULL);
397 apps = pathdup(make_path(app_dir, ".."));
398 if (apps)
400 panel_add_item(panel, apps, "Apps", FALSE, NULL, NULL);
401 g_free(apps);
404 loading_panel = NULL;
405 g_free(load_path);
407 current_panel[side] = panel;
409 gtk_widget_queue_resize(box);
410 g_signal_connect(panel->window, "size-request",
411 G_CALLBACK(panel_post_resize), panel);
412 g_signal_connect(panel->window, "size-allocate",
413 G_CALLBACK(reposition_panel), panel);
415 number_of_windows++;
416 gdk_window_lower(panel->window->window);
417 gtk_widget_show(panel->window);
418 /* This has no effect until after window is showing; GTK+ bug? */
419 keep_below(panel->window->window, TRUE);
422 GdkWindow *pinboard;
424 pinboard = pinboard_get_window();
425 /* (if pinboard is NULL, will go right to the back) */
426 window_put_just_above(panel->window->window, pinboard);
429 return panel;
432 /* Externally visible function to add an item to a panel */
433 gboolean panel_add(PanelSide side,
434 const gchar *path, const gchar *label, gboolean after)
436 g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, FALSE);
438 g_return_val_if_fail(current_panel[side] != NULL, FALSE);
440 panel_add_item(current_panel[side], path, label, after, NULL, NULL);
442 return TRUE;
445 /* Add the area covered by the panels to the region */
446 void panel_mark_used(GdkRegion *used)
448 int i;
450 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
452 Panel *panel = current_panel[i];
453 GdkRectangle rect;
455 if (!panel)
456 continue;
458 gdk_window_get_root_origin(panel->window->window,
459 &rect.x, &rect.y);
460 rect.width = panel->window->allocation.width;
461 rect.height = panel->window->allocation.height;
463 gdk_region_union_with_rect(used, &rect);
467 /* On xrandr screen size changes, update all panels */
468 void panel_update_size(void)
470 int i;
472 panel_check_xinerama();
474 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
476 if (current_panel[i])
478 reposition_panel(current_panel[i]->window,
479 &current_panel[i]->window->allocation,
480 current_panel[i]);
481 gtk_widget_queue_resize(current_panel[i]->window);
486 /****************************************************************
487 * INTERNAL FUNCTIONS *
488 ****************************************************************/
490 /* User has tried to close the panel via the window manager - confirm */
491 static int panel_delete(GtkWidget *widget, GdkEvent *event, Panel *panel)
493 return !confirm(_("You have tried to close a panel via the window "
494 "manager - I usually find that this is accidental... "
495 "really close?"),
496 GTK_STOCK_CLOSE, NULL);
499 static void panel_destroyed(GtkWidget *widget, Panel *panel)
501 if (current_panel[panel->side] == panel)
502 current_panel[panel->side] = NULL;
504 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
506 if (current_panel[PANEL_RIGHT])
507 gtk_widget_queue_resize(
508 current_panel[PANEL_RIGHT]->window);
509 if (current_panel[PANEL_LEFT])
510 gtk_widget_queue_resize(
511 current_panel[PANEL_LEFT]->window);
514 if (panel->autoscroll_speed)
515 g_source_remove(panel->autoscroll_to);
517 g_free(panel->name);
518 g_free(panel);
520 one_less_window();
523 static void panel_load_side(Panel *panel, xmlNodePtr side, gboolean after)
525 xmlNodePtr node;
526 char *label, *path, *shortcut, *args;
528 for (node = side->xmlChildrenNode; node; node = node->next)
530 if (node->type != XML_ELEMENT_NODE)
531 continue;
532 if (strcmp(node->name, "icon") != 0)
533 continue;
535 label = xmlGetProp(node, "label");
536 if (!label)
537 label = g_strdup("<missing label>");
538 path = xmlNodeGetContent(node);
539 if (!path)
540 path = g_strdup("<missing path>");
541 shortcut = xmlGetProp(node, "shortcut");
542 args = xmlGetProp(node, "args");
544 panel_add_item(panel, path, label, after, shortcut, args);
546 g_free(path);
547 g_free(label);
548 g_free(shortcut);
549 g_free(args);
553 /* Create one panel icon for each icon in the doc */
554 static void panel_load_from_xml(Panel *panel, xmlDocPtr doc)
556 xmlNodePtr root;
558 root = xmlDocGetRootElement(doc);
559 panel_load_side(panel, get_subnode(root, NULL, "start"), FALSE);
560 panel_load_side(panel, get_subnode(root, NULL, "end"), TRUE);
563 /* Called for each line in the config file while loading a new panel */
564 static const char *pan_from_file(gchar *line)
566 gchar *sep, *leaf;
568 g_return_val_if_fail(line != NULL, NULL);
569 g_return_val_if_fail(loading_panel != NULL, NULL);
571 if (*line == '\0')
572 return NULL;
574 sep = strpbrk(line, "<>");
575 if (!sep)
576 return _("Missing < or > in panel config file");
578 if (sep != line)
579 leaf = g_strndup(line, sep - line);
580 else
581 leaf = NULL;
583 panel_add_item(loading_panel, sep + 1, leaf, sep[0] == '>',
584 NULL, NULL);
586 g_free(leaf);
588 return NULL;
591 static gboolean icon_pointer_in(GtkWidget *widget,
592 GdkEventCrossing *event,
593 Icon *icon)
595 gtk_widget_set_state(widget,
596 icon->selected ? GTK_STATE_SELECTED : GTK_STATE_PRELIGHT);
598 return 0;
601 static gboolean icon_pointer_out(GtkWidget *widget,
602 GdkEventCrossing *event,
603 Icon *icon)
605 gtk_widget_set_state(widget,
606 icon->selected ? GTK_STATE_SELECTED : GTK_STATE_NORMAL);
608 return 0;
611 static void panel_icon_destroyed(PanelIcon *pi)
613 g_return_if_fail(pi->widget != NULL);
615 pi->widget = NULL;
617 g_object_unref(pi);
620 /* Set the tooltip AND hide/show the label */
621 static void panel_icon_set_tip(PanelIcon *pi)
623 XMLwrapper *ai;
624 xmlNode *node;
625 Icon *icon = (Icon *) pi;
627 g_return_if_fail(pi != NULL);
629 if (pi->label)
631 if (panel_want_show_text(pi))
632 gtk_widget_show(pi->label);
633 else
634 gtk_widget_hide(pi->label);
637 if (pi->socket)
638 ai = NULL;
639 else
640 ai = appinfo_get(icon->path, icon->item);
642 if (ai && ((node = xml_get_section(ai, NULL, "Summary"))))
644 guchar *str;
645 str = xmlNodeListGetString(node->doc,
646 node->xmlChildrenNode, 1);
647 if (str)
649 gtk_tooltips_set_tip(tooltips, pi->widget, str, NULL);
650 g_free(str);
653 else if ((!panel_want_show_text(pi)) && !pi->socket)
655 if (icon->item->leafname && icon->item->leafname[0])
656 gtk_tooltips_set_tip(tooltips, pi->widget,
657 icon->item->leafname, NULL);
659 else
660 gtk_tooltips_set_tip(tooltips, pi->widget, NULL, NULL);
662 if (ai)
663 g_object_unref(ai);
666 /* Add an icon with this path to the panel. If after is TRUE then the
667 * icon is added to the right/bottom end of the panel.
669 * If name is NULL a suitable name is taken from path.
671 static void panel_add_item(Panel *panel,
672 const gchar *path,
673 const gchar *name,
674 gboolean after,
675 const gchar *shortcut,
676 const gchar *args)
678 GtkWidget *widget;
679 PanelIcon *pi;
680 Icon *icon;
682 g_return_if_fail(panel != NULL);
683 g_return_if_fail(path != NULL);
685 widget = gtk_event_box_new();
686 gtk_widget_set_events(widget,
687 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
688 GDK_BUTTON3_MOTION_MASK |
689 GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
690 GDK_BUTTON_RELEASE_MASK);
692 gtk_box_pack_start(GTK_BOX(after ? panel->after : panel->before),
693 widget, FALSE, TRUE, 0);
694 if (after)
695 gtk_box_reorder_child(GTK_BOX(panel->after), widget, 0);
697 gtk_widget_realize(widget);
699 pi = panel_icon_new(panel, path, name);
700 icon = (Icon *) pi;
702 /* Widget takes the initial ref of Icon */
703 g_object_set_data(G_OBJECT(widget), "icon", pi);
705 pi->widget = widget;
706 g_object_ref(widget);
708 gtk_widget_set_name(pi->widget, "panel-icon");
710 g_signal_connect_swapped(widget, "destroy",
711 G_CALLBACK(panel_icon_destroyed), pi);
713 if (icon->item->base_type == TYPE_DIRECTORY)
714 run_applet(pi);
716 g_signal_connect(widget, "button_release_event",
717 G_CALLBACK(icon_button_release), pi);
718 g_signal_connect(widget, "button_press_event",
719 G_CALLBACK(icon_button_press), pi);
720 g_signal_connect(widget, "motion-notify-event",
721 G_CALLBACK(icon_motion_event), pi);
722 g_signal_connect(widget, "enter-notify-event",
723 G_CALLBACK(icon_pointer_in), pi);
724 g_signal_connect(widget, "leave-notify-event",
725 G_CALLBACK(icon_pointer_out), pi);
727 if (!pi->socket)
729 g_signal_connect(widget, "enter-notify-event",
730 G_CALLBACK(enter_icon), pi);
731 g_signal_connect_after(widget, "expose_event",
732 G_CALLBACK(expose_icon), pi);
733 g_signal_connect(widget, "drag_data_get",
734 G_CALLBACK(drag_data_get), NULL);
736 g_signal_connect(widget, "size_request",
737 G_CALLBACK(size_request), pi);
739 drag_set_panel_dest(pi);
741 pi->label = gtk_label_new(icon->item->leafname);
742 gtk_container_add(GTK_CONTAINER(pi->widget), pi->label);
743 gtk_misc_set_alignment(GTK_MISC(pi->label), 0.5, 1);
744 gtk_misc_set_padding(GTK_MISC(pi->label), 1, 2);
747 icon_set_shortcut(icon, shortcut);
748 icon_set_arguments(icon, args);
750 if (!loading_panel)
751 panel_save(panel);
753 panel_icon_set_tip(pi);
754 gtk_widget_show(widget);
757 static gboolean remove_item_from_side(GtkWidget *container, const gchar *path,
758 const gchar *label)
760 GList *kids, *next;
761 gboolean found = FALSE;
763 kids = gtk_container_get_children(GTK_CONTAINER(container));
765 for (next = kids; next; next = next->next)
767 Icon *icon;
768 icon = g_object_get_data(G_OBJECT(next->data), "icon");
769 if (!icon)
770 continue;
772 if (strcmp(path, icon->src_path) == 0 &&
773 (!label || strcmp(label, icon->item->leafname)==0))
775 icon_destroy(icon);
776 found = TRUE;
777 break;
781 g_list_free(kids);
783 return found;
786 /* Remove an item with this path. If more than one item matches, only
787 * one is removed. If label is not NULL then it must also match the item.
788 * Returns TRUE if an item was successfully removed.
790 gboolean panel_remove_item(PanelSide side, const gchar *path,
791 const gchar *label)
793 Panel *panel;
795 g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, FALSE);
796 g_return_val_if_fail(path != NULL, FALSE);
798 panel = current_panel[side];
799 if (!panel)
801 g_warning("No panel on this side of the screen!");
802 return FALSE;
805 if (remove_item_from_side(panel->before, path, label) ||
806 remove_item_from_side(panel->after, path, label))
808 panel_save(panel);
809 panel_update(panel);
810 return TRUE;
814 g_warning("Panel item '%s' not found", path);
815 return FALSE;
818 /* Called when Gtk+ wants to know how much space an icon needs.
819 * 'req' is already big enough for the label, if shown.
821 static void size_request(GtkWidget *widget, GtkRequisition *req, PanelIcon *pi)
823 Icon *icon = (Icon *) pi;
824 gboolean horz = (pi->panel->side == PANEL_TOP ||
825 pi->panel->side == PANEL_BOTTOM);
826 int max_width = 100;
827 int max_height = 100;
828 int image_width, image_height;
830 if (horz)
831 max_height = o_panel_width.int_value - req->height;
832 else
833 max_width = MAX(o_panel_width.int_value, req->width);
835 /* TODO: really need to recreate? */
836 if (pi->image)
837 g_object_unref(pi->image);
839 pi->image = scale_pixbuf(di_image(icon->item)->src_pixbuf,
840 MAX(20, max_width), MAX(20, max_height));
842 image_width = gdk_pixbuf_get_width(pi->image);
843 image_height = gdk_pixbuf_get_height(pi->image);
845 if (req->height > 0 && max_height < req->height)
847 pi->style = TEXT_BESIDE_ICON;
848 req->width += image_width;
849 req->height = MAX(req->height, image_height);
850 gtk_misc_set_alignment(GTK_MISC(pi->label), 1, 0.5);
852 else
854 pi->style = TEXT_UNDER_ICON;
855 req->width = MAX(req->width, image_width);
856 req->height += image_height;
857 gtk_misc_set_alignment(GTK_MISC(pi->label), 0.5, 1);
860 if (horz)
861 req->width += PANEL_ICON_SPACING;
862 else
863 req->height += PANEL_ICON_SPACING;
866 static gint expose_icon(GtkWidget *widget,
867 GdkEventExpose *event,
868 PanelIcon *pi)
870 return draw_icon(widget, &event->area, pi);
873 static gint draw_icon(GtkWidget *widget, GdkRectangle *badarea, PanelIcon *pi)
875 GdkRectangle area;
876 int width, height;
877 Icon *icon = (Icon *) pi;
878 int image_x;
879 int image_y;
880 GdkPixbuf *image;
881 int text_height = 0;
883 gdk_drawable_get_size(widget->window, &area.width, &area.height);
885 if (panel_want_show_text(pi))
886 text_height = pi->label->requisition.height;
888 g_return_val_if_fail(pi->image != NULL, FALSE);
890 image = pi->image;
892 width = gdk_pixbuf_get_width(image);
893 height = gdk_pixbuf_get_height(image);
895 if (pi->style == TEXT_UNDER_ICON)
897 image_x = (area.width - width) >> 1;
898 image_y = (area.height - height - text_height) >> 1;
900 else
902 image_x = PANEL_ICON_SPACING - 2;
903 image_y = (area.height - height) >> 1;
906 gdk_pixbuf_render_to_drawable_alpha(
907 image,
908 widget->window,
909 0, 0, /* src */
910 image_x, image_y, /* dest */
911 width, height,
912 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
913 GDK_RGB_DITHER_NORMAL, 0, 0);
915 if (icon->item->flags & ITEM_FLAG_SYMLINK)
917 gdk_pixbuf_render_to_drawable_alpha(im_symlink->pixbuf,
918 widget->window,
919 0, 0, /* src */
920 image_x, image_y + 2, /* dest */
921 -1, -1,
922 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
923 GDK_RGB_DITHER_NORMAL, 0, 0);
925 if (icon->item->flags & ITEM_FLAG_MOUNT_POINT)
927 MaskedPixmap *mp = icon->item->flags & ITEM_FLAG_MOUNTED
928 ? im_mounted
929 : im_unmounted;
931 gdk_pixbuf_render_to_drawable_alpha(mp->pixbuf,
932 widget->window,
933 0, 0, /* src */
934 image_x, image_y + 2, /* dest */
935 -1, -1,
936 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
937 GDK_RGB_DITHER_NORMAL, 0, 0);
939 return FALSE;
942 static void panel_icon_wink(Icon *icon)
944 PanelIcon *pi = (PanelIcon *) icon;
946 wink_widget(pi->widget);
949 /* icon may be NULL if the event is on the background */
950 static void perform_action(Panel *panel, PanelIcon *pi, GdkEventButton *event)
952 BindAction action;
953 Icon *icon = (Icon *) pi;
955 action = bind_lookup_bev(icon ? BIND_PANEL_ICON : BIND_PANEL, event);
957 if (pi && pi->socket)
958 if (action != ACT_POPUP_MENU && action != ACT_MOVE_ICON)
959 return;
961 switch (action)
963 case ACT_OPEN_ITEM:
964 dnd_motion_ungrab();
965 wink_widget(pi->widget);
966 icon_run(icon);
967 break;
968 case ACT_EDIT_ITEM:
969 dnd_motion_ungrab();
970 wink_widget(pi->widget);
971 run_diritem(icon->path, icon->item, NULL, NULL, TRUE);
972 break;
973 case ACT_POPUP_MENU:
974 dnd_motion_ungrab();
975 panel_show_menu(event, pi, panel);
976 break;
977 case ACT_MOVE_ICON:
978 dnd_motion_start(MOTION_REPOSITION);
979 break;
980 case ACT_PRIME_AND_SELECT:
981 if (!icon->selected)
982 icon_select_only(icon);
983 dnd_motion_start(MOTION_READY_FOR_DND);
984 break;
985 case ACT_PRIME_AND_TOGGLE:
986 icon_set_selected(icon, !icon->selected);
987 dnd_motion_start(MOTION_READY_FOR_DND);
988 break;
989 case ACT_PRIME_FOR_DND:
990 dnd_motion_start(MOTION_READY_FOR_DND);
991 break;
992 case ACT_TOGGLE_SELECTED:
993 icon_set_selected(icon, !icon->selected);
994 break;
995 case ACT_SELECT_EXCL:
996 icon_set_selected(icon, TRUE);
997 break;
998 case ACT_IGNORE:
999 break;
1000 case ACT_CLEAR_SELECTION:
1001 dnd_motion_ungrab();
1002 icon_select_only(NULL);
1003 break;
1004 default:
1005 g_warning("Unsupported action : %d\n", action);
1006 break;
1010 static gint panel_button_release(GtkWidget *widget,
1011 GdkEventButton *event,
1012 Panel *panel)
1014 if (dnd_motion_release(event))
1015 return TRUE;
1017 perform_action(panel, NULL, event);
1019 return TRUE;
1022 static gint panel_button_press(GtkWidget *widget,
1023 GdkEventButton *event,
1024 Panel *panel)
1026 if (dnd_motion_press(panel->window, event))
1027 perform_action(panel, NULL, event);
1029 return TRUE;
1032 static gint icon_button_release(GtkWidget *widget,
1033 GdkEventButton *event,
1034 PanelIcon *pi)
1036 if (pi->socket && event->button == 1)
1037 return FALSE; /* Restart button */
1039 if (dnd_motion_release(event))
1040 return TRUE;
1042 perform_action(pi->panel, pi, event);
1044 return TRUE;
1047 static gint icon_button_press(GtkWidget *widget,
1048 GdkEventButton *event,
1049 PanelIcon *pi)
1051 if (pi->socket && event->button == 1)
1052 return FALSE; /* Restart button */
1054 if (dnd_motion_press(widget, event))
1055 perform_action(pi->panel, pi, event);
1057 return TRUE;
1060 static void reposition_panel(GtkWidget *window,
1061 GtkAllocation *alloc, Panel *panel)
1063 int x = panel_geometry.x;
1064 int y = panel_geometry.y;
1065 int thickness;
1066 PanelSide side = panel->side;
1068 if (side == PANEL_LEFT || side == PANEL_RIGHT)
1070 if (side == PANEL_RIGHT)
1071 x += panel_geometry.width - alloc->width;
1073 if (current_panel[PANEL_TOP])
1075 GtkWidget *win = current_panel[PANEL_TOP]->window;
1076 y += win->allocation.height;
1080 if (side == PANEL_BOTTOM)
1081 y += panel_geometry.height - alloc->height;
1083 gtk_window_move(GTK_WINDOW(panel->window), x, y);
1084 gdk_window_move(panel->window->window, x, y);
1086 if (side == PANEL_BOTTOM || side == PANEL_TOP)
1088 if (current_panel[PANEL_RIGHT])
1089 gtk_widget_queue_resize(
1090 current_panel[PANEL_RIGHT]->window);
1091 if (current_panel[PANEL_LEFT])
1092 gtk_widget_queue_resize(
1093 current_panel[PANEL_LEFT]->window);
1096 /* Stop windows from maximising over all/part of us */
1098 struct {
1099 guint32 left, right, top, bottom;
1100 guint32 left_start_y, left_end_y;
1101 guint32 right_start_y, right_end_y;
1102 guint32 top_start_x, top_end_x;
1103 guint32 bottom_start_x, bottom_end_x;
1104 } strut = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1106 if (o_panel_avoid.int_value == FALSE)
1107 thickness = 2;
1108 else if (panel->side == PANEL_TOP ||
1109 panel->side == PANEL_BOTTOM)
1110 thickness = alloc->height;
1111 else
1112 thickness = alloc->width;
1114 switch (panel->side)
1116 case PANEL_LEFT:
1117 if (!o_panel_xinerama.int_value ||
1118 !monitor_adjacent
1119 [o_panel_monitor.int_value].left)
1121 strut.left = panel_geometry.x +
1122 thickness;
1123 strut.left_start_y = panel_geometry.y;
1124 strut.left_end_y = panel_geometry.y +
1125 panel_geometry.height - 1;
1127 /* else there is (part of) a monitor
1128 * to the left */
1129 else
1131 thickness = 0;
1133 break;
1134 case PANEL_RIGHT:
1135 if (!o_panel_xinerama.int_value ||
1136 !monitor_adjacent
1137 [o_panel_monitor.int_value].right)
1139 /* RHS of monitor might not abut edge
1140 * of total virtual screen */
1141 strut.right = screen_width -
1142 panel_geometry.x -
1143 panel_geometry.width +
1144 thickness;
1145 strut.right_start_y = panel_geometry.y;
1146 strut.right_end_y = panel_geometry.y +
1147 panel_geometry.height - 1;
1149 /* else there is (part of) a monitor
1150 * to the right */
1151 else
1153 thickness = 0;
1155 break;
1156 case PANEL_TOP:
1157 if (!o_panel_xinerama.int_value ||
1158 !monitor_adjacent
1159 [o_panel_monitor.int_value].top)
1161 strut.top = panel_geometry.y +
1162 thickness;
1163 strut.top_start_x = panel_geometry.x;
1164 strut.top_end_x = panel_geometry.x +
1165 panel_geometry.width - 1;
1167 /* else there is (part of) a monitor above */
1168 else
1170 thickness = 0;
1172 break;
1173 default: /* PANEL_BOTTOM */
1174 if (!o_panel_xinerama.int_value ||
1175 !monitor_adjacent
1176 [o_panel_monitor.int_value].bottom)
1178 /* Bottom of monitor might not abut
1179 * edge of total virtual screen */
1180 strut.bottom = screen_height -
1181 panel_geometry.y -
1182 panel_geometry.height +
1183 thickness;
1184 strut.bottom_start_x = panel_geometry.x;
1185 strut.bottom_end_x = panel_geometry.x +
1186 panel_geometry.width - 1;
1188 /* else there is (part of) a monitor below */
1189 else
1191 thickness = 0;
1193 break;
1196 if (thickness)
1198 /* Set full-width strut as well as partial in case
1199 * partial isn't supported by wm */
1200 gdk_property_change(panel->window->window,
1201 gdk_atom_intern("_NET_WM_STRUT",
1202 FALSE),
1203 gdk_atom_intern("CARDINAL", FALSE),
1204 32, GDK_PROP_MODE_REPLACE,
1205 (gchar *) &strut, 4);
1206 gdk_property_change(panel->window->window,
1207 gdk_atom_intern("_NET_WM_STRUT_PARTIAL",
1208 FALSE),
1209 gdk_atom_intern("CARDINAL", FALSE),
1210 32, GDK_PROP_MODE_REPLACE,
1211 (gchar *) &strut, 12);
1213 else
1215 gdk_property_delete(panel->window->window,
1216 gdk_atom_intern("_NET_WM_STRUT_PARTIAL",
1217 FALSE));
1218 gdk_property_delete(panel->window->window,
1219 gdk_atom_intern("_NET_WM_STRUT",
1220 FALSE));
1226 /* Same as drag_set_dest(), but for panel icons */
1227 static void drag_set_panel_dest(PanelIcon *pi)
1229 GtkWidget *obj = pi->widget;
1231 make_drop_target(pi->widget, 0);
1233 g_signal_connect(obj, "drag_motion", G_CALLBACK(drag_motion), pi);
1234 g_signal_connect(obj, "drag_leave", G_CALLBACK(drag_leave), pi);
1235 g_signal_connect(obj, "drag_end", G_CALLBACK(drag_end), pi);
1238 static gboolean drag_motion(GtkWidget *widget,
1239 GdkDragContext *context,
1240 gint x,
1241 gint y,
1242 guint time,
1243 PanelIcon *pi)
1245 GdkDragAction action = context->suggested_action;
1246 const char *type = NULL;
1247 Icon *icon = (Icon *) pi;
1248 DirItem *item = icon->item;
1249 int panel_x, panel_y;
1251 gdk_window_get_pointer(pi->panel->window->window,
1252 &panel_x, &panel_y, NULL);
1253 motion_may_raise(pi->panel, panel_x, panel_y);
1255 /* Should we scroll the panel when dragging? */
1256 if (motion_state != MOTION_REPOSITION)
1257 if (pi->panel->autoscroll_speed == 0)
1258 may_autoscroll(pi->panel);
1260 if (icon->selected)
1261 goto out; /* Can't drag a selection to itself */
1263 type = dnd_motion_item(context, &item);
1265 if ((context->actions & GDK_ACTION_ASK) && o_dnd_left_menu.int_value
1266 && type != drop_dest_prog)
1268 guint state;
1269 gdk_window_get_pointer(NULL, NULL, NULL, &state);
1270 if (state & GDK_BUTTON1_MASK)
1271 action = GDK_ACTION_ASK;
1274 if (!item)
1275 type = NULL;
1276 out:
1277 /* We actually must pretend to accept the drop, even if the
1278 * directory isn't writeable, so that the spring-opening
1279 * thing works.
1282 /* Don't allow drops to non-writeable directories */
1283 if (o_dnd_spring_open.int_value == FALSE &&
1284 type == drop_dest_dir &&
1285 access(icon->path, W_OK) != 0)
1287 type = NULL;
1290 g_dataset_set_data(context, "drop_dest_type", (gpointer) type);
1291 if (type)
1293 gdk_drag_status(context, action, time);
1294 g_dataset_set_data_full(context, "drop_dest_path",
1295 g_strdup(icon->path), g_free);
1296 if (type == drop_dest_dir)
1297 dnd_spring_load(context, NULL);
1299 if (dnd_highlight && dnd_highlight != pi->widget)
1301 gtk_drag_unhighlight(dnd_highlight);
1302 dnd_highlight = NULL;
1305 if (dnd_highlight == NULL)
1307 gtk_drag_highlight(pi->widget);
1308 dnd_highlight = pi->widget;
1312 return type != NULL;
1316 static void add_uri_list(GtkWidget *widget,
1317 GdkDragContext *context,
1318 gint x,
1319 gint y,
1320 GtkSelectionData *selection_data,
1321 guint info,
1322 guint32 time,
1323 Panel *panel)
1325 gboolean after = FALSE;
1326 GList *uris, *next;
1328 if (!selection_data->data)
1329 return;
1331 g_return_if_fail(selection_data->data[selection_data->length] == '\0');
1333 if (g_object_get_data(G_OBJECT(widget), "after"))
1334 after = TRUE;
1336 uris = uri_list_to_glist(selection_data->data);
1338 for (next = uris; next; next = next->next)
1340 guchar *path;
1342 path = get_local_path((EscapedPath *) next->data);
1344 if (path) {
1345 panel_add_item(panel, path, NULL, after, NULL, NULL);
1346 g_free(path);
1350 g_list_free(uris);
1353 static void drag_end(GtkWidget *widget,
1354 GdkDragContext *context,
1355 Icon *icon)
1357 if (tmp_icon_selected)
1359 icon_select_only(NULL);
1360 tmp_icon_selected = FALSE;
1364 static void drag_leave(GtkWidget *widget,
1365 GdkDragContext *context,
1366 guint32 time,
1367 Icon *icon)
1369 panel_drag_leave(widget, context, time, ((PanelIcon *) icon)->panel);
1371 if (dnd_highlight && dnd_highlight == widget)
1373 gtk_drag_unhighlight(dnd_highlight);
1374 dnd_highlight = NULL;
1377 dnd_spring_abort();
1380 /* Create XML icon nodes for these widgets.
1381 * Always frees the widgets list.
1383 static void make_widgets(xmlNodePtr side, GList *widgets)
1385 GList *next;
1387 for (next = widgets; next; next = next->next)
1389 Icon *icon;
1390 xmlNodePtr tree;
1392 icon = g_object_get_data(G_OBJECT(next->data), "icon");
1394 if (!icon)
1396 g_warning("Can't find Icon from widget\n");
1397 continue;
1400 tree = xmlNewTextChild(side, NULL, "icon", icon->src_path);
1402 xmlSetProp(tree, "label", icon->item->leafname);
1403 if (icon->shortcut)
1404 xmlSetProp(tree, "shortcut", icon->shortcut);
1405 if (icon->args)
1406 xmlSetProp(tree, "args", icon->args);
1409 if (widgets)
1410 g_list_free(widgets);
1413 void panel_save(Panel *panel)
1415 xmlDocPtr doc;
1416 xmlNodePtr root;
1417 guchar *save = NULL;
1418 guchar *save_new = NULL;
1420 g_return_if_fail(panel != NULL);
1422 if (strchr(panel->name, '/'))
1423 save = g_strdup(panel->name);
1424 else
1426 guchar *leaf;
1428 leaf = g_strconcat("pan_", panel->name, NULL);
1429 save = choices_find_path_save(leaf, PROJECT, TRUE);
1430 g_free(leaf);
1433 if (!save)
1434 return;
1436 doc = xmlNewDoc("1.0");
1437 xmlDocSetRootElement(doc, xmlNewDocNode(doc, NULL, "panel", NULL));
1439 root = xmlDocGetRootElement(doc);
1440 make_widgets(xmlNewChild(root, NULL, "start", NULL),
1441 gtk_container_get_children(GTK_CONTAINER(panel->before)));
1443 make_widgets(xmlNewChild(root, NULL, "end", NULL),
1444 g_list_reverse(gtk_container_get_children(
1445 GTK_CONTAINER(panel->after))));
1447 save_new = g_strconcat(save, ".new", NULL);
1448 if (save_xml_file(doc, save_new) || rename(save_new, save))
1449 delayed_error(_("Error saving panel %s: %s"),
1450 save, g_strerror(errno));
1451 g_free(save_new);
1453 g_free(save);
1454 if (doc)
1455 xmlFreeDoc(doc);
1458 /* Create a frame widget which can be used to add icons to the panel */
1459 static GtkWidget *make_insert_frame(Panel *panel)
1461 GtkWidget *frame;
1462 GtkTargetEntry target_table[] = {
1463 {"text/uri-list", 0, TARGET_URI_LIST},
1466 frame = gtk_frame_new(NULL);
1467 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
1468 gtk_widget_set_size_request(frame, 16, 16);
1470 g_signal_connect(frame, "drag-motion",
1471 G_CALLBACK(insert_drag_motion), panel);
1472 g_signal_connect(frame, "drag-leave",
1473 G_CALLBACK(panel_drag_leave), panel);
1475 g_signal_connect(frame, "drag-data-received",
1476 G_CALLBACK(add_uri_list), panel);
1477 gtk_drag_dest_set(frame,
1478 GTK_DEST_DEFAULT_ALL,
1479 target_table,
1480 sizeof(target_table) / sizeof(*target_table),
1481 GDK_ACTION_COPY);
1483 return frame;
1486 static gboolean enter_icon(GtkWidget *widget,
1487 GdkEventCrossing *event,
1488 Icon *icon)
1490 icon_may_update(icon);
1491 panel_icon_set_tip((PanelIcon *) icon);
1493 return FALSE;
1496 static gint panel_leave_event(GtkWidget *widget,
1497 GdkEventCrossing *event,
1498 Panel *panel)
1500 GdkWindow *pinboard;
1502 if (event->mode != GDK_CROSSING_NORMAL)
1503 return FALSE; /* Grab for menu, DnD, etc */
1505 keep_below(panel->window->window, TRUE);
1507 /* Shouldn't need this as well as keep_below but some WMs don't
1508 * automatically lower as soon as the hint is set */
1509 pinboard = pinboard_get_window();
1510 window_put_just_above(panel->window->window, pinboard);
1512 return FALSE;
1515 /* If (x, y) is at the edge of the panel then raise */
1516 static void motion_may_raise(Panel *panel, int x, int y)
1518 gboolean raise;
1520 if (panel->side == PANEL_TOP)
1521 raise = y == 0;
1522 else if (panel->side == PANEL_BOTTOM)
1523 raise = y == panel->window->allocation.height - 1;
1524 else if (panel->side == PANEL_LEFT)
1525 raise = x == 0;
1526 else
1527 raise = x == panel->window->allocation.width - 1;
1529 if (raise)
1531 keep_below(panel->window->window, FALSE);
1533 /* Shouldn't need this as well as keep_below but some WMs don't
1534 * automatically raise as soon as the hint is set */
1535 gdk_window_raise(panel->window->window);
1539 static gboolean may_autoscroll(Panel *panel)
1541 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1542 gint max, panel_x, panel_y, delta, new;
1544 if (panel->adj->upper <= panel->adj->page_size)
1545 goto stop_scrolling; /* Can see everything already */
1547 gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
1549 if (horz)
1551 delta = panel_x;
1552 max = panel->window->allocation.width;
1553 if (panel_y < 0 || panel_y > panel->window->allocation.height)
1554 goto stop_scrolling; /* Not over the panel */
1556 else
1558 delta = panel_y;
1559 max = panel->window->allocation.height;
1560 if (panel_x < 0 || panel_x > panel->window->allocation.width)
1561 goto stop_scrolling; /* Not over the panel */
1564 if (delta >= 20 && delta <= max - 20)
1565 goto stop_scrolling; /* Not at either end */
1567 panel->autoscroll_speed = MIN(panel->autoscroll_speed + 2, 200);
1569 new = panel->adj->value - ((delta < 20) ? panel->autoscroll_speed
1570 : -panel->autoscroll_speed);
1571 new = CLAMP(new, 0, panel->adj->upper - panel->adj->page_size);
1572 gtk_adjustment_set_value(panel->adj, new);
1574 panel->autoscroll_to = g_timeout_add(40,
1575 (GSourceFunc) may_autoscroll, panel);
1577 return FALSE;
1579 stop_scrolling:
1580 panel->autoscroll_speed = 0;
1581 return FALSE;
1584 static gint panel_motion_event(GtkWidget *widget,
1585 GdkEventMotion *event,
1586 Panel *panel)
1588 motion_may_raise(panel, event->x, event->y);
1590 if (motion_state != MOTION_REPOSITION)
1591 if (panel->autoscroll_speed == 0)
1592 may_autoscroll(panel);
1594 return FALSE;
1597 static gint icon_motion_event(GtkWidget *widget,
1598 GdkEventMotion *event,
1599 PanelIcon *pi)
1601 Panel *panel = pi->panel;
1602 GList *list, *me;
1603 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1604 int val;
1605 int dir = 0;
1607 if (motion_state == MOTION_READY_FOR_DND)
1609 if (dnd_motion_moved(event))
1610 start_drag(pi, event);
1611 return TRUE;
1613 else if (motion_state != MOTION_REPOSITION)
1614 return FALSE;
1616 list = gtk_container_get_children(GTK_CONTAINER(panel->before));
1617 list = g_list_append(list, NULL); /* The gap in the middle */
1618 list = g_list_concat(list,
1619 gtk_container_get_children(GTK_CONTAINER(panel->after)));
1620 me = g_list_find(list, widget);
1622 g_return_val_if_fail(me != NULL, TRUE);
1624 val = horz ? event->x_root : event->y_root;
1626 if (me->prev)
1628 GtkWidget *prev;
1629 int x, y;
1631 if (me->prev->data)
1632 prev = GTK_WIDGET(me->prev->data);
1633 else
1634 prev = panel->gap;
1636 gdk_window_get_origin(prev->window, &x, &y);
1638 if (val <= (horz ? x : y))
1639 dir = -1;
1642 if (dir == 0 && me->next)
1644 GtkWidget *next;
1645 int x, y, w, h;
1647 if (me->next->data)
1648 next = GTK_WIDGET(me->next->data);
1649 else
1650 next = panel->gap;
1652 gdk_window_get_origin(next->window, &x, &y);
1654 gdk_drawable_get_size(next->window, &w, &h);
1656 x += w;
1657 y += h;
1659 if (val >= (horz ? x : y)-1)
1661 if (next == panel->gap)
1662 dir = +2;
1663 else
1664 dir = +1;
1668 if (dir)
1669 reposition_icon(pi, g_list_index(list, widget) + dir);
1671 return TRUE;
1674 static void reposition_icon_on_side(GtkWidget *side, GtkWidget *widget,
1675 int index)
1677 GList *list;
1679 list = gtk_container_get_children(GTK_CONTAINER(side));
1681 /* Want to move icon to the list in the given 'side'. Is it there
1682 * already?
1685 if (!g_list_find(list, widget))
1687 /* No, reparent */
1688 gtk_grab_remove(widget);
1689 gtk_widget_reparent(widget, side);
1690 dnd_motion_grab_pointer();
1691 gtk_grab_add(widget);
1694 gtk_box_reorder_child(GTK_BOX(side), widget, index);
1696 g_list_free(list);
1699 /* Move icon to this index in the complete widget list.
1700 * 0 makes the icon the left-most icon. The gap in the middle has
1701 * an index number, which allows you to specify that the icon should
1702 * go on the left or right side.
1704 static void reposition_icon(PanelIcon *pi, int index)
1706 Panel *panel = pi->panel;
1707 GtkWidget *widget = pi->widget;
1708 GList *list;
1709 int before_len;
1711 list = gtk_container_get_children(GTK_CONTAINER(panel->before));
1712 before_len = g_list_length(list);
1713 g_list_free(list);
1715 if (index <= before_len)
1716 reposition_icon_on_side(panel->before, widget, index);
1717 else
1718 reposition_icon_on_side(panel->after, widget,
1719 index - (before_len + 1));
1721 panel_save(panel);
1724 static void start_drag(PanelIcon *pi, GdkEventMotion *event)
1726 GtkWidget *widget = pi->widget;
1727 Icon *icon = (Icon *) pi;
1729 if (!icon->selected)
1731 if (event->state & GDK_BUTTON1_MASK)
1733 /* Select just this one */
1734 icon_select_only(icon);
1735 tmp_icon_selected = TRUE;
1737 else
1738 icon_set_selected(icon, TRUE);
1741 g_return_if_fail(icon_selection != NULL);
1743 if (icon_selection->next == NULL)
1744 drag_one_item(widget, event, icon->path, icon->item, NULL);
1745 else
1747 guchar *uri_list;
1749 uri_list = icon_create_uri_list();
1750 drag_selection(widget, event, uri_list);
1751 g_free(uri_list);
1755 static void applet_died(GtkWidget *socket)
1757 gboolean never_plugged;
1759 never_plugged = (!g_object_get_data(G_OBJECT(socket), "lost_plug"))
1760 && !GTK_SOCKET(socket)->plug_window;
1762 if (never_plugged)
1764 report_error(
1765 _("Applet quit without ever creating a widget!"));
1766 gtk_widget_destroy(socket);
1769 gtk_widget_unref(socket);
1772 static void socket_destroyed(GtkWidget *socket, GtkWidget *widget)
1774 g_object_set_data(G_OBJECT(socket), "lost_plug", "yes");
1776 gtk_widget_unref(socket);
1778 gtk_widget_destroy(widget); /* Remove from panel */
1780 if (!closing_panel)
1781 panel_save(g_object_get_data(G_OBJECT(socket), "panel"));
1784 /* Try to run this applet.
1785 * Cases:
1787 * - No executable AppletRun:
1788 * icon->socket == NULL (unchanged) on return.
1790 * Otherwise, create socket (setting icon->socket) and ref it twice.
1792 * - AppletRun quits without connecting a plug:
1793 * On child death lost_plug is unset and socket is empty.
1794 * Unref socket.
1795 * Report error and destroy widget (to 'socket destroyed').
1797 * - AppletRun quits while plug is in socket:
1798 * Unref socket once. Socket will be destroyed later.
1800 * - Socket is destroyed.
1801 * Set lost_plug = "yes" and remove widget from panel.
1802 * Unref socket.
1804 static void run_applet(PanelIcon *pi)
1806 GError *error = NULL;
1807 char *argv[3];
1808 gint pid;
1809 Icon *icon = (Icon *) pi;
1811 argv[0] = (char *) make_path(icon->path, "AppletRun");
1813 if (access(argv[0], X_OK) != 0)
1814 return;
1816 pi->socket = gtk_socket_new();
1818 gtk_container_add(GTK_CONTAINER(pi->widget), pi->socket);
1819 gtk_widget_show_all(pi->socket);
1820 gtk_widget_realize(pi->socket);
1822 /* Always get button-2 events so we can drag */
1823 XGrabButton(gdk_display, Button2, AnyModifier,
1824 GDK_WINDOW_XWINDOW(pi->socket->window),
1825 False,
1826 ButtonPressMask | ButtonReleaseMask | Button2MotionMask,
1827 GrabModeAsync, /* Pointer */
1828 GrabModeAsync, /* Keyboard */
1829 None, None);
1832 gchar *pos;
1833 PanelSide side = pi->panel->side;
1835 /* Set a hint to let applets position their menus correctly */
1836 pos = g_strdup_printf("%s,%d",
1837 side == PANEL_TOP ? "Top" :
1838 side == PANEL_BOTTOM ? "Bottom" :
1839 side == PANEL_LEFT ? "Left" :
1840 "Right", MENU_MARGIN(side));
1841 gdk_property_change(pi->socket->window,
1842 gdk_atom_intern("_ROX_PANEL_MENU_POS", FALSE),
1843 gdk_atom_intern("STRING", FALSE),
1844 8, GDK_PROP_MODE_REPLACE,
1845 pos, strlen(pos));
1846 g_free(pos);
1848 /* Ensure that the properties are set before starting the
1849 * applet.
1851 gdk_flush();
1854 g_object_set_data(G_OBJECT(pi->widget), "icon", pi);
1855 g_object_set_data(G_OBJECT(pi->socket), "panel", pi->panel);
1857 argv[1] = g_strdup_printf("%ld",
1858 GDK_WINDOW_XWINDOW(pi->socket->window));
1859 argv[2] = NULL;
1861 if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
1862 NULL, NULL, &pid, &error))
1864 delayed_error(_("Error running applet:\n%s"), error->message);
1865 g_error_free(error);
1866 gtk_widget_destroy(pi->socket);
1867 pi->socket = NULL;
1869 else
1871 gtk_widget_ref(pi->socket);
1872 on_child_death(pid, (CallbackFn) applet_died, pi->socket);
1874 gtk_widget_ref(pi->socket);
1875 g_signal_connect(pi->socket, "destroy",
1876 G_CALLBACK(socket_destroyed), pi->widget);
1879 g_free(argv[1]);
1882 static void panel_post_resize(GtkWidget *win, GtkRequisition *req, Panel *panel)
1884 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
1886 req->width = panel_geometry.width;
1887 req->height += EDGE_WIDTH;
1889 else
1891 int h = panel_geometry.height;
1893 if (current_panel[PANEL_TOP])
1895 GtkWidget *win = current_panel[PANEL_TOP]->window;
1896 h -= win->allocation.height;
1899 if (current_panel[PANEL_BOTTOM])
1901 GtkWidget *win = current_panel[PANEL_BOTTOM]->window;
1902 h -= win->allocation.height;
1905 req->height = h;
1906 req->width += EDGE_WIDTH;
1910 static void update_side(GtkWidget *side)
1912 GList *kids, *next;
1914 kids = gtk_container_get_children(GTK_CONTAINER(side));
1915 for (next = kids; next; next = next->next)
1917 PanelIcon *pi;
1918 pi = g_object_get_data(next->data, "icon");
1919 panel_icon_set_tip(pi);
1921 g_list_free(kids);
1924 /* Tips or style has changed -- update everything on this panel */
1925 static void panel_set_style(Panel *panel)
1927 update_side(panel->before);
1928 update_side(panel->after);
1929 gtk_widget_queue_resize(panel->window);
1932 static gboolean recreate_panels(char **names)
1934 int i;
1936 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
1938 if (names[i])
1940 panel_new(names[i], i);
1941 g_free(names[i]);
1945 g_free(names);
1947 return FALSE;
1950 static void update_side_size(GtkWidget *side)
1952 GList *kids, *next;
1954 kids = gtk_container_get_children(GTK_CONTAINER(side));
1955 for (next = kids; next; next = next->next)
1957 PanelIcon *pi;
1958 pi = g_object_get_data(next->data, "icon");
1959 gtk_widget_queue_resize(pi->widget);
1961 g_list_free(kids);
1964 /* Update panel size and redraw */
1965 static void panel_update(Panel *panel)
1967 update_side_size(panel->before);
1968 update_side_size(panel->after);
1969 gtk_widget_queue_resize(panel->window);
1970 gtk_widget_queue_draw(panel->window);
1973 static void panel_style_changed(void)
1975 int i;
1977 if (o_override_redirect.has_changed)
1979 gchar **names;
1981 names = g_new(char *, PANEL_NUMBER_OF_SIDES);
1983 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
1985 Panel *panel = current_panel[i];
1986 names[i] = panel ? g_strdup(panel->name) : NULL;
1987 panel_new(NULL, i);
1990 g_idle_add((GtkFunction) recreate_panels, names);
1993 if (o_panel_style.has_changed)
1995 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
1997 if (current_panel[i])
1998 panel_set_style(current_panel[i]);
2001 if (o_panel_width.has_changed)
2003 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
2005 if (current_panel[i])
2006 panel_update(current_panel[i]);
2010 if (o_panel_xinerama.has_changed || o_panel_monitor.has_changed ||
2011 o_panel_avoid.has_changed)
2013 if (panel_check_xinerama() || o_panel_avoid.has_changed)
2015 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
2017 if (current_panel[i])
2019 reposition_panel(
2020 current_panel[i]->window,
2021 &current_panel[i]->
2022 window->allocation,
2023 current_panel[i]);
2024 gtk_widget_queue_resize(
2025 current_panel[i]->window);
2032 static gboolean draw_panel_edge(GtkWidget *widget, GdkEventExpose *event,
2033 Panel *panel)
2035 int x, y, width, height;
2037 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
2039 width = panel_geometry.width;
2040 height = EDGE_WIDTH;
2042 x = 0;
2043 if (panel->side == PANEL_BOTTOM)
2044 y = 0;
2045 else
2046 y = widget->allocation.height - EDGE_WIDTH;
2048 else
2050 width = EDGE_WIDTH;
2051 height = panel_geometry.height;
2053 y = 0;
2054 if (panel->side == PANEL_RIGHT)
2055 x = 0;
2056 else
2057 x = widget->allocation.width - EDGE_WIDTH;
2060 gdk_draw_rectangle(widget->window,
2061 widget->style->fg_gc[GTK_STATE_NORMAL], TRUE,
2062 x, y, width, height);
2064 return FALSE;
2067 static gpointer parent_class;
2069 static void panel_icon_destroy(Icon *icon)
2071 PanelIcon *pi = (PanelIcon *) icon;
2073 g_return_if_fail(pi != NULL);
2075 if (pi->image)
2076 g_object_unref(pi->image);
2078 g_return_if_fail(pi->widget != NULL);
2080 gtk_widget_destroy(pi->widget);
2083 static void panel_remove_items(void)
2085 Panel *panel;
2087 g_return_if_fail(icon_selection != NULL);
2089 panel = ((PanelIcon *) icon_selection->data)->panel;
2091 while (icon_selection)
2092 icon_destroy((Icon *) icon_selection->data);
2094 panel_save(panel);
2097 static void panel_icon_redraw(Icon *icon)
2099 gtk_widget_set_state(((PanelIcon *) icon)->widget,
2100 icon->selected ? GTK_STATE_SELECTED
2101 : GTK_STATE_NORMAL);
2102 gtk_widget_queue_draw(PANEL_ICON(icon)->widget);
2105 static void panel_icon_update(Icon *icon)
2107 PanelIcon *pi = (PanelIcon *) icon;
2109 gtk_widget_queue_draw(pi->widget);
2110 gtk_label_set_text(GTK_LABEL(pi->label), icon->item->leafname);
2111 panel_icon_set_tip(pi);
2112 panel_save(pi->panel);
2115 /* The point of this is to clear the selection if the existing icons
2116 * aren't from the same panel...
2118 static gboolean panel_icon_same_group(Icon *icon, Icon *other)
2120 if (IS_PANEL_ICON(other))
2122 PanelIcon *a = (PanelIcon *) icon;
2123 PanelIcon *b = (PanelIcon *) other;
2125 return a->panel == b->panel;
2127 else
2128 return FALSE;
2131 static void panel_icon_class_init(gpointer gclass, gpointer data)
2133 IconClass *icon = (IconClass *) gclass;
2135 parent_class = g_type_class_peek_parent(gclass);
2137 icon->destroy = panel_icon_destroy;
2138 icon->redraw = panel_icon_redraw;
2139 icon->update = panel_icon_update;
2140 icon->remove_items = panel_remove_items;
2141 icon->same_group = panel_icon_same_group;
2142 icon->wink = panel_icon_wink;
2145 static void panel_icon_init(GTypeInstance *object, gpointer gclass)
2147 PanelIcon *pi = (PanelIcon *) object;
2149 pi->widget = NULL;
2150 pi->image = NULL;
2151 pi->label = NULL;
2152 pi->socket = NULL;
2153 pi->style = TEXT_UNDER_ICON;
2156 static GType panel_icon_get_type(void)
2158 static GType type = 0;
2160 if (!type)
2162 static const GTypeInfo info =
2164 sizeof (PanelIconClass),
2165 NULL, /* base_init */
2166 NULL, /* base_finalise */
2167 panel_icon_class_init,
2168 NULL, /* class_finalise */
2169 NULL, /* class_data */
2170 sizeof(PanelIcon),
2171 0, /* n_preallocs */
2172 panel_icon_init
2175 type = g_type_register_static(icon_get_type(),
2176 "PanelIcon", &info, 0);
2179 return type;
2182 static PanelIcon *panel_icon_new(Panel *panel,
2183 const char *pathname,
2184 const char *name)
2186 PanelIcon *pi;
2187 Icon *icon;
2189 pi = g_object_new(panel_icon_get_type(), NULL);
2190 icon = (Icon *) pi;
2192 icon_set_path(icon, pathname, name);
2193 pi->panel = panel;
2195 return pi;
2198 static gboolean panel_want_show_text(PanelIcon *pi)
2200 Icon *icon = (Icon *) pi;
2202 if (!icon->item->leafname[0])
2203 return FALSE;
2205 if (o_panel_style.int_value == SHOW_BOTH)
2206 return TRUE;
2207 if (o_panel_style.int_value == SHOW_ICON)
2208 return FALSE;
2210 if (icon->item->flags & ITEM_FLAG_APPDIR)
2211 return FALSE;
2213 return TRUE;
2216 static void panel_position_menu(GtkMenu *menu, gint *x, gint *y,
2217 gboolean *push_in, gpointer data)
2219 int *pos = (int *) data;
2220 GtkRequisition requisition;
2221 int margin = pos[2];
2222 int mon = pos[3];
2223 int mon_right = monitor_geom[mon].x +
2224 monitor_geom[mon].width;
2225 int mon_bottom = monitor_geom[mon].y +
2226 monitor_geom[mon].height;
2228 gtk_widget_size_request(GTK_WIDGET(menu), &requisition);
2230 if (pos[0] == -1)
2231 *x = mon_right - margin - requisition.width;
2232 else if (pos[0] == -2)
2233 *x = monitor_geom[mon].x + margin;
2234 else
2235 *x = pos[0] - (requisition.width >> 2);
2237 if (pos[1] == -1)
2238 *y = mon_bottom - margin - requisition.height;
2239 else if (pos[1] == -2)
2240 *y = monitor_geom[mon].y + margin;
2241 else
2242 *y = pos[1] - (requisition.height >> 2);
2244 *x = CLAMP(*x, 0, mon_right - requisition.width);
2245 *y = CLAMP(*y, 0, mon_bottom - requisition.height);
2247 *push_in = FALSE;
2250 static void panel_show_menu(GdkEventButton *event, PanelIcon *pi, Panel *panel)
2252 PanelSide side = panel->side;
2253 int pos[4];
2255 pos[0] = event->x_root;
2256 pos[1] = event->y_root;
2257 pos[2] = MENU_MARGIN(side);
2258 /* FIXME: Should we read screen from event's window rather than
2259 * using default? */
2260 pos[3] = gdk_screen_get_monitor_at_point(
2261 gdk_screen_get_default(),
2262 event->x_root, event->y_root);
2264 icon_prepare_menu((Icon *) pi, FALSE);
2266 if (side == PANEL_LEFT)
2267 pos[0] = -2;
2268 else if (side == PANEL_RIGHT)
2269 pos[0] = -1;
2271 if (side == PANEL_TOP)
2272 pos[1] = -2;
2273 else if (side == PANEL_BOTTOM)
2274 pos[1] = -1;
2276 gtk_menu_popup(GTK_MENU(icon_menu), NULL, NULL,
2277 panel_position_menu,
2278 (gpointer) pos, event->button, event->time);
2281 /* Note: also called from icon handler */
2282 static gboolean panel_drag_motion(GtkWidget *widget,
2283 GdkDragContext *context,
2284 gint x,
2285 gint y,
2286 guint time,
2287 Panel *panel)
2289 int panel_x, panel_y;
2291 gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
2293 motion_may_raise(panel, panel_x, panel_y);
2294 gdk_drag_status(context, 0, time);
2296 return TRUE;
2299 static gboolean insert_drag_motion(GtkWidget *widget,
2300 GdkDragContext *context,
2301 gint x,
2302 gint y,
2303 guint time,
2304 Panel *panel)
2306 int panel_x, panel_y;
2308 gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
2309 motion_may_raise(panel, panel_x, panel_y);
2311 return FALSE;
2314 /* Note: also called from icon handler */
2315 static void panel_drag_leave(GtkWidget *widget,
2316 GdkDragContext *context,
2317 guint32 time,
2318 Panel *panel)
2320 GdkWindow *pinboard, *window;
2321 GtkAllocation *alloc = &panel->window->allocation;
2322 int x, y;
2324 window = panel->window->window;
2325 gdk_window_get_pointer(window, &x, &y, NULL);
2326 if (x < 0 || y < 0 || x > alloc->width || y > alloc->height)
2328 keep_below(panel->window->window, TRUE);
2330 /* Shouldn't need this as well as keep_below but some WMs don't
2331 * automatically lower as soon as the hint is set */
2332 pinboard = pinboard_get_window();
2333 window_put_just_above(panel->window->window, pinboard);
2337 static gboolean panel_check_xinerama(void)
2339 gint old_monitor = panel_monitor;
2341 panel_monitor = -1;
2343 if (o_panel_xinerama.int_value)
2345 if (o_panel_monitor.int_value < n_monitors)
2347 panel_monitor = o_panel_monitor.int_value;
2349 else
2351 g_warning(_("Xinerama monitor %d unavailable"),
2352 o_panel_monitor.int_value);
2356 if (panel_monitor == -1)
2358 panel_geometry.x = panel_geometry.y = 0;
2359 panel_geometry.width = screen_width;
2360 panel_geometry.height = screen_height;
2362 else
2364 panel_geometry = monitor_geom[panel_monitor];
2367 return old_monitor != panel_monitor;
2370 static GList *build_monitor_number(Option *option, xmlNode *node, guchar *label)
2372 GtkObject *adj;
2374 adj = gtk_adjustment_new(MAX(0, panel_monitor),
2375 0, n_monitors - 1, 1, 10, 1);
2376 return build_numentry_base(option, node, label, GTK_ADJUSTMENT(adj));