r1996: Cope slightly better with invalid filenames in various places (reported by
[rox-filer.git] / ROX-Filer / src / panel.c
blobb334863dcec36eca522e1cd32a218a7b41b764bc
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2002, 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 drag_motion(GtkWidget *widget,
131 GdkDragContext *context,
132 gint x,
133 gint y,
134 guint time,
135 PanelIcon *pi);
136 static void drag_leave(GtkWidget *widget,
137 GdkDragContext *context,
138 guint32 time,
139 Icon *icon);
140 static GtkWidget *make_insert_frame(Panel *panel);
141 static gboolean enter_icon(GtkWidget *widget,
142 GdkEventCrossing *event,
143 Icon *icon);
144 static gint icon_motion_event(GtkWidget *widget,
145 GdkEventMotion *event,
146 PanelIcon *pi);
147 static gint panel_leave_event(GtkWidget *widget,
148 GdkEventCrossing *event,
149 Panel *panel);
150 static gint panel_motion_event(GtkWidget *widget,
151 GdkEventMotion *event,
152 Panel *panel);
153 static void reposition_icon(PanelIcon *pi, int index);
154 static void start_drag(PanelIcon *pi, GdkEventMotion *event);
155 static void drag_end(GtkWidget *widget,
156 GdkDragContext *context,
157 Icon *icon);
158 static void perform_action(Panel *panel,
159 PanelIcon *pi,
160 GdkEventButton *event);
161 static void run_applet(PanelIcon *pi);
162 static void size_request(GtkWidget *widget, GtkRequisition *req, PanelIcon *pi);
163 static void panel_load_from_xml(Panel *panel, xmlDocPtr doc);
164 static gboolean draw_panel_edge(GtkWidget *widget, GdkEventExpose *event,
165 Panel *panel);
166 static PanelIcon *panel_icon_new(Panel *panel,
167 const char *pathname,
168 const char *name);
169 static GType panel_icon_get_type(void);
170 static gboolean panel_want_show_text(PanelIcon *pi);
171 static void panel_show_menu(GdkEventButton *event, PanelIcon *pi, Panel *panel);
172 static void panel_style_changed(void);
175 static GtkWidget *dnd_highlight = NULL; /* (stops flickering) */
177 /* When sliding the panel, records where the panel was before */
178 static gint slide_from_value = 0;
180 #define SHOW_BOTH 0
181 #define SHOW_APPS_SMALL 1
182 #define SHOW_ICON 2
183 static Option o_panel_style;
185 static int closing_panel = 0; /* Don't panel_save; destroying! */
187 /****************************************************************
188 * EXTERNAL INTERFACE *
189 ****************************************************************/
191 void panel_init(void)
193 option_add_int(&o_panel_style, "panel_style", SHOW_APPS_SMALL);
195 option_add_notify(panel_style_changed);
198 /* 'name' may be NULL or "" to remove the panel */
199 Panel *panel_new(const gchar *name, PanelSide side)
201 guchar *load_path;
202 Panel *panel;
203 GtkWidget *vp, *box, *frame, *align;
205 g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, NULL);
206 g_return_val_if_fail(loading_panel == NULL, NULL);
208 if (name && *name == '\0')
209 name = NULL;
211 if (current_panel[side])
213 if (name)
214 number_of_windows++;
215 closing_panel++;
216 gtk_widget_destroy(current_panel[side]->window);
217 closing_panel--;
218 if (name)
219 number_of_windows--;
222 if (name == NULL || *name == '\0')
223 return NULL;
225 panel = g_new(Panel, 1);
226 panel->name = g_strdup(name);
227 panel->side = side;
228 panel->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
229 gtk_window_set_resizable(GTK_WINDOW(panel->window), FALSE);
230 gtk_window_set_wmclass(GTK_WINDOW(panel->window), "ROX-Panel", PROJECT);
231 gtk_widget_set_name(panel->window, "rox-panel");
232 gtk_widget_set_events(panel->window,
233 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
234 GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
236 g_signal_connect(panel->window, "delete-event",
237 G_CALLBACK(panel_delete), panel);
238 g_signal_connect(panel->window, "destroy",
239 G_CALLBACK(panel_destroyed), panel);
240 g_signal_connect(panel->window, "button_press_event",
241 G_CALLBACK(panel_button_press), panel);
242 g_signal_connect(panel->window, "button_release_event",
243 G_CALLBACK(panel_button_release), panel);
244 g_signal_connect(panel->window, "motion-notify-event",
245 G_CALLBACK(panel_motion_event), panel);
246 g_signal_connect(panel->window, "leave-notify-event",
247 G_CALLBACK(panel_leave_event), panel);
249 if (strchr(name, '/'))
250 load_path = g_strdup(name);
251 else
253 guchar *leaf;
255 leaf = g_strconcat("pan_", name, NULL);
256 load_path = choices_find_path_load(leaf, PROJECT);
257 g_free(leaf);
260 if (panel->side == PANEL_RIGHT)
261 align = gtk_alignment_new(1.0, 0.0, 0.0, 1.0);
262 else if (panel->side == PANEL_BOTTOM)
263 align = gtk_alignment_new(0.0, 1.0, 1.0, 0.0);
264 else if (panel->side == PANEL_TOP)
265 align = gtk_alignment_new(0.0, 0.0, 1.0, 0.0);
266 else
267 align = gtk_alignment_new(0.0, 0.0, 0.0, 1.0);
269 gtk_container_add(GTK_CONTAINER(panel->window), align);
271 vp = gtk_viewport_new(NULL, NULL);
272 gtk_container_set_resize_mode(GTK_CONTAINER(vp), GTK_RESIZE_PARENT);
273 gtk_viewport_set_shadow_type(GTK_VIEWPORT(vp), GTK_SHADOW_NONE);
274 gtk_container_add(GTK_CONTAINER(align), vp);
276 g_signal_connect(align, "expose-event",
277 G_CALLBACK(draw_panel_edge), panel);
279 if (side == PANEL_TOP || side == PANEL_BOTTOM)
281 panel->adj = gtk_viewport_get_hadjustment(GTK_VIEWPORT(vp));
282 box = gtk_hbox_new(FALSE, 0);
283 panel->before = gtk_hbox_new(FALSE, 0);
284 panel->after = gtk_hbox_new(FALSE, 0);
286 else
288 panel->adj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(vp));
289 box = gtk_vbox_new(FALSE, 0);
290 panel->before = gtk_vbox_new(FALSE, 0);
291 panel->after = gtk_vbox_new(FALSE, 0);
294 gtk_container_add(GTK_CONTAINER(vp), box);
295 gtk_box_pack_start(GTK_BOX(box), panel->before, FALSE, TRUE, 0);
296 gtk_box_pack_end(GTK_BOX(box), panel->after, FALSE, TRUE, 0);
298 frame = make_insert_frame(panel);
299 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
301 /* This is used so that we can find the middle easily! */
302 panel->gap = gtk_event_box_new();
303 gtk_box_pack_start(GTK_BOX(box), panel->gap, FALSE, FALSE, 0);
305 frame = make_insert_frame(panel);
306 g_object_set_data(G_OBJECT(frame), "after", "yes");
307 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
309 gtk_widget_realize(panel->window);
310 make_panel_window(panel->window);
312 gtk_widget_show_all(align);
314 loading_panel = panel;
315 if (load_path && access(load_path, F_OK) == 0)
317 xmlDocPtr doc;
318 doc = xmlParseFile(load_path);
319 if (doc)
321 panel_load_from_xml(panel, doc);
322 xmlFreeDoc(doc);
324 else
326 parse_file(load_path, pan_from_file);
327 info_message(_("Your old panel file has been "
328 "converted to the new XML format."));
329 panel_save(panel);
332 else
334 /* Don't scare users with an empty panel... */
335 guchar *apps;
337 panel_add_item(panel, "~", "Home", FALSE, NULL);
339 apps = pathdup(make_path(app_dir, "..")->str);
340 if (apps)
342 panel_add_item(panel, apps, "Apps", FALSE, NULL);
343 g_free(apps);
346 loading_panel = NULL;
347 g_free(load_path);
349 current_panel[side] = panel;
351 gtk_widget_queue_resize(box);
352 g_signal_connect(panel->window, "size-request",
353 G_CALLBACK(panel_post_resize), panel);
354 g_signal_connect(panel->window, "size-allocate",
355 G_CALLBACK(reposition_panel), panel);
357 /* Stop windows from maximising over us completely, so that the
358 * auto-raise stuff works...
361 guint32 wm_strut[] = {0, 0, 0, 0};
363 if (panel->side == PANEL_LEFT)
364 wm_strut[0] = 2;
365 else if (panel->side == PANEL_RIGHT)
366 wm_strut[1] = 2;
367 else if (panel->side == PANEL_TOP)
368 wm_strut[2] = 2;
369 else
370 wm_strut[3] = 2;
372 gdk_property_change(panel->window->window,
373 gdk_atom_intern("_NET_WM_STRUT", FALSE),
374 gdk_atom_intern("CARDINAL", FALSE),
375 32, GDK_PROP_MODE_REPLACE,
376 (gchar *) &wm_strut, 4);
379 number_of_windows++;
380 gdk_window_lower(panel->window->window);
381 gtk_widget_show(panel->window);
384 GdkWindow *pinboard;
386 pinboard = pinboard_get_window();
388 window_put_just_above(panel->window->window, pinboard);
391 return panel;
394 /* Externally visible function to add an item to a panel */
395 gboolean panel_add(PanelSide side,
396 const gchar *path, const gchar *label, gboolean after)
398 g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, FALSE);
400 g_return_val_if_fail(current_panel[side] != NULL, FALSE);
402 panel_add_item(current_panel[side], path, label, after, NULL);
404 return TRUE;
407 /* Add the area covered by the panels to the region */
408 void panel_mark_used(GdkRegion *used)
410 int i;
412 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
414 Panel *panel = current_panel[i];
415 GdkRectangle rect;
417 if (!panel)
418 continue;
420 gdk_window_get_root_origin(panel->window->window,
421 &rect.x, &rect.y);
422 rect.width = panel->window->allocation.width;
423 rect.height = panel->window->allocation.height;
425 gdk_region_union_with_rect(used, &rect);
429 /****************************************************************
430 * INTERNAL FUNCTIONS *
431 ****************************************************************/
433 /* User has tried to close the panel via the window manager - confirm */
434 static int panel_delete(GtkWidget *widget, GdkEvent *event, Panel *panel)
436 return get_choice(_("Close panel?"),
437 _("You have tried to close a panel via the window "
438 "manager - I usually find that this is accidental... "
439 "really close?"),
440 2, _("Cancel"), _("Remove")) != 1;
443 static void panel_destroyed(GtkWidget *widget, Panel *panel)
445 if (current_panel[panel->side] == panel)
446 current_panel[panel->side] = NULL;
448 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
450 if (current_panel[PANEL_RIGHT])
451 gtk_widget_queue_resize(
452 current_panel[PANEL_RIGHT]->window);
453 if (current_panel[PANEL_LEFT])
454 gtk_widget_queue_resize(
455 current_panel[PANEL_LEFT]->window);
458 g_free(panel->name);
459 g_free(panel);
461 one_less_window();
464 static void panel_load_side(Panel *panel, xmlNodePtr side, gboolean after)
466 xmlNodePtr node;
467 char *label, *path, *shortcut;
469 for (node = side->xmlChildrenNode; node; node = node->next)
471 if (node->type != XML_ELEMENT_NODE)
472 continue;
473 if (strcmp(node->name, "icon") != 0)
474 continue;
476 label = xmlGetProp(node, "label");
477 if (!label)
478 label = g_strdup("<missing label>");
479 path = xmlNodeGetContent(node);
480 if (!path)
481 path = g_strdup("<missing path>");
482 shortcut = xmlGetProp(node, "shortcut");
484 panel_add_item(panel, path, label, after, shortcut);
486 g_free(path);
487 g_free(label);
488 g_free(shortcut);
492 /* Create one panel icon for each icon in the doc */
493 static void panel_load_from_xml(Panel *panel, xmlDocPtr doc)
495 xmlNodePtr root;
497 root = xmlDocGetRootElement(doc);
498 panel_load_side(panel, get_subnode(root, NULL, "start"), FALSE);
499 panel_load_side(panel, get_subnode(root, NULL, "end"), TRUE);
502 /* Called for each line in the config file while loading a new panel */
503 static const char *pan_from_file(gchar *line)
505 gchar *sep, *leaf;
507 g_return_val_if_fail(line != NULL, NULL);
508 g_return_val_if_fail(loading_panel != NULL, NULL);
510 if (*line == '\0')
511 return NULL;
513 sep = strpbrk(line, "<>");
514 if (!sep)
515 return _("Missing < or > in panel config file");
517 if (sep != line)
518 leaf = g_strndup(line, sep - line);
519 else
520 leaf = NULL;
522 panel_add_item(loading_panel, sep + 1, leaf, sep[0] == '>', NULL);
524 g_free(leaf);
526 return NULL;
529 static gboolean icon_pointer_in(GtkWidget *widget,
530 GdkEventCrossing *event,
531 Icon *icon)
533 gtk_widget_set_state(widget, GTK_STATE_PRELIGHT);
535 return 0;
538 static gboolean icon_pointer_out(GtkWidget *widget,
539 GdkEventCrossing *event,
540 Icon *icon)
542 gtk_widget_set_state(widget, GTK_STATE_NORMAL);
544 return 0;
547 static void panel_icon_destroyed(PanelIcon *pi)
549 g_return_if_fail(pi->widget != NULL);
551 pi->widget = NULL;
553 g_object_unref(pi);
556 /* Set the tooltip AND hide/show the label */
557 static void panel_icon_set_tip(PanelIcon *pi)
559 XMLwrapper *ai;
560 xmlNode *node;
561 Icon *icon = (Icon *) pi;
563 g_return_if_fail(pi != NULL);
565 if (pi->label)
567 if (panel_want_show_text(pi))
568 gtk_widget_show(pi->label);
569 else
570 gtk_widget_hide(pi->label);
573 if (pi->socket)
574 ai = NULL;
575 else
576 ai = appinfo_get(icon->path, icon->item);
578 if (ai && ((node = xml_get_section(ai, NULL, "Summary"))))
580 guchar *str;
581 str = xmlNodeListGetString(node->doc,
582 node->xmlChildrenNode, 1);
583 if (str)
585 gtk_tooltips_set_tip(tooltips, pi->widget, str, NULL);
586 g_free(str);
589 else if ((!panel_want_show_text(pi)) && !pi->socket)
591 gtk_tooltips_set_tip(tooltips, pi->widget,
592 icon->item->leafname, NULL);
594 else
595 gtk_tooltips_set_tip(tooltips, pi->widget, NULL, NULL);
597 if (ai)
598 g_object_unref(ai);
601 /* Add an icon with this path to the panel. If after is TRUE then the
602 * icon is added to the right/bottom end of the panel.
604 * If name is NULL a suitable name is taken from path.
606 static void panel_add_item(Panel *panel,
607 const gchar *path,
608 const gchar *name,
609 gboolean after,
610 const gchar *shortcut)
612 GtkWidget *widget;
613 PanelIcon *pi;
614 Icon *icon;
616 g_return_if_fail(panel != NULL);
617 g_return_if_fail(path != NULL);
619 widget = gtk_event_box_new();
620 gtk_widget_set_events(widget,
621 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
622 GDK_BUTTON3_MOTION_MASK |
623 GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
624 GDK_BUTTON_RELEASE_MASK);
626 gtk_box_pack_start(GTK_BOX(after ? panel->after : panel->before),
627 widget, FALSE, TRUE, 0);
628 if (after)
629 gtk_box_reorder_child(GTK_BOX(panel->after), widget, 0);
631 gtk_widget_realize(widget);
633 pi = panel_icon_new(panel, path, name);
634 icon = (Icon *) pi;
636 /* Widget takes the initial ref of Icon */
637 g_object_set_data(G_OBJECT(widget), "icon", pi);
639 pi->widget = widget;
640 g_object_ref(widget);
642 gtk_widget_set_name(pi->widget, "panel-icon");
644 g_signal_connect_swapped(widget, "destroy",
645 G_CALLBACK(panel_icon_destroyed), pi);
647 if (icon->item->base_type == TYPE_DIRECTORY)
648 run_applet(pi);
650 g_signal_connect(widget, "button_release_event",
651 G_CALLBACK(icon_button_release), pi);
652 g_signal_connect(widget, "button_press_event",
653 G_CALLBACK(icon_button_press), pi);
654 g_signal_connect(widget, "motion-notify-event",
655 G_CALLBACK(icon_motion_event), pi);
656 g_signal_connect(widget, "enter-notify-event",
657 G_CALLBACK(icon_pointer_in), pi);
658 g_signal_connect(widget, "leave-notify-event",
659 G_CALLBACK(icon_pointer_out), pi);
661 if (!pi->socket)
663 g_signal_connect(widget, "enter-notify-event",
664 G_CALLBACK(enter_icon), pi);
665 g_signal_connect_after(widget, "expose_event",
666 G_CALLBACK(expose_icon), pi);
667 g_signal_connect(widget, "drag_data_get",
668 G_CALLBACK(drag_data_get), NULL);
670 g_signal_connect(widget, "size_request",
671 G_CALLBACK(size_request), pi);
673 drag_set_panel_dest(pi);
675 pi->label = gtk_label_new(icon->item->leafname);
676 gtk_container_add(GTK_CONTAINER(pi->widget), pi->label);
677 gtk_misc_set_alignment(GTK_MISC(pi->label), 0.5, 1);
678 gtk_misc_set_padding(GTK_MISC(pi->label), 1, 2);
681 icon_set_shortcut(icon, shortcut);
683 if (!loading_panel)
684 panel_save(panel);
686 panel_icon_set_tip(pi);
687 gtk_widget_show(widget);
690 /* Called when Gtk+ wants to know how much space an icon needs.
691 * 'req' is already big enough for the label, if shown.
693 static void size_request(GtkWidget *widget, GtkRequisition *req, PanelIcon *pi)
695 int im_width, im_height;
696 Icon *icon = (Icon *) pi;
698 im_width = icon->item->image->width;
699 im_height = MIN(icon->item->image->height, ICON_HEIGHT);
701 req->height += im_height;
702 req->width = MAX(req->width, im_width);
704 if (pi->panel->side == PANEL_LEFT || pi->panel->side == PANEL_RIGHT)
705 req->height += PANEL_ICON_SPACING;
706 else
707 req->width += PANEL_ICON_SPACING;
710 static gint expose_icon(GtkWidget *widget,
711 GdkEventExpose *event,
712 PanelIcon *pi)
714 return draw_icon(widget, &event->area, pi);
717 static gint draw_icon(GtkWidget *widget, GdkRectangle *badarea, PanelIcon *pi)
719 GdkRectangle area;
720 int width, height;
721 Icon *icon = (Icon *) pi;
723 gdk_drawable_get_size(widget->window, &width, &height);
725 area.x = 0;
726 area.width = width;
727 area.height = icon->item->image->height;
729 if (panel_want_show_text(pi))
731 int text_height = pi->label->requisition.height;
733 area.y = height - text_height - area.height;
735 draw_large_icon(widget, &area, icon->item,
736 icon->item->image, icon->selected);
738 else
740 area.y = (height - area.height) >> 1;
741 draw_large_icon(widget, &area, icon->item,
742 icon->item->image, icon->selected);
745 return FALSE;
748 static void panel_icon_wink(Icon *icon)
750 PanelIcon *pi = (PanelIcon *) icon;
752 wink_widget(pi->widget);
755 /* icon may be NULL if the event is on the background */
756 static void perform_action(Panel *panel, PanelIcon *pi, GdkEventButton *event)
758 BindAction action;
759 Icon *icon = (Icon *) pi;
761 action = bind_lookup_bev(icon ? BIND_PANEL_ICON : BIND_PANEL, event);
763 if (pi && pi->socket)
764 if (action != ACT_POPUP_MENU && action != ACT_MOVE_ICON)
765 return;
767 switch (action)
769 case ACT_OPEN_ITEM:
770 dnd_motion_ungrab();
771 wink_widget(pi->widget);
772 run_diritem(icon->path, icon->item, NULL, NULL, FALSE);
773 break;
774 case ACT_EDIT_ITEM:
775 dnd_motion_ungrab();
776 wink_widget(pi->widget);
777 run_diritem(icon->path, icon->item, NULL, NULL, TRUE);
778 break;
779 case ACT_POPUP_MENU:
780 dnd_motion_ungrab();
781 panel_show_menu(event, pi, panel);
782 break;
783 case ACT_MOVE_ICON:
784 dnd_motion_start(MOTION_REPOSITION);
785 break;
786 case ACT_PRIME_AND_SELECT:
787 if (!icon->selected)
788 icon_select_only(icon);
789 dnd_motion_start(MOTION_READY_FOR_DND);
790 break;
791 case ACT_PRIME_AND_TOGGLE:
792 icon_set_selected(icon, !icon->selected);
793 dnd_motion_start(MOTION_READY_FOR_DND);
794 break;
795 case ACT_PRIME_FOR_DND:
796 dnd_motion_start(MOTION_READY_FOR_DND);
797 break;
798 case ACT_TOGGLE_SELECTED:
799 icon_set_selected(icon, !icon->selected);
800 break;
801 case ACT_SELECT_EXCL:
802 icon_set_selected(icon, TRUE);
803 break;
804 case ACT_SLIDE_CLEAR_PANEL:
805 icon_select_only(NULL);
806 /* (no break) */
807 case ACT_SLIDE_PANEL:
808 dnd_motion_grab_pointer();
809 slide_from_value = panel->adj->value;
810 dnd_motion_start(MOTION_REPOSITION);
811 break;
812 case ACT_IGNORE:
813 break;
814 case ACT_CLEAR_SELECTION:
815 icon_select_only(NULL);
816 break;
817 default:
818 g_warning("Unsupported action : %d\n", action);
819 break;
823 static gint panel_button_release(GtkWidget *widget,
824 GdkEventButton *event,
825 Panel *panel)
827 if (dnd_motion_release(event))
828 return TRUE;
830 perform_action(panel, NULL, event);
832 return TRUE;
835 static gint panel_button_press(GtkWidget *widget,
836 GdkEventButton *event,
837 Panel *panel)
839 if (dnd_motion_press(panel->window, event))
840 perform_action(panel, NULL, event);
842 return TRUE;
845 static gint icon_button_release(GtkWidget *widget,
846 GdkEventButton *event,
847 PanelIcon *pi)
849 if (pi->socket && event->button == 1)
850 return FALSE; /* Restart button */
852 if (dnd_motion_release(event))
853 return TRUE;
855 perform_action(pi->panel, pi, event);
857 return TRUE;
860 static gint icon_button_press(GtkWidget *widget,
861 GdkEventButton *event,
862 PanelIcon *pi)
864 if (pi->socket && event->button == 1)
865 return FALSE; /* Restart button */
867 if (dnd_motion_press(widget, event))
868 perform_action(pi->panel, pi, event);
870 return TRUE;
873 static void reposition_panel(GtkWidget *window,
874 GtkAllocation *alloc, Panel *panel)
876 int x = 0, y = 0;
877 PanelSide side = panel->side;
879 if (side == PANEL_LEFT || side == PANEL_RIGHT)
881 if (side == PANEL_RIGHT)
882 x = screen_width - alloc->width;
884 if (current_panel[PANEL_TOP])
886 GtkWidget *win = current_panel[PANEL_TOP]->window;
887 y += win->allocation.height;
891 if (side == PANEL_BOTTOM)
892 y = screen_height - alloc->height;
894 gtk_window_move(GTK_WINDOW(panel->window), x, y);
895 gdk_window_move(panel->window->window, x, y);
897 if (side == PANEL_BOTTOM || side == PANEL_TOP)
899 if (current_panel[PANEL_RIGHT])
900 gtk_widget_queue_resize(
901 current_panel[PANEL_RIGHT]->window);
902 if (current_panel[PANEL_LEFT])
903 gtk_widget_queue_resize(
904 current_panel[PANEL_LEFT]->window);
908 /* Same as drag_set_dest(), but for panel icons */
909 static void drag_set_panel_dest(PanelIcon *pi)
911 GtkWidget *obj = pi->widget;
913 make_drop_target(pi->widget, 0);
915 g_signal_connect(obj, "drag_motion", G_CALLBACK(drag_motion), pi);
916 g_signal_connect(obj, "drag_leave", G_CALLBACK(drag_leave), pi);
917 g_signal_connect(obj, "drag_end", G_CALLBACK(drag_end), pi);
920 static gboolean drag_motion(GtkWidget *widget,
921 GdkDragContext *context,
922 gint x,
923 gint y,
924 guint time,
925 PanelIcon *pi)
927 GdkDragAction action = context->suggested_action;
928 const char *type = NULL;
929 Icon *icon = (Icon *) pi;
930 DirItem *item = icon->item;
932 if (icon->selected)
933 goto out; /* Can't drag a selection to itself */
935 type = dnd_motion_item(context, &item);
937 if (!item)
938 type = NULL;
939 out:
940 /* We actually must pretend to accept the drop, even if the
941 * directory isn't writeable, so that the spring-opening
942 * thing works.
945 /* Don't allow drops to non-writeable directories */
946 if (o_dnd_spring_open.int_value == FALSE &&
947 type == drop_dest_dir &&
948 access(icon->path, W_OK) != 0)
950 type = NULL;
953 g_dataset_set_data(context, "drop_dest_type", (gpointer) type);
954 if (type)
956 gdk_drag_status(context, action, time);
957 g_dataset_set_data_full(context, "drop_dest_path",
958 g_strdup(icon->path), g_free);
959 if (type == drop_dest_dir)
960 dnd_spring_load(context, NULL);
962 if (dnd_highlight && dnd_highlight != pi->widget)
964 gtk_drag_unhighlight(dnd_highlight);
965 dnd_highlight = NULL;
968 if (dnd_highlight == NULL)
970 gtk_drag_highlight(pi->widget);
971 dnd_highlight = pi->widget;
975 return type != NULL;
979 static void add_uri_list(GtkWidget *widget,
980 GdkDragContext *context,
981 gint x,
982 gint y,
983 GtkSelectionData *selection_data,
984 guint info,
985 guint32 time,
986 Panel *panel)
988 gboolean after = FALSE;
989 GList *uris, *next;
991 if (!selection_data->data)
992 return;
994 g_return_if_fail(selection_data->data[selection_data->length] == '\0');
996 if (g_object_get_data(G_OBJECT(widget), "after"))
997 after = TRUE;
999 uris = uri_list_to_glist(selection_data->data);
1001 for (next = uris; next; next = next->next)
1003 const guchar *path;
1005 path = get_local_path((guchar *) next->data);
1007 if (path)
1008 panel_add_item(panel, path, NULL, after, NULL);
1011 g_list_free(uris);
1014 static void drag_end(GtkWidget *widget,
1015 GdkDragContext *context,
1016 Icon *icon)
1018 if (tmp_icon_selected)
1020 icon_select_only(NULL);
1021 tmp_icon_selected = FALSE;
1025 static void drag_leave(GtkWidget *widget,
1026 GdkDragContext *context,
1027 guint32 time,
1028 Icon *icon)
1030 if (dnd_highlight && dnd_highlight == widget)
1032 gtk_drag_unhighlight(dnd_highlight);
1033 dnd_highlight = NULL;
1036 dnd_spring_abort();
1039 /* Create XML icon nodes for these widgets.
1040 * Always frees the widgets list.
1042 static void make_widgets(xmlNodePtr side, GList *widgets)
1044 GList *next;
1046 for (next = widgets; next; next = next->next)
1048 Icon *icon;
1049 xmlNodePtr tree;
1051 icon = g_object_get_data(G_OBJECT(next->data), "icon");
1053 if (!icon)
1055 g_warning("Can't find Icon from widget\n");
1056 continue;
1059 tree = xmlNewTextChild(side, NULL, "icon", icon->src_path);
1061 xmlSetProp(tree, "label", icon->item->leafname);
1062 if (icon->shortcut)
1063 xmlSetProp(tree, "shortcut", icon->shortcut);
1066 if (widgets)
1067 g_list_free(widgets);
1070 void panel_save(Panel *panel)
1072 xmlDocPtr doc;
1073 xmlNodePtr root;
1074 guchar *save = NULL;
1075 guchar *save_new = NULL;
1077 g_return_if_fail(panel != NULL);
1079 if (strchr(panel->name, '/'))
1080 save = g_strdup(panel->name);
1081 else
1083 guchar *leaf;
1085 leaf = g_strconcat("pan_", panel->name, NULL);
1086 save = choices_find_path_save(leaf, PROJECT, TRUE);
1087 g_free(leaf);
1090 if (!save)
1091 return;
1093 doc = xmlNewDoc("1.0");
1094 xmlDocSetRootElement(doc, xmlNewDocNode(doc, NULL, "panel", NULL));
1096 root = xmlDocGetRootElement(doc);
1097 make_widgets(xmlNewChild(root, NULL, "start", NULL),
1098 gtk_container_get_children(GTK_CONTAINER(panel->before)));
1100 make_widgets(xmlNewChild(root, NULL, "end", NULL),
1101 g_list_reverse(gtk_container_get_children(
1102 GTK_CONTAINER(panel->after))));
1104 save_new = g_strconcat(save, ".new", NULL);
1105 if (save_xml_file(doc, save_new) || rename(save_new, save))
1106 delayed_error(_("Error saving panel %s: %s"),
1107 save, g_strerror(errno));
1108 g_free(save_new);
1110 g_free(save);
1111 if (doc)
1112 xmlFreeDoc(doc);
1115 /* Create a frame widget which can be used to add icons to the panel */
1116 static GtkWidget *make_insert_frame(Panel *panel)
1118 GtkWidget *frame;
1119 GtkTargetEntry target_table[] = {
1120 {"text/uri-list", 0, TARGET_URI_LIST},
1123 frame = gtk_frame_new(NULL);
1124 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
1125 gtk_widget_set_size_request(frame, 16, 16);
1127 g_signal_connect(frame, "drag-data-received",
1128 G_CALLBACK(add_uri_list), panel);
1129 gtk_drag_dest_set(frame,
1130 GTK_DEST_DEFAULT_ALL,
1131 target_table,
1132 sizeof(target_table) / sizeof(*target_table),
1133 GDK_ACTION_COPY);
1135 return frame;
1138 static gboolean enter_icon(GtkWidget *widget,
1139 GdkEventCrossing *event,
1140 Icon *icon)
1142 icon_may_update(icon);
1144 return FALSE;
1147 static gint panel_leave_event(GtkWidget *widget,
1148 GdkEventCrossing *event,
1149 Panel *panel)
1151 GdkWindow *pinboard;
1153 pinboard = pinboard_get_window();
1154 window_put_just_above(panel->window->window, pinboard);
1156 return FALSE;
1159 static gint panel_motion_event(GtkWidget *widget,
1160 GdkEventMotion *event,
1161 Panel *panel)
1163 gint delta, new;
1164 gboolean raise;
1165 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1167 if (panel->side == PANEL_TOP)
1168 raise = event->y == 0;
1169 else if (panel->side == PANEL_BOTTOM)
1170 raise = event->y == panel->window->allocation.height - 1;
1171 else if (panel->side == PANEL_LEFT)
1172 raise = event->x == 0;
1173 else
1174 raise = event->x == panel->window->allocation.width - 1;
1176 if (raise)
1177 gdk_window_raise(panel->window->window);
1179 if (motion_state != MOTION_REPOSITION)
1180 return FALSE;
1182 if (horz)
1183 delta = event->x_root - drag_start_x;
1184 else
1185 delta = event->y_root - drag_start_y;
1187 new = slide_from_value - delta;
1188 new = CLAMP(new, 0, panel->adj->upper - panel->adj->page_size);
1190 gtk_adjustment_set_value(panel->adj, new);
1192 return TRUE;
1195 static gint icon_motion_event(GtkWidget *widget,
1196 GdkEventMotion *event,
1197 PanelIcon *pi)
1199 Panel *panel = pi->panel;
1200 GList *list, *me;
1201 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1202 int val;
1203 int dir = 0;
1205 if (motion_state == MOTION_READY_FOR_DND)
1207 if (dnd_motion_moved(event))
1208 start_drag(pi, event);
1209 return TRUE;
1211 else if (motion_state != MOTION_REPOSITION)
1212 return FALSE;
1214 list = gtk_container_get_children(GTK_CONTAINER(panel->before));
1215 list = g_list_append(list, NULL); /* The gap in the middle */
1216 list = g_list_concat(list,
1217 gtk_container_get_children(GTK_CONTAINER(panel->after)));
1218 me = g_list_find(list, widget);
1220 g_return_val_if_fail(me != NULL, TRUE);
1222 val = horz ? event->x_root : event->y_root;
1224 if (me->prev)
1226 GtkWidget *prev;
1227 int x, y;
1229 if (me->prev->data)
1230 prev = GTK_WIDGET(me->prev->data);
1231 else
1232 prev = panel->gap;
1234 gdk_window_get_origin(prev->window, &x, &y);
1236 if (val <= (horz ? x : y))
1237 dir = -1;
1240 if (dir == 0 && me->next)
1242 GtkWidget *next;
1243 int x, y, w, h;
1245 if (me->next->data)
1246 next = GTK_WIDGET(me->next->data);
1247 else
1248 next = panel->gap;
1250 gdk_window_get_origin(next->window, &x, &y);
1252 gdk_drawable_get_size(next->window, &w, &h);
1254 x += w;
1255 y += h;
1257 if (val >= (horz ? x : y))
1259 if (next == panel->gap)
1260 dir = +2;
1261 else
1262 dir = +1;
1266 if (dir)
1267 reposition_icon(pi, g_list_index(list, widget) + dir);
1269 return TRUE;
1272 static void reposition_icon_on_side(GtkWidget *side, GtkWidget *widget,
1273 int index)
1275 GList *list;
1277 list = gtk_container_get_children(GTK_CONTAINER(side));
1279 /* Want to move icon to the list in the given 'side'. Is it there
1280 * already?
1283 if (!g_list_find(list, widget))
1285 /* No, reparent */
1286 gtk_grab_remove(widget);
1287 gtk_widget_reparent(widget, side);
1288 dnd_motion_grab_pointer();
1289 gtk_grab_add(widget);
1292 gtk_box_reorder_child(GTK_BOX(side), widget, index);
1294 g_list_free(list);
1297 /* Move icon to this index in the complete widget list.
1298 * 0 makes the icon the left-most icon. The gap in the middle has
1299 * an index number, which allows you to specify that the icon should
1300 * go on the left or right side.
1302 static void reposition_icon(PanelIcon *pi, int index)
1304 Panel *panel = pi->panel;
1305 GtkWidget *widget = pi->widget;
1306 GList *list;
1307 int before_len;
1309 list = gtk_container_get_children(GTK_CONTAINER(panel->before));
1310 before_len = g_list_length(list);
1311 g_list_free(list);
1313 if (index <= before_len)
1314 reposition_icon_on_side(panel->before, widget, index);
1315 else
1316 reposition_icon_on_side(panel->after, widget,
1317 index - (before_len + 1));
1319 panel_save(panel);
1322 static void start_drag(PanelIcon *pi, GdkEventMotion *event)
1324 GtkWidget *widget = pi->widget;
1325 Icon *icon = (Icon *) pi;
1327 if (!icon->selected)
1329 if (event->state & GDK_BUTTON1_MASK)
1331 /* Select just this one */
1332 icon_select_only(icon);
1333 tmp_icon_selected = TRUE;
1335 else
1336 icon_set_selected(icon, TRUE);
1339 g_return_if_fail(icon_selection != NULL);
1341 if (icon_selection->next == NULL)
1342 drag_one_item(widget, event, icon->path, icon->item, NULL);
1343 else
1345 guchar *uri_list;
1347 uri_list = icon_create_uri_list();
1348 drag_selection(widget, event, uri_list);
1349 g_free(uri_list);
1353 static void applet_died(GtkWidget *socket)
1355 gboolean never_plugged;
1357 never_plugged = (!g_object_get_data(G_OBJECT(socket), "lost_plug"))
1358 && !GTK_SOCKET(socket)->plug_window;
1360 if (never_plugged)
1362 report_error(
1363 _("Applet quit without ever creating a widget!"));
1364 gtk_widget_destroy(socket);
1367 gtk_widget_unref(socket);
1370 static void socket_destroyed(GtkWidget *socket, GtkWidget *widget)
1372 g_object_set_data(G_OBJECT(socket), "lost_plug", "yes");
1374 gtk_widget_unref(socket);
1376 gtk_widget_destroy(widget); /* Remove from panel */
1378 if (!closing_panel)
1379 panel_save(g_object_get_data(G_OBJECT(socket), "panel"));
1382 /* Try to run this applet.
1383 * Cases:
1385 * - No executable AppletRun:
1386 * icon->socket == NULL (unchanged) on return.
1388 * Otherwise, create socket (setting icon->socket) and ref it twice.
1390 * - AppletRun quits without connecting a plug:
1391 * On child death lost_plug is unset and socket is empty.
1392 * Unref socket.
1393 * Report error and destroy widget (to 'socket destroyed').
1395 * - AppletRun quits while plug is in socket:
1396 * Unref socket once. Socket will be destroyed later.
1398 * - Socket is destroyed.
1399 * Set lost_plug = "yes" and remove widget from panel.
1400 * Unref socket.
1402 static void run_applet(PanelIcon *pi)
1404 GError *error = NULL;
1405 char *argv[3];
1406 gint pid;
1407 Icon *icon = (Icon *) pi;
1409 argv[0] = make_path(icon->path, "AppletRun")->str;
1411 if (access(argv[0], X_OK) != 0)
1412 return;
1414 pi->socket = gtk_socket_new();
1416 gtk_container_add(GTK_CONTAINER(pi->widget), pi->socket);
1417 gtk_widget_show_all(pi->socket);
1418 gtk_widget_realize(pi->socket);
1421 gchar *pos;
1422 PanelSide side = pi->panel->side;
1424 /* Set a hint to let applets position their menus correctly */
1425 pos = g_strdup_printf("%s,%d",
1426 side == PANEL_TOP ? "Top" :
1427 side == PANEL_BOTTOM ? "Bottom" :
1428 side == PANEL_LEFT ? "Left" :
1429 "Right", MENU_MARGIN);
1430 gdk_property_change(pi->socket->window,
1431 gdk_atom_intern("_ROX_PANEL_MENU_POS", FALSE),
1432 gdk_atom_intern("STRING", FALSE),
1433 8, GDK_PROP_MODE_REPLACE,
1434 pos, strlen(pos));
1435 g_free(pos);
1438 g_object_set_data(G_OBJECT(pi->widget), "icon", pi);
1439 g_object_set_data(G_OBJECT(pi->socket), "panel", pi->panel);
1441 argv[1] = g_strdup_printf("%ld",
1442 GDK_WINDOW_XWINDOW(pi->socket->window));
1443 argv[2] = NULL;
1445 if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
1446 NULL, NULL, &pid, &error))
1448 delayed_error(_("Error running applet:\n%s"), error->message);
1449 g_error_free(error);
1450 gtk_widget_destroy(pi->socket);
1451 pi->socket = NULL;
1453 else
1455 gtk_widget_ref(pi->socket);
1456 on_child_death(pid, (CallbackFn) applet_died, pi->socket);
1458 gtk_widget_ref(pi->socket);
1459 g_signal_connect(pi->socket, "destroy",
1460 G_CALLBACK(socket_destroyed), pi->widget);
1463 g_free(argv[1]);
1466 static void panel_post_resize(GtkWidget *win, GtkRequisition *req, Panel *panel)
1468 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
1470 req->width = screen_width;
1471 req->height += EDGE_WIDTH;
1473 else
1475 int h = screen_height;
1477 if (current_panel[PANEL_TOP])
1479 GtkWidget *win = current_panel[PANEL_TOP]->window;
1480 h -= win->allocation.height;
1483 if (current_panel[PANEL_BOTTOM])
1485 GtkWidget *win = current_panel[PANEL_BOTTOM]->window;
1486 h -= win->allocation.height;
1489 req->height = h;
1490 req->width += EDGE_WIDTH;
1494 static void update_side(GtkWidget *side)
1496 GList *kids, *next;
1498 kids = gtk_container_get_children(GTK_CONTAINER(side));
1499 for (next = kids; next; next = next->next)
1501 PanelIcon *pi;
1502 pi = g_object_get_data(next->data, "icon");
1503 panel_icon_set_tip(pi);
1505 g_list_free(kids);
1508 /* Tips or style has changed -- update everything on this panel */
1509 static void panel_set_style(Panel *panel)
1511 update_side(panel->before);
1512 update_side(panel->after);
1513 gtk_widget_queue_resize(panel->window);
1516 static gboolean recreate_panels(char **names)
1518 int i;
1520 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
1522 if (names[i])
1524 panel_new(names[i], i);
1525 g_free(names[i]);
1529 g_free(names);
1531 return FALSE;
1534 static void panel_style_changed(void)
1536 int i;
1538 if (o_override_redirect.has_changed)
1540 gchar **names;
1542 names = g_new(char *, PANEL_NUMBER_OF_SIDES);
1544 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
1546 Panel *panel = current_panel[i];
1547 names[i] = panel ? g_strdup(panel->name) : NULL;
1548 panel_new(NULL, i);
1551 g_idle_add((GtkFunction) recreate_panels, names);
1554 if (o_panel_style.has_changed)
1556 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
1558 if (current_panel[i])
1559 panel_set_style(current_panel[i]);
1564 static gboolean draw_panel_edge(GtkWidget *widget, GdkEventExpose *event,
1565 Panel *panel)
1567 int x, y, width, height;
1569 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
1571 width = screen_width;
1572 height = EDGE_WIDTH;
1574 x = 0;
1575 if (panel->side == PANEL_BOTTOM)
1576 y = 0;
1577 else
1578 y = widget->allocation.height - EDGE_WIDTH;
1580 else
1582 width = EDGE_WIDTH;
1583 height = screen_height;
1585 y = 0;
1586 if (panel->side == PANEL_RIGHT)
1587 x = 0;
1588 else
1589 x = widget->allocation.width - EDGE_WIDTH;
1592 gdk_draw_rectangle(widget->window,
1593 widget->style->fg_gc[GTK_STATE_NORMAL], TRUE,
1594 x, y, width, height);
1596 return FALSE;
1599 static gpointer parent_class;
1601 static void panel_icon_destroy(Icon *icon)
1603 PanelIcon *pi = (PanelIcon *) icon;
1605 g_return_if_fail(pi->widget != NULL);
1607 gtk_widget_destroy(pi->widget);
1610 static void panel_remove_items(void)
1612 Panel *panel;
1614 g_return_if_fail(icon_selection != NULL);
1616 panel = ((PanelIcon *) icon_selection->data)->panel;
1618 while (icon_selection)
1619 icon_destroy((Icon *) icon_selection->data);
1621 panel_save(panel);
1624 static void panel_icon_redraw(Icon *icon)
1626 gtk_widget_queue_draw(PANEL_ICON(icon)->widget);
1629 static void panel_icon_update(Icon *icon)
1631 PanelIcon *pi = (PanelIcon *) icon;
1633 gtk_widget_queue_draw(pi->widget);
1634 gtk_label_set_text(GTK_LABEL(pi->label), icon->item->leafname);
1635 panel_icon_set_tip(pi);
1636 panel_save(pi->panel);
1639 /* The point of this is to clear the selection if the existing icons
1640 * aren't from the same panel...
1642 static gboolean panel_icon_same_group(Icon *icon, Icon *other)
1644 if (IS_PANEL_ICON(other))
1646 PanelIcon *a = (PanelIcon *) icon;
1647 PanelIcon *b = (PanelIcon *) other;
1649 return a->panel == b->panel;
1651 else
1652 return FALSE;
1655 static void panel_icon_class_init(gpointer gclass, gpointer data)
1657 IconClass *icon = (IconClass *) gclass;
1659 parent_class = g_type_class_peek_parent(gclass);
1661 icon->destroy = panel_icon_destroy;
1662 icon->redraw = panel_icon_redraw;
1663 icon->update = panel_icon_update;
1664 icon->remove_items = panel_remove_items;
1665 icon->same_group = panel_icon_same_group;
1666 icon->wink = panel_icon_wink;
1669 static void panel_icon_init(GTypeInstance *object, gpointer gclass)
1671 PanelIcon *pi = (PanelIcon *) object;
1673 pi->widget = NULL;
1674 pi->label = NULL;
1675 pi->socket = NULL;
1678 static GType panel_icon_get_type(void)
1680 static GType type = 0;
1682 if (!type)
1684 static const GTypeInfo info =
1686 sizeof (PanelIconClass),
1687 NULL, /* base_init */
1688 NULL, /* base_finalise */
1689 panel_icon_class_init,
1690 NULL, /* class_finalise */
1691 NULL, /* class_data */
1692 sizeof(PanelIcon),
1693 0, /* n_preallocs */
1694 panel_icon_init
1697 type = g_type_register_static(icon_get_type(),
1698 "PanelIcon", &info, 0);
1701 return type;
1704 static PanelIcon *panel_icon_new(Panel *panel,
1705 const char *pathname,
1706 const char *name)
1708 PanelIcon *pi;
1709 Icon *icon;
1711 pi = g_object_new(panel_icon_get_type(), NULL);
1712 icon = (Icon *) pi;
1714 icon_set_path(icon, pathname, name);
1715 pi->panel = panel;
1717 return pi;
1720 static gboolean panel_want_show_text(PanelIcon *pi)
1722 Icon *icon = (Icon *) pi;
1724 if (o_panel_style.int_value == SHOW_BOTH)
1725 return TRUE;
1726 if (o_panel_style.int_value == SHOW_ICON)
1727 return FALSE;
1729 if (icon->item->flags & ITEM_FLAG_APPDIR)
1730 return FALSE;
1732 return TRUE;
1735 static void panel_position_menu(GtkMenu *menu, gint *x, gint *y,
1736 gboolean *push_in, gpointer data)
1738 int *pos = (int *) data;
1739 GtkRequisition requisition;
1741 gtk_widget_size_request(GTK_WIDGET(menu), &requisition);
1743 if (pos[0] == -1)
1744 *x = screen_width - MENU_MARGIN - requisition.width;
1745 else if (pos[0] == -2)
1746 *x = MENU_MARGIN;
1747 else
1748 *x = pos[0] - (requisition.width >> 2);
1750 if (pos[1] == -1)
1751 *y = screen_height - MENU_MARGIN - requisition.height;
1752 else if (pos[1] == -2)
1753 *y = MENU_MARGIN;
1754 else
1755 *y = pos[1] - (requisition.height >> 2);
1757 *x = CLAMP(*x, 0, screen_width - requisition.width);
1758 *y = CLAMP(*y, 0, screen_height - requisition.height);
1760 *push_in = FALSE;
1763 static void panel_show_menu(GdkEventButton *event, PanelIcon *pi, Panel *panel)
1765 PanelSide side = panel->side;
1766 int pos[2];
1768 pos[0] = event->x_root;
1769 pos[1] = event->y_root;
1771 icon_prepare_menu((Icon *) pi, FALSE);
1773 if (side == PANEL_LEFT)
1774 pos[0] = -2;
1775 else if (side == PANEL_RIGHT)
1776 pos[0] = -1;
1778 if (side == PANEL_TOP)
1779 pos[1] = -2;
1780 else if (side == PANEL_BOTTOM)
1781 pos[1] = -1;
1783 gtk_menu_popup(GTK_MENU(icon_menu), NULL, NULL,
1784 panel_position_menu,
1785 (gpointer) pos, event->button, event->time);