r2416: Panel auto-raising also works during drag-and-drop.
[rox-filer.git] / ROX-Filer / src / panel.c
blob4e5cba5335196af5c0ca0b0e1fa5fe400c200340
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 "display.h"
47 #include "bind.h"
48 #include "dnd.h"
49 #include "support.h"
50 #include "filer.h"
51 #include "icon.h"
52 #include "run.h"
53 #include "appinfo.h"
54 #include "xml.h"
55 #include "pinboard.h" /* For pinboard_get_window() */
57 /* The width of the separator at the inner edge of the panel */
58 #define EDGE_WIDTH 2
60 /* The gap between panel icons */
61 #define PANEL_ICON_SPACING 8
63 static gboolean tmp_icon_selected = FALSE; /* When dragging */
65 typedef struct _PanelIconClass PanelIconClass;
66 typedef struct _PanelIcon PanelIcon;
68 struct _PanelIconClass {
69 IconClass parent;
72 struct _PanelIcon {
73 Icon icon;
75 Panel *panel;
76 GtkWidget *widget; /* The drawing area for the icon */
77 GtkWidget *label;
78 GtkWidget *socket; /* For applets */
81 #define PANEL_ICON(obj) GTK_CHECK_CAST((obj), panel_icon_get_type(), PanelIcon)
82 #define IS_PANEL_ICON(obj) \
83 G_TYPE_CHECK_INSTANCE_TYPE((obj), panel_icon_get_type())
85 Panel *current_panel[PANEL_NUMBER_OF_SIDES];
87 /* NULL => Not loading a panel */
88 static Panel *loading_panel = NULL;
90 /* Static prototypes */
91 static int panel_delete(GtkWidget *widget, GdkEvent *event, Panel *panel);
92 static void panel_destroyed(GtkWidget *widget, Panel *panel);
93 static const char *pan_from_file(gchar *line);
94 static gint icon_button_release(GtkWidget *widget,
95 GdkEventButton *event,
96 PanelIcon *pi);
97 static gint icon_button_press(GtkWidget *widget,
98 GdkEventButton *event,
99 PanelIcon *pi);
100 static void reposition_panel(GtkWidget *window,
101 GtkAllocation *alloc, Panel *panel);
102 static gint expose_icon(GtkWidget *widget,
103 GdkEventExpose *event,
104 PanelIcon *pi);
105 static gint draw_icon(GtkWidget *widget,
106 GdkRectangle *badarea,
107 PanelIcon *pi);
108 static gint panel_button_release(GtkWidget *widget,
109 GdkEventButton *event,
110 Panel *panel);
111 static gint panel_button_press(GtkWidget *widget,
112 GdkEventButton *event,
113 Panel *panel);
114 static void panel_post_resize(GtkWidget *box,
115 GtkRequisition *req, Panel *panel);
116 static void drag_set_panel_dest(PanelIcon *pi);
117 static void add_uri_list(GtkWidget *widget,
118 GdkDragContext *context,
119 gint x,
120 gint y,
121 GtkSelectionData *selection_data,
122 guint info,
123 guint32 time,
124 Panel *panel);
125 static void panel_add_item(Panel *panel,
126 const gchar *path,
127 const gchar *name,
128 gboolean after,
129 const gchar *shortcut);
130 static gboolean panel_drag_motion(GtkWidget *widget,
131 GdkDragContext *context,
132 gint x,
133 gint y,
134 guint time,
135 Panel *panel);
136 static gboolean drag_motion(GtkWidget *widget,
137 GdkDragContext *context,
138 gint x,
139 gint y,
140 guint time,
141 PanelIcon *pi);
142 static void panel_drag_leave(GtkWidget *widget,
143 GdkDragContext *context,
144 guint32 time,
145 Panel *panel);
146 static void drag_leave(GtkWidget *widget,
147 GdkDragContext *context,
148 guint32 time,
149 Icon *icon);
150 static GtkWidget *make_insert_frame(Panel *panel);
151 static gboolean enter_icon(GtkWidget *widget,
152 GdkEventCrossing *event,
153 Icon *icon);
154 static gint icon_motion_event(GtkWidget *widget,
155 GdkEventMotion *event,
156 PanelIcon *pi);
157 static gint panel_leave_event(GtkWidget *widget,
158 GdkEventCrossing *event,
159 Panel *panel);
160 static gint panel_motion_event(GtkWidget *widget,
161 GdkEventMotion *event,
162 Panel *panel);
163 static void reposition_icon(PanelIcon *pi, int index);
164 static void start_drag(PanelIcon *pi, GdkEventMotion *event);
165 static void drag_end(GtkWidget *widget,
166 GdkDragContext *context,
167 Icon *icon);
168 static void perform_action(Panel *panel,
169 PanelIcon *pi,
170 GdkEventButton *event);
171 static void run_applet(PanelIcon *pi);
172 static void size_request(GtkWidget *widget, GtkRequisition *req, PanelIcon *pi);
173 static void panel_load_from_xml(Panel *panel, xmlDocPtr doc);
174 static gboolean draw_panel_edge(GtkWidget *widget, GdkEventExpose *event,
175 Panel *panel);
176 static PanelIcon *panel_icon_new(Panel *panel,
177 const char *pathname,
178 const char *name);
179 static GType panel_icon_get_type(void);
180 static gboolean panel_want_show_text(PanelIcon *pi);
181 static void panel_show_menu(GdkEventButton *event, PanelIcon *pi, Panel *panel);
182 static void panel_style_changed(void);
185 static GtkWidget *dnd_highlight = NULL; /* (stops flickering) */
187 /* When sliding the panel, records where the panel was before */
188 static gint slide_from_value = 0;
190 #define SHOW_BOTH 0
191 #define SHOW_APPS_SMALL 1
192 #define SHOW_ICON 2
193 static Option o_panel_style;
195 static int closing_panel = 0; /* Don't panel_save; destroying! */
197 /****************************************************************
198 * EXTERNAL INTERFACE *
199 ****************************************************************/
201 void panel_init(void)
203 option_add_int(&o_panel_style, "panel_style", SHOW_APPS_SMALL);
205 option_add_notify(panel_style_changed);
208 /* 'name' may be NULL or "" to remove the panel */
209 Panel *panel_new(const gchar *name, PanelSide side)
211 guchar *load_path;
212 Panel *panel;
213 GtkWidget *vp, *box, *frame, *align;
215 g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, NULL);
216 g_return_val_if_fail(loading_panel == NULL, NULL);
218 if (name && *name == '\0')
219 name = NULL;
221 if (current_panel[side])
223 if (name)
224 number_of_windows++;
225 closing_panel++;
226 gtk_widget_destroy(current_panel[side]->window);
227 closing_panel--;
228 if (name)
229 number_of_windows--;
232 if (name == NULL || *name == '\0')
233 return NULL;
235 panel = g_new(Panel, 1);
236 panel->name = g_strdup(name);
237 panel->side = side;
238 panel->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
239 gtk_window_set_resizable(GTK_WINDOW(panel->window), FALSE);
240 gtk_window_set_wmclass(GTK_WINDOW(panel->window), "ROX-Panel", PROJECT);
241 gtk_widget_set_name(panel->window, "rox-panel");
242 gtk_widget_set_events(panel->window,
243 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
244 GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
246 /* We make the panel a drop target only so that we can auto-raise! */
247 gtk_drag_dest_set(panel->window, 0, NULL, 0, GDK_ACTION_PRIVATE);
248 g_signal_connect(panel->window, "drag_leave",
249 G_CALLBACK(panel_drag_leave), panel);
250 g_signal_connect(panel->window, "drag_motion",
251 G_CALLBACK(panel_drag_motion), panel);
253 g_signal_connect(panel->window, "delete-event",
254 G_CALLBACK(panel_delete), panel);
255 g_signal_connect(panel->window, "destroy",
256 G_CALLBACK(panel_destroyed), panel);
257 g_signal_connect(panel->window, "button_press_event",
258 G_CALLBACK(panel_button_press), panel);
259 g_signal_connect(panel->window, "button_release_event",
260 G_CALLBACK(panel_button_release), panel);
261 g_signal_connect(panel->window, "motion-notify-event",
262 G_CALLBACK(panel_motion_event), panel);
263 g_signal_connect(panel->window, "leave-notify-event",
264 G_CALLBACK(panel_leave_event), panel);
266 if (strchr(name, '/'))
267 load_path = g_strdup(name);
268 else
270 guchar *leaf;
272 leaf = g_strconcat("pan_", name, NULL);
273 load_path = choices_find_path_load(leaf, PROJECT);
274 g_free(leaf);
277 if (panel->side == PANEL_RIGHT)
278 align = gtk_alignment_new(1.0, 0.0, 0.0, 1.0);
279 else if (panel->side == PANEL_BOTTOM)
280 align = gtk_alignment_new(0.0, 1.0, 1.0, 0.0);
281 else if (panel->side == PANEL_TOP)
282 align = gtk_alignment_new(0.0, 0.0, 1.0, 0.0);
283 else
284 align = gtk_alignment_new(0.0, 0.0, 0.0, 1.0);
286 gtk_container_add(GTK_CONTAINER(panel->window), align);
288 vp = gtk_viewport_new(NULL, NULL);
289 gtk_container_set_resize_mode(GTK_CONTAINER(vp), GTK_RESIZE_PARENT);
290 gtk_viewport_set_shadow_type(GTK_VIEWPORT(vp), GTK_SHADOW_NONE);
291 gtk_container_add(GTK_CONTAINER(align), vp);
293 g_signal_connect(align, "expose-event",
294 G_CALLBACK(draw_panel_edge), panel);
296 if (side == PANEL_TOP || side == PANEL_BOTTOM)
298 panel->adj = gtk_viewport_get_hadjustment(GTK_VIEWPORT(vp));
299 box = gtk_hbox_new(FALSE, 0);
300 panel->before = gtk_hbox_new(FALSE, 0);
301 panel->after = gtk_hbox_new(FALSE, 0);
303 else
305 panel->adj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(vp));
306 box = gtk_vbox_new(FALSE, 0);
307 panel->before = gtk_vbox_new(FALSE, 0);
308 panel->after = gtk_vbox_new(FALSE, 0);
311 gtk_container_add(GTK_CONTAINER(vp), box);
312 gtk_box_pack_start(GTK_BOX(box), panel->before, FALSE, TRUE, 0);
313 gtk_box_pack_end(GTK_BOX(box), panel->after, FALSE, TRUE, 0);
315 frame = make_insert_frame(panel);
316 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
318 /* This is used so that we can find the middle easily! */
319 panel->gap = gtk_event_box_new();
320 gtk_box_pack_start(GTK_BOX(box), panel->gap, FALSE, FALSE, 0);
322 frame = make_insert_frame(panel);
323 g_object_set_data(G_OBJECT(frame), "after", "yes");
324 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
326 gtk_widget_realize(panel->window);
327 make_panel_window(panel->window);
328 if (!(gtk_major_version == 2 && gtk_minor_version == 0))
329 gtk_window_stick(GTK_WINDOW(panel->window));
331 gtk_widget_show_all(align);
333 loading_panel = panel;
334 if (load_path && access(load_path, F_OK) == 0)
336 xmlDocPtr doc;
337 doc = xmlParseFile(load_path);
338 if (doc)
340 panel_load_from_xml(panel, doc);
341 xmlFreeDoc(doc);
343 else
345 parse_file(load_path, pan_from_file);
346 info_message(_("Your old panel file has been "
347 "converted to the new XML format."));
348 panel_save(panel);
351 else
353 /* Don't scare users with an empty panel... */
354 guchar *apps;
356 panel_add_item(panel, "~", "Home", FALSE, NULL);
358 apps = pathdup(make_path(app_dir, ".."));
359 if (apps)
361 panel_add_item(panel, apps, "Apps", FALSE, NULL);
362 g_free(apps);
365 loading_panel = NULL;
366 g_free(load_path);
368 current_panel[side] = panel;
370 gtk_widget_queue_resize(box);
371 g_signal_connect(panel->window, "size-request",
372 G_CALLBACK(panel_post_resize), panel);
373 g_signal_connect(panel->window, "size-allocate",
374 G_CALLBACK(reposition_panel), panel);
376 /* Stop windows from maximising over us completely, so that the
377 * auto-raise stuff works...
380 guint32 wm_strut[] = {0, 0, 0, 0};
382 if (panel->side == PANEL_LEFT)
383 wm_strut[0] = 2;
384 else if (panel->side == PANEL_RIGHT)
385 wm_strut[1] = 2;
386 else if (panel->side == PANEL_TOP)
387 wm_strut[2] = 2;
388 else
389 wm_strut[3] = 2;
391 gdk_property_change(panel->window->window,
392 gdk_atom_intern("_NET_WM_STRUT", FALSE),
393 gdk_atom_intern("CARDINAL", FALSE),
394 32, GDK_PROP_MODE_REPLACE,
395 (gchar *) &wm_strut, 4);
398 number_of_windows++;
399 gdk_window_lower(panel->window->window);
400 gtk_widget_show(panel->window);
403 GdkWindow *pinboard;
405 pinboard = pinboard_get_window();
406 /* (if pinboard is NULL, will go right to the back) */
407 window_put_just_above(panel->window->window, pinboard);
410 return panel;
413 /* Externally visible function to add an item to a panel */
414 gboolean panel_add(PanelSide side,
415 const gchar *path, const gchar *label, gboolean after)
417 g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, FALSE);
419 g_return_val_if_fail(current_panel[side] != NULL, FALSE);
421 panel_add_item(current_panel[side], path, label, after, NULL);
423 return TRUE;
426 /* Add the area covered by the panels to the region */
427 void panel_mark_used(GdkRegion *used)
429 int i;
431 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
433 Panel *panel = current_panel[i];
434 GdkRectangle rect;
436 if (!panel)
437 continue;
439 gdk_window_get_root_origin(panel->window->window,
440 &rect.x, &rect.y);
441 rect.width = panel->window->allocation.width;
442 rect.height = panel->window->allocation.height;
444 gdk_region_union_with_rect(used, &rect);
448 /****************************************************************
449 * INTERNAL FUNCTIONS *
450 ****************************************************************/
452 /* User has tried to close the panel via the window manager - confirm */
453 static int panel_delete(GtkWidget *widget, GdkEvent *event, Panel *panel)
455 return !confirm(_("You have tried to close a panel via the window "
456 "manager - I usually find that this is accidental... "
457 "really close?"),
458 GTK_STOCK_CLOSE, NULL);
461 static void panel_destroyed(GtkWidget *widget, Panel *panel)
463 if (current_panel[panel->side] == panel)
464 current_panel[panel->side] = NULL;
466 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
468 if (current_panel[PANEL_RIGHT])
469 gtk_widget_queue_resize(
470 current_panel[PANEL_RIGHT]->window);
471 if (current_panel[PANEL_LEFT])
472 gtk_widget_queue_resize(
473 current_panel[PANEL_LEFT]->window);
476 g_free(panel->name);
477 g_free(panel);
479 one_less_window();
482 static void panel_load_side(Panel *panel, xmlNodePtr side, gboolean after)
484 xmlNodePtr node;
485 char *label, *path, *shortcut;
487 for (node = side->xmlChildrenNode; node; node = node->next)
489 if (node->type != XML_ELEMENT_NODE)
490 continue;
491 if (strcmp(node->name, "icon") != 0)
492 continue;
494 label = xmlGetProp(node, "label");
495 if (!label)
496 label = g_strdup("<missing label>");
497 path = xmlNodeGetContent(node);
498 if (!path)
499 path = g_strdup("<missing path>");
500 shortcut = xmlGetProp(node, "shortcut");
502 panel_add_item(panel, path, label, after, shortcut);
504 g_free(path);
505 g_free(label);
506 g_free(shortcut);
510 /* Create one panel icon for each icon in the doc */
511 static void panel_load_from_xml(Panel *panel, xmlDocPtr doc)
513 xmlNodePtr root;
515 root = xmlDocGetRootElement(doc);
516 panel_load_side(panel, get_subnode(root, NULL, "start"), FALSE);
517 panel_load_side(panel, get_subnode(root, NULL, "end"), TRUE);
520 /* Called for each line in the config file while loading a new panel */
521 static const char *pan_from_file(gchar *line)
523 gchar *sep, *leaf;
525 g_return_val_if_fail(line != NULL, NULL);
526 g_return_val_if_fail(loading_panel != NULL, NULL);
528 if (*line == '\0')
529 return NULL;
531 sep = strpbrk(line, "<>");
532 if (!sep)
533 return _("Missing < or > in panel config file");
535 if (sep != line)
536 leaf = g_strndup(line, sep - line);
537 else
538 leaf = NULL;
540 panel_add_item(loading_panel, sep + 1, leaf, sep[0] == '>', NULL);
542 g_free(leaf);
544 return NULL;
547 static gboolean icon_pointer_in(GtkWidget *widget,
548 GdkEventCrossing *event,
549 Icon *icon)
551 gtk_widget_set_state(widget, GTK_STATE_PRELIGHT);
553 return 0;
556 static gboolean icon_pointer_out(GtkWidget *widget,
557 GdkEventCrossing *event,
558 Icon *icon)
560 gtk_widget_set_state(widget, GTK_STATE_NORMAL);
562 return 0;
565 static void panel_icon_destroyed(PanelIcon *pi)
567 g_return_if_fail(pi->widget != NULL);
569 pi->widget = NULL;
571 g_object_unref(pi);
574 /* Set the tooltip AND hide/show the label */
575 static void panel_icon_set_tip(PanelIcon *pi)
577 XMLwrapper *ai;
578 xmlNode *node;
579 Icon *icon = (Icon *) pi;
581 g_return_if_fail(pi != NULL);
583 if (pi->label)
585 if (panel_want_show_text(pi))
586 gtk_widget_show(pi->label);
587 else
588 gtk_widget_hide(pi->label);
591 if (pi->socket)
592 ai = NULL;
593 else
594 ai = appinfo_get(icon->path, icon->item);
596 if (ai && ((node = xml_get_section(ai, NULL, "Summary"))))
598 guchar *str;
599 str = xmlNodeListGetString(node->doc,
600 node->xmlChildrenNode, 1);
601 if (str)
603 gtk_tooltips_set_tip(tooltips, pi->widget, str, NULL);
604 g_free(str);
607 else if ((!panel_want_show_text(pi)) && !pi->socket)
609 gtk_tooltips_set_tip(tooltips, pi->widget,
610 icon->item->leafname, NULL);
612 else
613 gtk_tooltips_set_tip(tooltips, pi->widget, NULL, NULL);
615 if (ai)
616 g_object_unref(ai);
619 /* Add an icon with this path to the panel. If after is TRUE then the
620 * icon is added to the right/bottom end of the panel.
622 * If name is NULL a suitable name is taken from path.
624 static void panel_add_item(Panel *panel,
625 const gchar *path,
626 const gchar *name,
627 gboolean after,
628 const gchar *shortcut)
630 GtkWidget *widget;
631 PanelIcon *pi;
632 Icon *icon;
634 g_return_if_fail(panel != NULL);
635 g_return_if_fail(path != NULL);
637 widget = gtk_event_box_new();
638 gtk_widget_set_events(widget,
639 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
640 GDK_BUTTON3_MOTION_MASK |
641 GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
642 GDK_BUTTON_RELEASE_MASK);
644 gtk_box_pack_start(GTK_BOX(after ? panel->after : panel->before),
645 widget, FALSE, TRUE, 0);
646 if (after)
647 gtk_box_reorder_child(GTK_BOX(panel->after), widget, 0);
649 gtk_widget_realize(widget);
651 pi = panel_icon_new(panel, path, name);
652 icon = (Icon *) pi;
654 /* Widget takes the initial ref of Icon */
655 g_object_set_data(G_OBJECT(widget), "icon", pi);
657 pi->widget = widget;
658 g_object_ref(widget);
660 gtk_widget_set_name(pi->widget, "panel-icon");
662 g_signal_connect_swapped(widget, "destroy",
663 G_CALLBACK(panel_icon_destroyed), pi);
665 if (icon->item->base_type == TYPE_DIRECTORY)
666 run_applet(pi);
668 g_signal_connect(widget, "button_release_event",
669 G_CALLBACK(icon_button_release), pi);
670 g_signal_connect(widget, "button_press_event",
671 G_CALLBACK(icon_button_press), pi);
672 g_signal_connect(widget, "motion-notify-event",
673 G_CALLBACK(icon_motion_event), pi);
674 g_signal_connect(widget, "enter-notify-event",
675 G_CALLBACK(icon_pointer_in), pi);
676 g_signal_connect(widget, "leave-notify-event",
677 G_CALLBACK(icon_pointer_out), pi);
679 if (!pi->socket)
681 g_signal_connect(widget, "enter-notify-event",
682 G_CALLBACK(enter_icon), pi);
683 g_signal_connect_after(widget, "expose_event",
684 G_CALLBACK(expose_icon), pi);
685 g_signal_connect(widget, "drag_data_get",
686 G_CALLBACK(drag_data_get), NULL);
688 g_signal_connect(widget, "size_request",
689 G_CALLBACK(size_request), pi);
691 drag_set_panel_dest(pi);
693 pi->label = gtk_label_new(icon->item->leafname);
694 gtk_container_add(GTK_CONTAINER(pi->widget), pi->label);
695 gtk_misc_set_alignment(GTK_MISC(pi->label), 0.5, 1);
696 gtk_misc_set_padding(GTK_MISC(pi->label), 1, 2);
699 icon_set_shortcut(icon, shortcut);
701 if (!loading_panel)
702 panel_save(panel);
704 panel_icon_set_tip(pi);
705 gtk_widget_show(widget);
708 /* Called when Gtk+ wants to know how much space an icon needs.
709 * 'req' is already big enough for the label, if shown.
711 static void size_request(GtkWidget *widget, GtkRequisition *req, PanelIcon *pi)
713 int im_width, im_height;
714 Icon *icon = (Icon *) pi;
716 im_width = icon->item->image->width;
717 im_height = MIN(icon->item->image->height, ICON_HEIGHT);
719 req->height += im_height;
720 req->width = MAX(req->width, im_width);
722 if (pi->panel->side == PANEL_LEFT || pi->panel->side == PANEL_RIGHT)
723 req->height += PANEL_ICON_SPACING;
724 else
725 req->width += PANEL_ICON_SPACING;
728 static gint expose_icon(GtkWidget *widget,
729 GdkEventExpose *event,
730 PanelIcon *pi)
732 return draw_icon(widget, &event->area, pi);
735 static gint draw_icon(GtkWidget *widget, GdkRectangle *badarea, PanelIcon *pi)
737 GdkRectangle area;
738 int width, height;
739 Icon *icon = (Icon *) pi;
741 gdk_drawable_get_size(widget->window, &width, &height);
743 area.x = 0;
744 area.width = width;
745 area.height = icon->item->image->height;
747 if (panel_want_show_text(pi))
749 int text_height = pi->label->requisition.height;
751 area.y = height - text_height - area.height;
753 draw_large_icon(widget->window, &area, icon->item,
754 icon->item->image, icon->selected);
756 else
758 area.y = (height - area.height) >> 1;
759 draw_large_icon(widget->window, &area, icon->item,
760 icon->item->image, icon->selected);
763 return FALSE;
766 static void panel_icon_wink(Icon *icon)
768 PanelIcon *pi = (PanelIcon *) icon;
770 wink_widget(pi->widget);
773 /* icon may be NULL if the event is on the background */
774 static void perform_action(Panel *panel, PanelIcon *pi, GdkEventButton *event)
776 BindAction action;
777 Icon *icon = (Icon *) pi;
779 action = bind_lookup_bev(icon ? BIND_PANEL_ICON : BIND_PANEL, event);
781 if (pi && pi->socket)
782 if (action != ACT_POPUP_MENU && action != ACT_MOVE_ICON)
783 return;
785 switch (action)
787 case ACT_OPEN_ITEM:
788 dnd_motion_ungrab();
789 wink_widget(pi->widget);
790 run_diritem(icon->path, icon->item, NULL, NULL, FALSE);
791 break;
792 case ACT_EDIT_ITEM:
793 dnd_motion_ungrab();
794 wink_widget(pi->widget);
795 run_diritem(icon->path, icon->item, NULL, NULL, TRUE);
796 break;
797 case ACT_POPUP_MENU:
798 dnd_motion_ungrab();
799 panel_show_menu(event, pi, panel);
800 break;
801 case ACT_MOVE_ICON:
802 dnd_motion_start(MOTION_REPOSITION);
803 break;
804 case ACT_PRIME_AND_SELECT:
805 if (!icon->selected)
806 icon_select_only(icon);
807 dnd_motion_start(MOTION_READY_FOR_DND);
808 break;
809 case ACT_PRIME_AND_TOGGLE:
810 icon_set_selected(icon, !icon->selected);
811 dnd_motion_start(MOTION_READY_FOR_DND);
812 break;
813 case ACT_PRIME_FOR_DND:
814 dnd_motion_start(MOTION_READY_FOR_DND);
815 break;
816 case ACT_TOGGLE_SELECTED:
817 icon_set_selected(icon, !icon->selected);
818 break;
819 case ACT_SELECT_EXCL:
820 icon_set_selected(icon, TRUE);
821 break;
822 case ACT_SLIDE_CLEAR_PANEL:
823 icon_select_only(NULL);
824 /* (no break) */
825 case ACT_SLIDE_PANEL:
826 dnd_motion_grab_pointer();
827 slide_from_value = panel->adj->value;
828 dnd_motion_start(MOTION_REPOSITION);
829 break;
830 case ACT_IGNORE:
831 break;
832 case ACT_CLEAR_SELECTION:
833 icon_select_only(NULL);
834 break;
835 default:
836 g_warning("Unsupported action : %d\n", action);
837 break;
841 static gint panel_button_release(GtkWidget *widget,
842 GdkEventButton *event,
843 Panel *panel)
845 if (dnd_motion_release(event))
846 return TRUE;
848 perform_action(panel, NULL, event);
850 return TRUE;
853 static gint panel_button_press(GtkWidget *widget,
854 GdkEventButton *event,
855 Panel *panel)
857 if (dnd_motion_press(panel->window, event))
858 perform_action(panel, NULL, event);
860 return TRUE;
863 static gint icon_button_release(GtkWidget *widget,
864 GdkEventButton *event,
865 PanelIcon *pi)
867 if (pi->socket && event->button == 1)
868 return FALSE; /* Restart button */
870 if (dnd_motion_release(event))
871 return TRUE;
873 perform_action(pi->panel, pi, event);
875 return TRUE;
878 static gint icon_button_press(GtkWidget *widget,
879 GdkEventButton *event,
880 PanelIcon *pi)
882 if (pi->socket && event->button == 1)
883 return FALSE; /* Restart button */
885 if (dnd_motion_press(widget, event))
886 perform_action(pi->panel, pi, event);
888 return TRUE;
891 static void reposition_panel(GtkWidget *window,
892 GtkAllocation *alloc, Panel *panel)
894 int x = 0, y = 0;
895 PanelSide side = panel->side;
897 if (side == PANEL_LEFT || side == PANEL_RIGHT)
899 if (side == PANEL_RIGHT)
900 x = screen_width - alloc->width;
902 if (current_panel[PANEL_TOP])
904 GtkWidget *win = current_panel[PANEL_TOP]->window;
905 y += win->allocation.height;
909 if (side == PANEL_BOTTOM)
910 y = screen_height - alloc->height;
912 gtk_window_move(GTK_WINDOW(panel->window), x, y);
913 gdk_window_move(panel->window->window, x, y);
915 if (side == PANEL_BOTTOM || side == PANEL_TOP)
917 if (current_panel[PANEL_RIGHT])
918 gtk_widget_queue_resize(
919 current_panel[PANEL_RIGHT]->window);
920 if (current_panel[PANEL_LEFT])
921 gtk_widget_queue_resize(
922 current_panel[PANEL_LEFT]->window);
926 /* Same as drag_set_dest(), but for panel icons */
927 static void drag_set_panel_dest(PanelIcon *pi)
929 GtkWidget *obj = pi->widget;
931 make_drop_target(pi->widget, 0);
933 g_signal_connect(obj, "drag_motion", G_CALLBACK(drag_motion), pi);
934 g_signal_connect(obj, "drag_leave", G_CALLBACK(drag_leave), pi);
935 g_signal_connect(obj, "drag_end", G_CALLBACK(drag_end), pi);
938 static gboolean drag_motion(GtkWidget *widget,
939 GdkDragContext *context,
940 gint x,
941 gint y,
942 guint time,
943 PanelIcon *pi)
945 GdkDragAction action = context->suggested_action;
946 const char *type = NULL;
947 Icon *icon = (Icon *) pi;
948 DirItem *item = icon->item;
950 panel_drag_motion(widget, context, x, y, time, pi->panel);
952 if (icon->selected)
953 goto out; /* Can't drag a selection to itself */
955 type = dnd_motion_item(context, &item);
957 if (!item)
958 type = NULL;
959 out:
960 /* We actually must pretend to accept the drop, even if the
961 * directory isn't writeable, so that the spring-opening
962 * thing works.
965 /* Don't allow drops to non-writeable directories */
966 if (o_dnd_spring_open.int_value == FALSE &&
967 type == drop_dest_dir &&
968 access(icon->path, W_OK) != 0)
970 type = NULL;
973 g_dataset_set_data(context, "drop_dest_type", (gpointer) type);
974 if (type)
976 gdk_drag_status(context, action, time);
977 g_dataset_set_data_full(context, "drop_dest_path",
978 g_strdup(icon->path), g_free);
979 if (type == drop_dest_dir)
980 dnd_spring_load(context, NULL);
982 if (dnd_highlight && dnd_highlight != pi->widget)
984 gtk_drag_unhighlight(dnd_highlight);
985 dnd_highlight = NULL;
988 if (dnd_highlight == NULL)
990 gtk_drag_highlight(pi->widget);
991 dnd_highlight = pi->widget;
995 return type != NULL;
999 static void add_uri_list(GtkWidget *widget,
1000 GdkDragContext *context,
1001 gint x,
1002 gint y,
1003 GtkSelectionData *selection_data,
1004 guint info,
1005 guint32 time,
1006 Panel *panel)
1008 gboolean after = FALSE;
1009 GList *uris, *next;
1011 if (!selection_data->data)
1012 return;
1014 g_return_if_fail(selection_data->data[selection_data->length] == '\0');
1016 if (g_object_get_data(G_OBJECT(widget), "after"))
1017 after = TRUE;
1019 uris = uri_list_to_glist(selection_data->data);
1021 for (next = uris; next; next = next->next)
1023 const guchar *path;
1025 path = get_local_path((guchar *) next->data);
1027 if (path)
1028 panel_add_item(panel, path, NULL, after, NULL);
1031 g_list_free(uris);
1034 static void drag_end(GtkWidget *widget,
1035 GdkDragContext *context,
1036 Icon *icon)
1038 if (tmp_icon_selected)
1040 icon_select_only(NULL);
1041 tmp_icon_selected = FALSE;
1045 static void drag_leave(GtkWidget *widget,
1046 GdkDragContext *context,
1047 guint32 time,
1048 Icon *icon)
1050 panel_drag_leave(widget, context, time, ((PanelIcon *) icon)->panel);
1052 if (dnd_highlight && dnd_highlight == widget)
1054 gtk_drag_unhighlight(dnd_highlight);
1055 dnd_highlight = NULL;
1058 dnd_spring_abort();
1061 /* Create XML icon nodes for these widgets.
1062 * Always frees the widgets list.
1064 static void make_widgets(xmlNodePtr side, GList *widgets)
1066 GList *next;
1068 for (next = widgets; next; next = next->next)
1070 Icon *icon;
1071 xmlNodePtr tree;
1073 icon = g_object_get_data(G_OBJECT(next->data), "icon");
1075 if (!icon)
1077 g_warning("Can't find Icon from widget\n");
1078 continue;
1081 tree = xmlNewTextChild(side, NULL, "icon", icon->src_path);
1083 xmlSetProp(tree, "label", icon->item->leafname);
1084 if (icon->shortcut)
1085 xmlSetProp(tree, "shortcut", icon->shortcut);
1088 if (widgets)
1089 g_list_free(widgets);
1092 void panel_save(Panel *panel)
1094 xmlDocPtr doc;
1095 xmlNodePtr root;
1096 guchar *save = NULL;
1097 guchar *save_new = NULL;
1099 g_return_if_fail(panel != NULL);
1101 if (strchr(panel->name, '/'))
1102 save = g_strdup(panel->name);
1103 else
1105 guchar *leaf;
1107 leaf = g_strconcat("pan_", panel->name, NULL);
1108 save = choices_find_path_save(leaf, PROJECT, TRUE);
1109 g_free(leaf);
1112 if (!save)
1113 return;
1115 doc = xmlNewDoc("1.0");
1116 xmlDocSetRootElement(doc, xmlNewDocNode(doc, NULL, "panel", NULL));
1118 root = xmlDocGetRootElement(doc);
1119 make_widgets(xmlNewChild(root, NULL, "start", NULL),
1120 gtk_container_get_children(GTK_CONTAINER(panel->before)));
1122 make_widgets(xmlNewChild(root, NULL, "end", NULL),
1123 g_list_reverse(gtk_container_get_children(
1124 GTK_CONTAINER(panel->after))));
1126 save_new = g_strconcat(save, ".new", NULL);
1127 if (save_xml_file(doc, save_new) || rename(save_new, save))
1128 delayed_error(_("Error saving panel %s: %s"),
1129 save, g_strerror(errno));
1130 g_free(save_new);
1132 g_free(save);
1133 if (doc)
1134 xmlFreeDoc(doc);
1137 /* Create a frame widget which can be used to add icons to the panel */
1138 static GtkWidget *make_insert_frame(Panel *panel)
1140 GtkWidget *frame;
1141 GtkTargetEntry target_table[] = {
1142 {"text/uri-list", 0, TARGET_URI_LIST},
1145 frame = gtk_frame_new(NULL);
1146 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
1147 gtk_widget_set_size_request(frame, 16, 16);
1149 g_signal_connect(frame, "drag-motion",
1150 G_CALLBACK(panel_drag_motion), panel);
1151 g_signal_connect(frame, "drag-leave",
1152 G_CALLBACK(panel_drag_leave), panel);
1154 g_signal_connect(frame, "drag-data-received",
1155 G_CALLBACK(add_uri_list), panel);
1156 gtk_drag_dest_set(frame,
1157 GTK_DEST_DEFAULT_ALL,
1158 target_table,
1159 sizeof(target_table) / sizeof(*target_table),
1160 GDK_ACTION_COPY);
1162 return frame;
1165 static gboolean enter_icon(GtkWidget *widget,
1166 GdkEventCrossing *event,
1167 Icon *icon)
1169 icon_may_update(icon);
1171 return FALSE;
1174 static gint panel_leave_event(GtkWidget *widget,
1175 GdkEventCrossing *event,
1176 Panel *panel)
1178 GdkWindow *pinboard;
1180 pinboard = pinboard_get_window();
1181 window_put_just_above(panel->window->window, pinboard);
1183 return FALSE;
1186 /* If (x, y) is at the edge of the panel then raise */
1187 static void motion_may_raise(Panel *panel, int x, int y)
1189 gboolean raise;
1191 if (panel->side == PANEL_TOP)
1192 raise = y == 0;
1193 else if (panel->side == PANEL_BOTTOM)
1194 raise = y == panel->window->allocation.height - 1;
1195 else if (panel->side == PANEL_LEFT)
1196 raise = x == 0;
1197 else
1198 raise = x == panel->window->allocation.width - 1;
1200 if (raise)
1201 gdk_window_raise(panel->window->window);
1204 static gint panel_motion_event(GtkWidget *widget,
1205 GdkEventMotion *event,
1206 Panel *panel)
1208 gint delta, new;
1209 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1211 motion_may_raise(panel, event->x, event->y);
1213 if (motion_state != MOTION_REPOSITION)
1214 return FALSE;
1216 if (horz)
1217 delta = event->x_root - drag_start_x;
1218 else
1219 delta = event->y_root - drag_start_y;
1221 new = slide_from_value - delta;
1222 new = CLAMP(new, 0, panel->adj->upper - panel->adj->page_size);
1224 gtk_adjustment_set_value(panel->adj, new);
1226 return TRUE;
1229 static gint icon_motion_event(GtkWidget *widget,
1230 GdkEventMotion *event,
1231 PanelIcon *pi)
1233 Panel *panel = pi->panel;
1234 GList *list, *me;
1235 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1236 int val;
1237 int dir = 0;
1239 if (motion_state == MOTION_READY_FOR_DND)
1241 if (dnd_motion_moved(event))
1242 start_drag(pi, event);
1243 return TRUE;
1245 else if (motion_state != MOTION_REPOSITION)
1246 return FALSE;
1248 list = gtk_container_get_children(GTK_CONTAINER(panel->before));
1249 list = g_list_append(list, NULL); /* The gap in the middle */
1250 list = g_list_concat(list,
1251 gtk_container_get_children(GTK_CONTAINER(panel->after)));
1252 me = g_list_find(list, widget);
1254 g_return_val_if_fail(me != NULL, TRUE);
1256 val = horz ? event->x_root : event->y_root;
1258 if (me->prev)
1260 GtkWidget *prev;
1261 int x, y;
1263 if (me->prev->data)
1264 prev = GTK_WIDGET(me->prev->data);
1265 else
1266 prev = panel->gap;
1268 gdk_window_get_origin(prev->window, &x, &y);
1270 if (val <= (horz ? x : y))
1271 dir = -1;
1274 if (dir == 0 && me->next)
1276 GtkWidget *next;
1277 int x, y, w, h;
1279 if (me->next->data)
1280 next = GTK_WIDGET(me->next->data);
1281 else
1282 next = panel->gap;
1284 gdk_window_get_origin(next->window, &x, &y);
1286 gdk_drawable_get_size(next->window, &w, &h);
1288 x += w;
1289 y += h;
1291 if (val >= (horz ? x : y))
1293 if (next == panel->gap)
1294 dir = +2;
1295 else
1296 dir = +1;
1300 if (dir)
1301 reposition_icon(pi, g_list_index(list, widget) + dir);
1303 return TRUE;
1306 static void reposition_icon_on_side(GtkWidget *side, GtkWidget *widget,
1307 int index)
1309 GList *list;
1311 list = gtk_container_get_children(GTK_CONTAINER(side));
1313 /* Want to move icon to the list in the given 'side'. Is it there
1314 * already?
1317 if (!g_list_find(list, widget))
1319 /* No, reparent */
1320 gtk_grab_remove(widget);
1321 gtk_widget_reparent(widget, side);
1322 dnd_motion_grab_pointer();
1323 gtk_grab_add(widget);
1326 gtk_box_reorder_child(GTK_BOX(side), widget, index);
1328 g_list_free(list);
1331 /* Move icon to this index in the complete widget list.
1332 * 0 makes the icon the left-most icon. The gap in the middle has
1333 * an index number, which allows you to specify that the icon should
1334 * go on the left or right side.
1336 static void reposition_icon(PanelIcon *pi, int index)
1338 Panel *panel = pi->panel;
1339 GtkWidget *widget = pi->widget;
1340 GList *list;
1341 int before_len;
1343 list = gtk_container_get_children(GTK_CONTAINER(panel->before));
1344 before_len = g_list_length(list);
1345 g_list_free(list);
1347 if (index <= before_len)
1348 reposition_icon_on_side(panel->before, widget, index);
1349 else
1350 reposition_icon_on_side(panel->after, widget,
1351 index - (before_len + 1));
1353 panel_save(panel);
1356 static void start_drag(PanelIcon *pi, GdkEventMotion *event)
1358 GtkWidget *widget = pi->widget;
1359 Icon *icon = (Icon *) pi;
1361 if (!icon->selected)
1363 if (event->state & GDK_BUTTON1_MASK)
1365 /* Select just this one */
1366 icon_select_only(icon);
1367 tmp_icon_selected = TRUE;
1369 else
1370 icon_set_selected(icon, TRUE);
1373 g_return_if_fail(icon_selection != NULL);
1375 if (icon_selection->next == NULL)
1376 drag_one_item(widget, event, icon->path, icon->item, NULL);
1377 else
1379 guchar *uri_list;
1381 uri_list = icon_create_uri_list();
1382 drag_selection(widget, event, uri_list);
1383 g_free(uri_list);
1387 static void applet_died(GtkWidget *socket)
1389 gboolean never_plugged;
1391 never_plugged = (!g_object_get_data(G_OBJECT(socket), "lost_plug"))
1392 && !GTK_SOCKET(socket)->plug_window;
1394 if (never_plugged)
1396 report_error(
1397 _("Applet quit without ever creating a widget!"));
1398 gtk_widget_destroy(socket);
1401 gtk_widget_unref(socket);
1404 static void socket_destroyed(GtkWidget *socket, GtkWidget *widget)
1406 g_object_set_data(G_OBJECT(socket), "lost_plug", "yes");
1408 gtk_widget_unref(socket);
1410 gtk_widget_destroy(widget); /* Remove from panel */
1412 if (!closing_panel)
1413 panel_save(g_object_get_data(G_OBJECT(socket), "panel"));
1416 /* Try to run this applet.
1417 * Cases:
1419 * - No executable AppletRun:
1420 * icon->socket == NULL (unchanged) on return.
1422 * Otherwise, create socket (setting icon->socket) and ref it twice.
1424 * - AppletRun quits without connecting a plug:
1425 * On child death lost_plug is unset and socket is empty.
1426 * Unref socket.
1427 * Report error and destroy widget (to 'socket destroyed').
1429 * - AppletRun quits while plug is in socket:
1430 * Unref socket once. Socket will be destroyed later.
1432 * - Socket is destroyed.
1433 * Set lost_plug = "yes" and remove widget from panel.
1434 * Unref socket.
1436 static void run_applet(PanelIcon *pi)
1438 GError *error = NULL;
1439 char *argv[3];
1440 gint pid;
1441 Icon *icon = (Icon *) pi;
1443 argv[0] = (char *) make_path(icon->path, "AppletRun");
1445 if (access(argv[0], X_OK) != 0)
1446 return;
1448 pi->socket = gtk_socket_new();
1450 gtk_container_add(GTK_CONTAINER(pi->widget), pi->socket);
1451 gtk_widget_show_all(pi->socket);
1452 gtk_widget_realize(pi->socket);
1455 gchar *pos;
1456 PanelSide side = pi->panel->side;
1458 /* Set a hint to let applets position their menus correctly */
1459 pos = g_strdup_printf("%s,%d",
1460 side == PANEL_TOP ? "Top" :
1461 side == PANEL_BOTTOM ? "Bottom" :
1462 side == PANEL_LEFT ? "Left" :
1463 "Right", MENU_MARGIN(side));
1464 gdk_property_change(pi->socket->window,
1465 gdk_atom_intern("_ROX_PANEL_MENU_POS", FALSE),
1466 gdk_atom_intern("STRING", FALSE),
1467 8, GDK_PROP_MODE_REPLACE,
1468 pos, strlen(pos));
1469 g_free(pos);
1472 g_object_set_data(G_OBJECT(pi->widget), "icon", pi);
1473 g_object_set_data(G_OBJECT(pi->socket), "panel", pi->panel);
1475 argv[1] = g_strdup_printf("%ld",
1476 GDK_WINDOW_XWINDOW(pi->socket->window));
1477 argv[2] = NULL;
1479 if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
1480 NULL, NULL, &pid, &error))
1482 delayed_error(_("Error running applet:\n%s"), error->message);
1483 g_error_free(error);
1484 gtk_widget_destroy(pi->socket);
1485 pi->socket = NULL;
1487 else
1489 gtk_widget_ref(pi->socket);
1490 on_child_death(pid, (CallbackFn) applet_died, pi->socket);
1492 gtk_widget_ref(pi->socket);
1493 g_signal_connect(pi->socket, "destroy",
1494 G_CALLBACK(socket_destroyed), pi->widget);
1497 g_free(argv[1]);
1500 static void panel_post_resize(GtkWidget *win, GtkRequisition *req, Panel *panel)
1502 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
1504 req->width = screen_width;
1505 req->height += EDGE_WIDTH;
1507 else
1509 int h = screen_height;
1511 if (current_panel[PANEL_TOP])
1513 GtkWidget *win = current_panel[PANEL_TOP]->window;
1514 h -= win->allocation.height;
1517 if (current_panel[PANEL_BOTTOM])
1519 GtkWidget *win = current_panel[PANEL_BOTTOM]->window;
1520 h -= win->allocation.height;
1523 req->height = h;
1524 req->width += EDGE_WIDTH;
1528 static void update_side(GtkWidget *side)
1530 GList *kids, *next;
1532 kids = gtk_container_get_children(GTK_CONTAINER(side));
1533 for (next = kids; next; next = next->next)
1535 PanelIcon *pi;
1536 pi = g_object_get_data(next->data, "icon");
1537 panel_icon_set_tip(pi);
1539 g_list_free(kids);
1542 /* Tips or style has changed -- update everything on this panel */
1543 static void panel_set_style(Panel *panel)
1545 update_side(panel->before);
1546 update_side(panel->after);
1547 gtk_widget_queue_resize(panel->window);
1550 static gboolean recreate_panels(char **names)
1552 int i;
1554 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
1556 if (names[i])
1558 panel_new(names[i], i);
1559 g_free(names[i]);
1563 g_free(names);
1565 return FALSE;
1568 static void panel_style_changed(void)
1570 int i;
1572 if (o_override_redirect.has_changed)
1574 gchar **names;
1576 names = g_new(char *, PANEL_NUMBER_OF_SIDES);
1578 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
1580 Panel *panel = current_panel[i];
1581 names[i] = panel ? g_strdup(panel->name) : NULL;
1582 panel_new(NULL, i);
1585 g_idle_add((GtkFunction) recreate_panels, names);
1588 if (o_panel_style.has_changed)
1590 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
1592 if (current_panel[i])
1593 panel_set_style(current_panel[i]);
1598 static gboolean draw_panel_edge(GtkWidget *widget, GdkEventExpose *event,
1599 Panel *panel)
1601 int x, y, width, height;
1603 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
1605 width = screen_width;
1606 height = EDGE_WIDTH;
1608 x = 0;
1609 if (panel->side == PANEL_BOTTOM)
1610 y = 0;
1611 else
1612 y = widget->allocation.height - EDGE_WIDTH;
1614 else
1616 width = EDGE_WIDTH;
1617 height = screen_height;
1619 y = 0;
1620 if (panel->side == PANEL_RIGHT)
1621 x = 0;
1622 else
1623 x = widget->allocation.width - EDGE_WIDTH;
1626 gdk_draw_rectangle(widget->window,
1627 widget->style->fg_gc[GTK_STATE_NORMAL], TRUE,
1628 x, y, width, height);
1630 return FALSE;
1633 static gpointer parent_class;
1635 static void panel_icon_destroy(Icon *icon)
1637 PanelIcon *pi = (PanelIcon *) icon;
1639 g_return_if_fail(pi->widget != NULL);
1641 gtk_widget_destroy(pi->widget);
1644 static void panel_remove_items(void)
1646 Panel *panel;
1648 g_return_if_fail(icon_selection != NULL);
1650 panel = ((PanelIcon *) icon_selection->data)->panel;
1652 while (icon_selection)
1653 icon_destroy((Icon *) icon_selection->data);
1655 panel_save(panel);
1658 static void panel_icon_redraw(Icon *icon)
1660 gtk_widget_queue_draw(PANEL_ICON(icon)->widget);
1663 static void panel_icon_update(Icon *icon)
1665 PanelIcon *pi = (PanelIcon *) icon;
1667 gtk_widget_queue_draw(pi->widget);
1668 gtk_label_set_text(GTK_LABEL(pi->label), icon->item->leafname);
1669 panel_icon_set_tip(pi);
1670 panel_save(pi->panel);
1673 /* The point of this is to clear the selection if the existing icons
1674 * aren't from the same panel...
1676 static gboolean panel_icon_same_group(Icon *icon, Icon *other)
1678 if (IS_PANEL_ICON(other))
1680 PanelIcon *a = (PanelIcon *) icon;
1681 PanelIcon *b = (PanelIcon *) other;
1683 return a->panel == b->panel;
1685 else
1686 return FALSE;
1689 static void panel_icon_class_init(gpointer gclass, gpointer data)
1691 IconClass *icon = (IconClass *) gclass;
1693 parent_class = g_type_class_peek_parent(gclass);
1695 icon->destroy = panel_icon_destroy;
1696 icon->redraw = panel_icon_redraw;
1697 icon->update = panel_icon_update;
1698 icon->remove_items = panel_remove_items;
1699 icon->same_group = panel_icon_same_group;
1700 icon->wink = panel_icon_wink;
1703 static void panel_icon_init(GTypeInstance *object, gpointer gclass)
1705 PanelIcon *pi = (PanelIcon *) object;
1707 pi->widget = NULL;
1708 pi->label = NULL;
1709 pi->socket = NULL;
1712 static GType panel_icon_get_type(void)
1714 static GType type = 0;
1716 if (!type)
1718 static const GTypeInfo info =
1720 sizeof (PanelIconClass),
1721 NULL, /* base_init */
1722 NULL, /* base_finalise */
1723 panel_icon_class_init,
1724 NULL, /* class_finalise */
1725 NULL, /* class_data */
1726 sizeof(PanelIcon),
1727 0, /* n_preallocs */
1728 panel_icon_init
1731 type = g_type_register_static(icon_get_type(),
1732 "PanelIcon", &info, 0);
1735 return type;
1738 static PanelIcon *panel_icon_new(Panel *panel,
1739 const char *pathname,
1740 const char *name)
1742 PanelIcon *pi;
1743 Icon *icon;
1745 pi = g_object_new(panel_icon_get_type(), NULL);
1746 icon = (Icon *) pi;
1748 icon_set_path(icon, pathname, name);
1749 pi->panel = panel;
1751 return pi;
1754 static gboolean panel_want_show_text(PanelIcon *pi)
1756 Icon *icon = (Icon *) pi;
1758 if (o_panel_style.int_value == SHOW_BOTH)
1759 return TRUE;
1760 if (o_panel_style.int_value == SHOW_ICON)
1761 return FALSE;
1763 if (icon->item->flags & ITEM_FLAG_APPDIR)
1764 return FALSE;
1766 return TRUE;
1769 static void panel_position_menu(GtkMenu *menu, gint *x, gint *y,
1770 gboolean *push_in, gpointer data)
1772 int *pos = (int *) data;
1773 GtkRequisition requisition;
1774 int margin = pos[2];
1776 gtk_widget_size_request(GTK_WIDGET(menu), &requisition);
1778 if (pos[0] == -1)
1779 *x = screen_width - margin - requisition.width;
1780 else if (pos[0] == -2)
1781 *x = margin;
1782 else
1783 *x = pos[0] - (requisition.width >> 2);
1785 if (pos[1] == -1)
1786 *y = screen_height - margin - requisition.height;
1787 else if (pos[1] == -2)
1788 *y = margin;
1789 else
1790 *y = pos[1] - (requisition.height >> 2);
1792 *x = CLAMP(*x, 0, screen_width - requisition.width);
1793 *y = CLAMP(*y, 0, screen_height - requisition.height);
1795 *push_in = FALSE;
1798 static void panel_show_menu(GdkEventButton *event, PanelIcon *pi, Panel *panel)
1800 PanelSide side = panel->side;
1801 int pos[3];
1803 pos[0] = event->x_root;
1804 pos[1] = event->y_root;
1805 pos[2] = MENU_MARGIN(side);
1807 icon_prepare_menu((Icon *) pi, FALSE);
1809 if (side == PANEL_LEFT)
1810 pos[0] = -2;
1811 else if (side == PANEL_RIGHT)
1812 pos[0] = -1;
1814 if (side == PANEL_TOP)
1815 pos[1] = -2;
1816 else if (side == PANEL_BOTTOM)
1817 pos[1] = -1;
1819 gtk_menu_popup(GTK_MENU(icon_menu), NULL, NULL,
1820 panel_position_menu,
1821 (gpointer) pos, event->button, event->time);
1824 /* Note: also called from icon handler */
1825 static gboolean panel_drag_motion(GtkWidget *widget,
1826 GdkDragContext *context,
1827 gint x,
1828 gint y,
1829 guint time,
1830 Panel *panel)
1832 int panel_x, panel_y;
1834 gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
1836 motion_may_raise(panel, panel_x, panel_y);
1837 gdk_drag_status(context, 0, time);
1838 return TRUE;
1841 /* Note: also called from icon handler */
1842 static void panel_drag_leave(GtkWidget *widget,
1843 GdkDragContext *context,
1844 guint32 time,
1845 Panel *panel)
1847 GdkWindow *pinboard, *window;
1848 GtkAllocation *alloc = &panel->window->allocation;
1849 int x, y;
1851 window = panel->window->window;
1852 gdk_window_get_pointer(window, &x, &y, NULL);
1853 if (x < 0 || y < 0 || x > alloc->width || y > alloc->height)
1855 pinboard = pinboard_get_window();
1856 window_put_just_above(panel->window->window, pinboard);