r1352: Auto-raise panels when the pointer hits the side of the screen, and lower
[rox-filer.git] / ROX-Filer / src / panel.c
blobc68e280ebbf583c41f48388d5fd7f2cc7d54c274
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"
55 /* The width of the separator at the inner edge of the panel */
56 #define EDGE_WIDTH 2
58 /* The gap between panel icons */
59 #define PANEL_ICON_SPACING 8
61 static gboolean tmp_icon_selected = FALSE; /* When dragging */
63 typedef struct _PanelIconClass PanelIconClass;
64 typedef struct _PanelIcon PanelIcon;
66 struct _PanelIconClass {
67 IconClass parent;
70 struct _PanelIcon {
71 Icon icon;
73 Panel *panel;
74 GtkWidget *widget; /* The drawing area for the icon */
75 GtkWidget *label;
76 GtkWidget *socket; /* For applets */
79 #define PANEL_ICON(obj) GTK_CHECK_CAST((obj), panel_icon_get_type(), PanelIcon)
80 #define IS_PANEL_ICON(obj) \
81 G_TYPE_CHECK_INSTANCE_TYPE((obj), panel_icon_get_type())
83 Panel *current_panel[PANEL_NUMBER_OF_SIDES];
85 /* NULL => Not loading a panel */
86 static Panel *loading_panel = NULL;
88 /* Static prototypes */
89 static int panel_delete(GtkWidget *widget, GdkEvent *event, Panel *panel);
90 static void panel_destroyed(GtkWidget *widget, Panel *panel);
91 static const char *pan_from_file(gchar *line);
92 static gint icon_button_release(GtkWidget *widget,
93 GdkEventButton *event,
94 PanelIcon *pi);
95 static gint icon_button_press(GtkWidget *widget,
96 GdkEventButton *event,
97 PanelIcon *pi);
98 static void reposition_panel(GtkWidget *window,
99 GtkAllocation *alloc, Panel *panel);
100 static gint expose_icon(GtkWidget *widget,
101 GdkEventExpose *event,
102 PanelIcon *pi);
103 static gint draw_icon(GtkWidget *widget,
104 GdkRectangle *badarea,
105 PanelIcon *pi);
106 static gint panel_button_release(GtkWidget *widget,
107 GdkEventButton *event,
108 Panel *panel);
109 static gint panel_button_press(GtkWidget *widget,
110 GdkEventButton *event,
111 Panel *panel);
112 static void panel_post_resize(GtkWidget *box,
113 GtkRequisition *req, Panel *panel);
114 static void drag_set_panel_dest(PanelIcon *pi);
115 static void add_uri_list(GtkWidget *widget,
116 GdkDragContext *context,
117 gint x,
118 gint y,
119 GtkSelectionData *selection_data,
120 guint info,
121 guint32 time,
122 Panel *panel);
123 static void panel_add_item(Panel *panel,
124 const guchar *path,
125 const guchar *name,
126 gboolean after);
127 static gboolean drag_motion(GtkWidget *widget,
128 GdkDragContext *context,
129 gint x,
130 gint y,
131 guint time,
132 PanelIcon *pi);
133 static void drag_leave(GtkWidget *widget,
134 GdkDragContext *context,
135 guint32 time,
136 Icon *icon);
137 static GtkWidget *make_insert_frame(Panel *panel);
138 static gboolean enter_icon(GtkWidget *widget,
139 GdkEventCrossing *event,
140 Icon *icon);
141 static gint icon_motion_event(GtkWidget *widget,
142 GdkEventMotion *event,
143 PanelIcon *pi);
144 static gint panel_leave_event(GtkWidget *widget,
145 GdkEventCrossing *event,
146 Panel *panel);
147 static gint panel_motion_event(GtkWidget *widget,
148 GdkEventMotion *event,
149 Panel *panel);
150 static void reposition_icon(PanelIcon *pi, int index);
151 static void start_drag(PanelIcon *pi, GdkEventMotion *event);
152 static void drag_end(GtkWidget *widget,
153 GdkDragContext *context,
154 Icon *icon);
155 static void perform_action(Panel *panel,
156 PanelIcon *pi,
157 GdkEventButton *event);
158 static void run_applet(PanelIcon *pi);
159 static void size_request(GtkWidget *widget, GtkRequisition *req, PanelIcon *pi);
160 static void panel_load_from_xml(Panel *panel, xmlDocPtr doc);
161 static gboolean draw_panel_edge(GtkWidget *widget, GdkEventExpose *event,
162 Panel *panel);
163 static PanelIcon *panel_icon_new(Panel *panel,
164 const char *pathname,
165 const char *name);
166 static GType panel_icon_get_type(void);
167 static gboolean panel_want_show_text(PanelIcon *pi);
168 static void panel_show_menu(GdkEventButton *event, PanelIcon *pi, Panel *panel);
169 static void panel_style_changed(void);
172 static GtkWidget *dnd_highlight = NULL; /* (stops flickering) */
174 /* When sliding the panel, records where the panel was before */
175 static gint slide_from_value = 0;
177 #define SHOW_BOTH 0
178 #define SHOW_APPS_SMALL 1
179 #define SHOW_ICON 2
180 static Option o_panel_style;
182 static int closing_panel = 0; /* Don't panel_save; destroying! */
184 /****************************************************************
185 * EXTERNAL INTERFACE *
186 ****************************************************************/
188 void panel_init(void)
190 option_add_int(&o_panel_style, "panel_style", SHOW_APPS_SMALL);
192 option_add_notify(panel_style_changed);
195 /* 'name' may be NULL or "" to remove the panel */
196 Panel *panel_new(const gchar *name, PanelSide side)
198 guchar *load_path;
199 Panel *panel;
200 GtkWidget *vp, *box, *frame, *align;
202 g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, NULL);
203 g_return_val_if_fail(loading_panel == NULL, NULL);
205 if (name && *name == '\0')
206 name = NULL;
208 if (current_panel[side])
210 if (name)
211 number_of_windows++;
212 closing_panel++;
213 gtk_widget_destroy(current_panel[side]->window);
214 closing_panel--;
215 if (name)
216 number_of_windows--;
219 if (name == NULL || *name == '\0')
220 return NULL;
222 panel = g_new(Panel, 1);
223 panel->name = g_strdup(name);
224 panel->side = side;
225 panel->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
226 gtk_window_set_resizable(GTK_WINDOW(panel->window), FALSE);
227 gtk_window_set_wmclass(GTK_WINDOW(panel->window), "ROX-Panel", PROJECT);
228 gtk_widget_set_events(panel->window,
229 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
230 GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
232 g_signal_connect(panel->window, "delete-event",
233 G_CALLBACK(panel_delete), panel);
234 g_signal_connect(panel->window, "destroy",
235 G_CALLBACK(panel_destroyed), panel);
236 g_signal_connect(panel->window, "button_press_event",
237 G_CALLBACK(panel_button_press), panel);
238 g_signal_connect(panel->window, "button_release_event",
239 G_CALLBACK(panel_button_release), panel);
240 g_signal_connect(panel->window, "motion-notify-event",
241 G_CALLBACK(panel_motion_event), panel);
242 g_signal_connect(panel->window, "leave-notify-event",
243 G_CALLBACK(panel_leave_event), panel);
245 if (strchr(name, '/'))
246 load_path = g_strdup(name);
247 else
249 guchar *leaf;
251 leaf = g_strconcat("pan_", name, NULL);
252 load_path = choices_find_path_load(leaf, PROJECT);
253 g_free(leaf);
256 if (panel->side == PANEL_RIGHT)
257 align = gtk_alignment_new(1.0, 0.0, 0.0, 1.0);
258 else if (panel->side == PANEL_BOTTOM)
259 align = gtk_alignment_new(0.0, 1.0, 1.0, 0.0);
260 else if (panel->side == PANEL_TOP)
261 align = gtk_alignment_new(0.0, 0.0, 1.0, 0.0);
262 else
263 align = gtk_alignment_new(0.0, 0.0, 0.0, 1.0);
265 gtk_container_add(GTK_CONTAINER(panel->window), align);
267 vp = gtk_viewport_new(NULL, NULL);
268 gtk_container_set_resize_mode(GTK_CONTAINER(vp), GTK_RESIZE_PARENT);
269 gtk_viewport_set_shadow_type(GTK_VIEWPORT(vp), GTK_SHADOW_NONE);
270 gtk_container_add(GTK_CONTAINER(align), vp);
272 g_signal_connect(align, "expose-event",
273 G_CALLBACK(draw_panel_edge), panel);
275 if (side == PANEL_TOP || side == PANEL_BOTTOM)
277 panel->adj = gtk_viewport_get_hadjustment(GTK_VIEWPORT(vp));
278 box = gtk_hbox_new(FALSE, 0);
279 panel->before = gtk_hbox_new(FALSE, 0);
280 panel->after = gtk_hbox_new(FALSE, 0);
282 else
284 panel->adj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(vp));
285 box = gtk_vbox_new(FALSE, 0);
286 panel->before = gtk_vbox_new(FALSE, 0);
287 panel->after = gtk_vbox_new(FALSE, 0);
290 gtk_container_add(GTK_CONTAINER(vp), box);
291 gtk_box_pack_start(GTK_BOX(box), panel->before, FALSE, TRUE, 0);
292 gtk_box_pack_end(GTK_BOX(box), panel->after, FALSE, TRUE, 0);
294 frame = make_insert_frame(panel);
295 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
297 /* This is used so that we can find the middle easily! */
298 panel->gap = gtk_event_box_new();
299 gtk_box_pack_start(GTK_BOX(box), panel->gap, FALSE, FALSE, 0);
301 frame = make_insert_frame(panel);
302 g_object_set_data(G_OBJECT(frame), "after", "yes");
303 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
305 gtk_widget_realize(panel->window);
306 make_panel_window(panel->window);
308 gtk_widget_show_all(align);
310 loading_panel = panel;
311 if (load_path && access(load_path, F_OK) == 0)
313 xmlDocPtr doc;
314 doc = xmlParseFile(load_path);
315 if (doc)
317 panel_load_from_xml(panel, doc);
318 xmlFreeDoc(doc);
320 else
322 parse_file(load_path, pan_from_file);
323 info_message(_("Your old panel file has been "
324 "converted to the new XML format."));
325 panel_save(panel);
328 else
330 /* Don't scare users with an empty panel... */
331 guchar *apps;
333 panel_add_item(panel, "~", "Home", FALSE);
335 apps = pathdup(make_path(app_dir, "..")->str);
336 if (apps)
338 panel_add_item(panel, apps, "Apps", FALSE);
339 g_free(apps);
342 loading_panel = NULL;
343 g_free(load_path);
345 current_panel[side] = panel;
347 gtk_widget_queue_resize(box);
348 g_signal_connect(panel->window, "size-request",
349 G_CALLBACK(panel_post_resize), panel);
350 g_signal_connect(panel->window, "size-allocate",
351 G_CALLBACK(reposition_panel), panel);
353 /* Stop windows from maximising over us completely, so that the
354 * auto-raise stuff works...
357 guint32 wm_strut[] = {0, 0, 0, 0};
359 if (panel->side == PANEL_LEFT)
360 wm_strut[0] = 2;
361 else if (panel->side == PANEL_RIGHT)
362 wm_strut[1] = 2;
363 else if (panel->side == PANEL_TOP)
364 wm_strut[2] = 2;
365 else
366 wm_strut[3] = 2;
368 gdk_property_change(panel->window->window,
369 gdk_atom_intern("_NET_WM_STRUT", FALSE),
370 gdk_atom_intern("CARDINAL", FALSE),
371 32, GDK_PROP_MODE_REPLACE,
372 (gchar *) &wm_strut, 4);
375 number_of_windows++;
376 gdk_window_lower(panel->window->window);
377 gtk_widget_show(panel->window);
378 gdk_window_lower(panel->window->window);
380 return panel;
383 /* Externally visible function to add an item to a panel */
384 gboolean panel_add(PanelSide side,
385 const gchar *path, const gchar *label, gboolean after)
387 g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, FALSE);
389 g_return_val_if_fail(current_panel[side] != NULL, FALSE);
391 panel_add_item(current_panel[side], path, label, after);
393 return TRUE;
396 /****************************************************************
397 * INTERNAL FUNCTIONS *
398 ****************************************************************/
400 /* User has tried to close the panel via the window manager - confirm */
401 static int panel_delete(GtkWidget *widget, GdkEvent *event, Panel *panel)
403 return get_choice(_("Close panel?"),
404 _("You have tried to close a panel via the window "
405 "manager - I usually find that this is accidental... "
406 "really close?"),
407 2, _("Cancel"), _("Remove")) != 1;
410 static void panel_destroyed(GtkWidget *widget, Panel *panel)
412 if (current_panel[panel->side] == panel)
413 current_panel[panel->side] = NULL;
415 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
417 if (current_panel[PANEL_RIGHT])
418 gtk_widget_queue_resize(
419 current_panel[PANEL_RIGHT]->window);
420 if (current_panel[PANEL_LEFT])
421 gtk_widget_queue_resize(
422 current_panel[PANEL_LEFT]->window);
425 g_free(panel->name);
426 g_free(panel);
428 if (--number_of_windows < 1)
429 gtk_main_quit();
432 static void panel_load_side(Panel *panel, xmlNodePtr side, gboolean after)
434 xmlNodePtr node;
435 char *label, *path;
437 for (node = side->xmlChildrenNode; node; node = node->next)
439 if (node->type != XML_ELEMENT_NODE)
440 continue;
441 if (strcmp(node->name, "icon") != 0)
442 continue;
444 label = xmlGetProp(node, "label");
445 if (!label)
446 label = g_strdup("<missing label>");
447 path = xmlNodeGetContent(node);
448 if (!path)
449 path = g_strdup("<missing path>");
451 panel_add_item(panel, path, label, after);
453 g_free(path);
454 g_free(label);
458 /* Create one panel icon for each icon in the doc */
459 static void panel_load_from_xml(Panel *panel, xmlDocPtr doc)
461 xmlNodePtr root;
463 root = xmlDocGetRootElement(doc);
464 panel_load_side(panel, get_subnode(root, NULL, "start"), FALSE);
465 panel_load_side(panel, get_subnode(root, NULL, "end"), TRUE);
468 /* Called for each line in the config file while loading a new panel */
469 static const char *pan_from_file(gchar *line)
471 gchar *sep, *leaf;
473 g_return_val_if_fail(line != NULL, NULL);
474 g_return_val_if_fail(loading_panel != NULL, NULL);
476 if (*line == '\0')
477 return NULL;
479 sep = strpbrk(line, "<>");
480 if (!sep)
481 return _("Missing < or > in panel config file");
483 if (sep != line)
484 leaf = g_strndup(line, sep - line);
485 else
486 leaf = NULL;
488 panel_add_item(loading_panel, sep + 1, leaf, sep[0] == '>');
490 g_free(leaf);
492 return NULL;
495 static gboolean icon_pointer_in(GtkWidget *widget,
496 GdkEventCrossing *event,
497 Icon *icon)
499 gtk_widget_set_state(widget, GTK_STATE_PRELIGHT);
501 return 0;
504 static gboolean icon_pointer_out(GtkWidget *widget,
505 GdkEventCrossing *event,
506 Icon *icon)
508 gtk_widget_set_state(widget, GTK_STATE_NORMAL);
510 return 0;
513 static void panel_icon_destroyed(PanelIcon *pi)
515 g_return_if_fail(pi->widget != NULL);
517 pi->widget = NULL;
519 g_object_unref(pi);
522 /* Set the tooltip AND hide/show the label */
523 static void panel_icon_set_tip(PanelIcon *pi)
525 XMLwrapper *ai;
526 xmlNode *node;
527 Icon *icon = (Icon *) pi;
529 g_return_if_fail(pi != NULL);
531 if (pi->label)
533 if (panel_want_show_text(pi))
534 gtk_widget_show(pi->label);
535 else
536 gtk_widget_hide(pi->label);
539 if (pi->socket)
540 ai = NULL;
541 else
542 ai = appinfo_get(icon->path, icon->item);
544 if (ai && ((node = appinfo_get_section(ai, "Summary"))))
546 guchar *str;
547 str = xmlNodeListGetString(node->doc,
548 node->xmlChildrenNode, 1);
549 if (str)
551 gtk_tooltips_set_tip(tooltips, pi->widget, str, NULL);
552 g_free(str);
555 else if ((!panel_want_show_text(pi)) && !pi->socket)
557 gtk_tooltips_set_tip(tooltips, pi->widget,
558 icon->item->leafname, NULL);
560 else
561 gtk_tooltips_set_tip(tooltips, pi->widget, NULL, NULL);
563 if (ai)
564 g_object_unref(ai);
567 /* Add an icon with this path to the panel. If after is TRUE then the
568 * icon is added to the right/bottom end of the panel.
570 * If name is NULL a suitable name is taken from path.
572 static void panel_add_item(Panel *panel,
573 const guchar *path,
574 const guchar *name,
575 gboolean after)
577 GtkWidget *widget;
578 PanelIcon *pi;
579 Icon *icon;
581 g_return_if_fail(panel != NULL);
582 g_return_if_fail(path != NULL);
584 widget = gtk_event_box_new();
585 gtk_widget_set_events(widget,
586 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
587 GDK_BUTTON3_MOTION_MASK |
588 GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
589 GDK_BUTTON_RELEASE_MASK);
591 gtk_box_pack_start(GTK_BOX(after ? panel->after : panel->before),
592 widget, FALSE, TRUE, 0);
593 if (after)
594 gtk_box_reorder_child(GTK_BOX(panel->after), widget, 0);
596 gtk_widget_realize(widget);
598 pi = panel_icon_new(panel, path, name);
599 icon = (Icon *) pi;
601 /* Widget takes the initial ref of Icon */
602 g_object_set_data(G_OBJECT(widget), "icon", pi);
604 pi->widget = widget;
605 g_object_ref(widget);
607 gtk_widget_set_name(pi->widget, "panel-icon");
609 g_signal_connect_swapped(widget, "destroy",
610 G_CALLBACK(panel_icon_destroyed), pi);
612 if (icon->item->base_type == TYPE_DIRECTORY)
613 run_applet(pi);
615 g_signal_connect(widget, "button_release_event",
616 G_CALLBACK(icon_button_release), pi);
617 g_signal_connect(widget, "button_press_event",
618 G_CALLBACK(icon_button_press), pi);
619 g_signal_connect(widget, "motion-notify-event",
620 G_CALLBACK(icon_motion_event), pi);
621 g_signal_connect(widget, "enter-notify-event",
622 G_CALLBACK(icon_pointer_in), pi);
623 g_signal_connect(widget, "leave-notify-event",
624 G_CALLBACK(icon_pointer_out), pi);
626 if (!pi->socket)
628 g_signal_connect(widget, "enter-notify-event",
629 G_CALLBACK(enter_icon), pi);
630 g_signal_connect_after(widget, "expose_event",
631 G_CALLBACK(expose_icon), pi);
632 g_signal_connect(widget, "drag_data_get",
633 G_CALLBACK(drag_data_get), NULL);
635 g_signal_connect(widget, "size_request",
636 G_CALLBACK(size_request), pi);
638 drag_set_panel_dest(pi);
640 pi->label = gtk_label_new(icon->item->leafname);
641 gtk_container_add(GTK_CONTAINER(pi->widget), pi->label);
642 gtk_misc_set_alignment(GTK_MISC(pi->label), 0.5, 1);
643 gtk_misc_set_padding(GTK_MISC(pi->label), 1, 2);
646 if (!loading_panel)
647 panel_save(panel);
649 panel_icon_set_tip(pi);
650 gtk_widget_show(widget);
653 /* Called when Gtk+ wants to know how much space an icon needs.
654 * 'req' is already big enough for the label, if shown.
656 static void size_request(GtkWidget *widget, GtkRequisition *req, PanelIcon *pi)
658 int im_width, im_height;
659 Icon *icon = (Icon *) pi;
661 im_width = icon->item->image->width;
662 im_height = MIN(icon->item->image->height, ICON_HEIGHT);
664 req->height += im_height;
665 req->width = MAX(req->width, im_width);
667 if (pi->panel->side == PANEL_LEFT || pi->panel->side == PANEL_RIGHT)
668 req->height += PANEL_ICON_SPACING;
669 else
670 req->width += PANEL_ICON_SPACING;
673 static gint expose_icon(GtkWidget *widget,
674 GdkEventExpose *event,
675 PanelIcon *pi)
677 return draw_icon(widget, &event->area, pi);
680 static gint draw_icon(GtkWidget *widget, GdkRectangle *badarea, PanelIcon *pi)
682 GdkRectangle area;
683 int width, height;
684 Icon *icon = (Icon *) pi;
686 gdk_drawable_get_size(widget->window, &width, &height);
688 area.x = 0;
689 area.width = width;
690 area.height = icon->item->image->height;
692 if (panel_want_show_text(pi))
694 int text_height = pi->label->requisition.height;
696 area.y = height - text_height - area.height;
698 draw_large_icon(widget, &area, icon->item,
699 icon->item->image, icon->selected);
701 else
703 area.y = (height - area.height) >> 1;
704 draw_large_icon(widget, &area, icon->item,
705 icon->item->image, icon->selected);
708 return FALSE;
711 /* icon may be NULL if the event is on the background */
712 static void perform_action(Panel *panel, PanelIcon *pi, GdkEventButton *event)
714 BindAction action;
715 Icon *icon = (Icon *) pi;
717 action = bind_lookup_bev(icon ? BIND_PANEL_ICON : BIND_PANEL, event);
719 if (pi && pi->socket)
720 if (action != ACT_POPUP_MENU && action != ACT_MOVE_ICON)
721 return;
723 switch (action)
725 case ACT_OPEN_ITEM:
726 dnd_motion_ungrab();
727 wink_widget(pi->widget);
728 run_diritem(icon->path, icon->item, NULL, NULL, FALSE);
729 break;
730 case ACT_EDIT_ITEM:
731 dnd_motion_ungrab();
732 wink_widget(pi->widget);
733 run_diritem(icon->path, icon->item, NULL, NULL, TRUE);
734 break;
735 case ACT_POPUP_MENU:
736 dnd_motion_ungrab();
737 panel_show_menu(event, pi, panel);
738 break;
739 case ACT_MOVE_ICON:
740 dnd_motion_start(MOTION_REPOSITION);
741 break;
742 case ACT_PRIME_AND_SELECT:
743 if (!icon->selected)
744 icon_select_only(icon);
745 dnd_motion_start(MOTION_READY_FOR_DND);
746 break;
747 case ACT_PRIME_AND_TOGGLE:
748 icon_set_selected(icon, !icon->selected);
749 dnd_motion_start(MOTION_READY_FOR_DND);
750 break;
751 case ACT_PRIME_FOR_DND:
752 dnd_motion_start(MOTION_READY_FOR_DND);
753 break;
754 case ACT_TOGGLE_SELECTED:
755 icon_set_selected(icon, !icon->selected);
756 break;
757 case ACT_SELECT_EXCL:
758 icon_set_selected(icon, TRUE);
759 break;
760 case ACT_SLIDE_CLEAR_PANEL:
761 icon_select_only(NULL);
762 /* (no break) */
763 case ACT_SLIDE_PANEL:
764 dnd_motion_grab_pointer();
765 slide_from_value = panel->adj->value;
766 dnd_motion_start(MOTION_REPOSITION);
767 break;
768 case ACT_IGNORE:
769 break;
770 case ACT_CLEAR_SELECTION:
771 icon_select_only(NULL);
772 break;
773 default:
774 g_warning("Unsupported action : %d\n", action);
775 break;
779 static gint panel_button_release(GtkWidget *widget,
780 GdkEventButton *event,
781 Panel *panel)
783 if (dnd_motion_release(event))
784 return TRUE;
786 perform_action(panel, NULL, event);
788 return TRUE;
791 static gint panel_button_press(GtkWidget *widget,
792 GdkEventButton *event,
793 Panel *panel)
795 if (dnd_motion_press(panel->window, event))
796 perform_action(panel, NULL, event);
798 return TRUE;
801 static gint icon_button_release(GtkWidget *widget,
802 GdkEventButton *event,
803 PanelIcon *pi)
805 if (pi->socket && event->button == 1)
806 return FALSE; /* Restart button */
808 if (dnd_motion_release(event))
809 return TRUE;
811 perform_action(pi->panel, pi, event);
813 return TRUE;
816 static gint icon_button_press(GtkWidget *widget,
817 GdkEventButton *event,
818 PanelIcon *pi)
820 if (pi->socket && event->button == 1)
821 return FALSE; /* Restart button */
823 if (dnd_motion_press(widget, event))
824 perform_action(pi->panel, pi, event);
826 return TRUE;
829 static void reposition_panel(GtkWidget *window,
830 GtkAllocation *alloc, Panel *panel)
832 int x = 0, y = 0;
833 PanelSide side = panel->side;
835 if (side == PANEL_LEFT || side == PANEL_RIGHT)
837 if (side == PANEL_RIGHT)
838 x = screen_width - alloc->width;
840 if (current_panel[PANEL_TOP])
842 GtkWidget *win = current_panel[PANEL_TOP]->window;
843 y += win->allocation.height;
847 if (side == PANEL_BOTTOM)
848 y = screen_height - alloc->height;
850 gtk_window_move(GTK_WINDOW(panel->window), x, y);
851 gdk_window_move(panel->window->window, x, y);
853 if (side == PANEL_BOTTOM || side == PANEL_TOP)
855 if (current_panel[PANEL_RIGHT])
856 gtk_widget_queue_resize(
857 current_panel[PANEL_RIGHT]->window);
858 if (current_panel[PANEL_LEFT])
859 gtk_widget_queue_resize(
860 current_panel[PANEL_LEFT]->window);
864 /* Same as drag_set_dest(), but for panel icons */
865 static void drag_set_panel_dest(PanelIcon *pi)
867 GtkWidget *obj = pi->widget;
869 make_drop_target(pi->widget, 0);
871 g_signal_connect(obj, "drag_motion", G_CALLBACK(drag_motion), pi);
872 g_signal_connect(obj, "drag_leave", G_CALLBACK(drag_leave), pi);
873 g_signal_connect(obj, "drag_end", G_CALLBACK(drag_end), pi);
876 static gboolean drag_motion(GtkWidget *widget,
877 GdkDragContext *context,
878 gint x,
879 gint y,
880 guint time,
881 PanelIcon *pi)
883 GdkDragAction action = context->suggested_action;
884 char *type = NULL;
885 Icon *icon = (Icon *) pi;
886 DirItem *item = icon->item;
888 if (icon->selected)
889 goto out; /* Can't drag a selection to itself */
891 type = dnd_motion_item(context, &item);
893 if (!item)
894 type = NULL;
895 out:
896 /* We actually must pretend to accept the drop, even if the
897 * directory isn't writeable, so that the spring-opening
898 * thing works.
901 /* Don't allow drops to non-writeable directories */
902 if (o_dnd_spring_open.int_value == FALSE &&
903 type == drop_dest_dir &&
904 access(icon->path, W_OK) != 0)
906 type = NULL;
909 g_dataset_set_data(context, "drop_dest_type", type);
910 if (type)
912 gdk_drag_status(context, action, time);
913 g_dataset_set_data_full(context, "drop_dest_path",
914 g_strdup(icon->path), g_free);
915 if (type == drop_dest_dir)
916 dnd_spring_load(context, NULL);
918 if (dnd_highlight && dnd_highlight != pi->widget)
920 gtk_drag_unhighlight(dnd_highlight);
921 dnd_highlight = NULL;
924 if (dnd_highlight == NULL)
926 gtk_drag_highlight(pi->widget);
927 dnd_highlight = pi->widget;
931 return type != NULL;
935 static void add_uri_list(GtkWidget *widget,
936 GdkDragContext *context,
937 gint x,
938 gint y,
939 GtkSelectionData *selection_data,
940 guint info,
941 guint32 time,
942 Panel *panel)
944 gboolean after = FALSE;
945 GList *uris, *next;
947 if (!selection_data->data)
948 return;
950 g_return_if_fail(selection_data->data[selection_data->length] == '\0');
952 if (g_object_get_data(G_OBJECT(widget), "after"))
953 after = TRUE;
955 uris = uri_list_to_glist(selection_data->data);
957 for (next = uris; next; next = next->next)
959 const guchar *path;
961 path = get_local_path((guchar *) next->data);
963 if (path)
964 panel_add_item(panel, path, NULL, after);
967 g_list_free(uris);
970 static void drag_end(GtkWidget *widget,
971 GdkDragContext *context,
972 Icon *icon)
974 if (tmp_icon_selected)
976 icon_select_only(NULL);
977 tmp_icon_selected = FALSE;
981 static void drag_leave(GtkWidget *widget,
982 GdkDragContext *context,
983 guint32 time,
984 Icon *icon)
986 if (dnd_highlight && dnd_highlight == widget)
988 gtk_drag_unhighlight(dnd_highlight);
989 dnd_highlight = NULL;
992 dnd_spring_abort();
995 /* Create XML icon nodes for these widgets.
996 * Always frees the widgets list.
998 static void make_widgets(xmlNodePtr side, GList *widgets)
1000 GList *next;
1002 for (next = widgets; next; next = next->next)
1004 Icon *icon;
1005 xmlNodePtr tree;
1007 icon = g_object_get_data(G_OBJECT(next->data), "icon");
1009 if (!icon)
1011 g_warning("Can't find Icon from widget\n");
1012 continue;
1015 tree = xmlNewTextChild(side, NULL, "icon", icon->src_path);
1017 xmlSetProp(tree, "label", icon->item->leafname);
1020 if (widgets)
1021 g_list_free(widgets);
1024 void panel_save(Panel *panel)
1026 xmlDocPtr doc;
1027 xmlNodePtr root;
1028 guchar *save = NULL;
1029 guchar *save_new = NULL;
1031 g_return_if_fail(panel != NULL);
1033 if (strchr(panel->name, '/'))
1034 save = g_strdup(panel->name);
1035 else
1037 guchar *leaf;
1039 leaf = g_strconcat("pan_", panel->name, NULL);
1040 save = choices_find_path_save(leaf, PROJECT, TRUE);
1041 g_free(leaf);
1044 if (!save)
1045 return;
1047 doc = xmlNewDoc("1.0");
1048 xmlDocSetRootElement(doc, xmlNewDocNode(doc, NULL, "panel", NULL));
1050 root = xmlDocGetRootElement(doc);
1051 make_widgets(xmlNewChild(root, NULL, "start", NULL),
1052 gtk_container_get_children(GTK_CONTAINER(panel->before)));
1054 make_widgets(xmlNewChild(root, NULL, "end", NULL),
1055 g_list_reverse(gtk_container_get_children(
1056 GTK_CONTAINER(panel->after))));
1058 save_new = g_strconcat(save, ".new", NULL);
1059 if (save_xml_file(doc, save_new) || rename(save_new, save))
1060 delayed_error(_("Error saving panel %s: %s"),
1061 save, g_strerror(errno));
1062 g_free(save_new);
1064 g_free(save);
1065 if (doc)
1066 xmlFreeDoc(doc);
1069 /* Create a frame widget which can be used to add icons to the panel */
1070 static GtkWidget *make_insert_frame(Panel *panel)
1072 GtkWidget *frame;
1073 GtkTargetEntry target_table[] = {
1074 {"text/uri-list", 0, TARGET_URI_LIST},
1077 frame = gtk_frame_new(NULL);
1078 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
1079 gtk_widget_set_size_request(frame, 16, 16);
1081 g_signal_connect(frame, "drag-data-received",
1082 G_CALLBACK(add_uri_list), panel);
1083 gtk_drag_dest_set(frame,
1084 GTK_DEST_DEFAULT_ALL,
1085 target_table,
1086 sizeof(target_table) / sizeof(*target_table),
1087 GDK_ACTION_COPY);
1089 return frame;
1092 static gboolean enter_icon(GtkWidget *widget,
1093 GdkEventCrossing *event,
1094 Icon *icon)
1096 icon_may_update(icon);
1098 return FALSE;
1101 static gint panel_leave_event(GtkWidget *widget,
1102 GdkEventCrossing *event,
1103 Panel *panel)
1105 gdk_window_lower(panel->window->window);
1107 return 0;
1110 static gint panel_motion_event(GtkWidget *widget,
1111 GdkEventMotion *event,
1112 Panel *panel)
1114 gint delta, new;
1115 gboolean raise;
1116 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1118 if (panel->side == PANEL_TOP)
1119 raise = event->y == 0;
1120 else if (panel->side == PANEL_BOTTOM)
1121 raise = event->y == panel->window->allocation.height - 1;
1122 else if (panel->side == PANEL_LEFT)
1123 raise = event->x == 0;
1124 else
1125 raise = event->x == panel->window->allocation.width - 1;
1127 if (raise)
1128 gdk_window_raise(panel->window->window);
1130 if (motion_state != MOTION_REPOSITION)
1131 return FALSE;
1133 if (horz)
1134 delta = event->x_root - drag_start_x;
1135 else
1136 delta = event->y_root - drag_start_y;
1138 new = slide_from_value - delta;
1139 new = CLAMP(new, 0, panel->adj->upper - panel->adj->page_size);
1141 gtk_adjustment_set_value(panel->adj, new);
1143 return TRUE;
1146 static gint icon_motion_event(GtkWidget *widget,
1147 GdkEventMotion *event,
1148 PanelIcon *pi)
1150 Panel *panel = pi->panel;
1151 GList *list, *me;
1152 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1153 int val;
1154 int dir = 0;
1156 if (motion_state == MOTION_READY_FOR_DND)
1158 if (dnd_motion_moved(event))
1159 start_drag(pi, event);
1160 return TRUE;
1162 else if (motion_state != MOTION_REPOSITION)
1163 return FALSE;
1165 list = gtk_container_get_children(GTK_CONTAINER(panel->before));
1166 list = g_list_append(list, NULL); /* The gap in the middle */
1167 list = g_list_concat(list,
1168 gtk_container_get_children(GTK_CONTAINER(panel->after)));
1169 me = g_list_find(list, widget);
1171 g_return_val_if_fail(me != NULL, TRUE);
1173 val = horz ? event->x_root : event->y_root;
1175 if (me->prev)
1177 GtkWidget *prev;
1178 int x, y;
1180 if (me->prev->data)
1181 prev = GTK_WIDGET(me->prev->data);
1182 else
1183 prev = panel->gap;
1185 gdk_window_get_origin(prev->window, &x, &y);
1187 if (val <= (horz ? x : y))
1188 dir = -1;
1191 if (dir == 0 && me->next)
1193 GtkWidget *next;
1194 int x, y, w, h;
1196 if (me->next->data)
1197 next = GTK_WIDGET(me->next->data);
1198 else
1199 next = panel->gap;
1201 gdk_window_get_origin(next->window, &x, &y);
1203 gdk_drawable_get_size(next->window, &w, &h);
1205 x += w;
1206 y += h;
1208 if (val >= (horz ? x : y))
1210 if (next == panel->gap)
1211 dir = +2;
1212 else
1213 dir = +1;
1217 if (dir)
1218 reposition_icon(pi, g_list_index(list, widget) + dir);
1220 return TRUE;
1223 /* Move icon to this index in the complete widget list.
1224 * 0 makes the icon the left-most icon. The gap in the middle has
1225 * an index number, which allows you to specify that the icon should
1226 * go on the left or right side.
1228 static void reposition_icon(PanelIcon *pi, int index)
1230 Panel *panel = pi->panel;
1231 GtkWidget *widget = pi->widget;
1232 GList *list;
1233 int before_len;
1235 list = gtk_container_get_children(GTK_CONTAINER(panel->before));
1236 before_len = g_list_length(list);
1238 if (index <= before_len)
1240 /* Want to move icon to the 'before' list. Is it there
1241 * already?
1244 if (!g_list_find(list, widget))
1246 /* No, reparent */
1247 gtk_grab_remove(widget);
1248 gtk_widget_reparent(widget, panel->before);
1249 dnd_motion_grab_pointer();
1250 gtk_grab_add(widget);
1253 gtk_box_reorder_child(GTK_BOX(panel->before), widget, index);
1255 else
1257 /* Else, we need it in the 'after' list. */
1259 index -= before_len + 1;
1261 g_list_free(list);
1263 list = gtk_container_get_children(GTK_CONTAINER(panel->after));
1265 if (!g_list_find(list, widget))
1267 /* Not already there, reparent */
1268 gtk_grab_remove(widget);
1269 gtk_widget_reparent(widget, panel->after);
1270 dnd_motion_grab_pointer();
1271 gtk_grab_add(widget);
1274 gtk_box_reorder_child(GTK_BOX(panel->after), widget, index);
1277 g_list_free(list);
1279 panel_save(panel);
1282 static void start_drag(PanelIcon *pi, GdkEventMotion *event)
1284 GtkWidget *widget = pi->widget;
1285 Icon *icon = (Icon *) pi;
1287 if (!icon->selected)
1289 if (event->state & GDK_BUTTON1_MASK)
1291 /* Select just this one */
1292 icon_select_only(icon);
1293 tmp_icon_selected = TRUE;
1295 else
1296 icon_set_selected(icon, TRUE);
1299 g_return_if_fail(icon_selection != NULL);
1301 if (icon_selection->next == NULL)
1302 drag_one_item(widget, event, icon->path, icon->item, NULL);
1303 else
1305 guchar *uri_list;
1307 uri_list = icon_create_uri_list();
1308 drag_selection(widget, event, uri_list);
1309 g_free(uri_list);
1313 static void applet_died(GtkWidget *socket)
1315 gboolean never_plugged;
1317 never_plugged = (!g_object_get_data(G_OBJECT(socket), "lost_plug"))
1318 && !GTK_SOCKET(socket)->plug_window;
1320 if (never_plugged)
1322 report_error(
1323 _("Applet quit without ever creating a widget!"));
1324 gtk_widget_destroy(socket);
1327 gtk_widget_unref(socket);
1330 static void socket_destroyed(GtkWidget *socket, GtkWidget *widget)
1332 g_object_set_data(G_OBJECT(socket), "lost_plug", "yes");
1334 gtk_widget_unref(socket);
1336 gtk_widget_destroy(widget); /* Remove from panel */
1338 if (!closing_panel)
1339 panel_save(g_object_get_data(G_OBJECT(socket), "panel"));
1342 /* Try to run this applet.
1343 * Cases:
1345 * - No executable AppletRun:
1346 * icon->socket == NULL (unchanged) on return.
1348 * Otherwise, create socket (setting icon->socket) and ref it twice.
1350 * - AppletRun quits without connecting a plug:
1351 * On child death lost_plug is unset and socket is empty.
1352 * Unref socket.
1353 * Report error and destroy widget (to 'socket destroyed').
1355 * - AppletRun quits while plug is in socket:
1356 * Unref socket once. Socket will be destroyed later.
1358 * - Socket is destroyed.
1359 * Set lost_plug = "yes" and remove widget from panel.
1360 * Unref socket.
1362 static void run_applet(PanelIcon *pi)
1364 GError *error = NULL;
1365 char *argv[3];
1366 gint pid;
1367 Icon *icon = (Icon *) pi;
1369 argv[0] = make_path(icon->path, "AppletRun")->str;
1371 if (access(argv[0], X_OK) != 0)
1372 return;
1374 pi->socket = gtk_socket_new();
1376 gtk_container_add(GTK_CONTAINER(pi->widget), pi->socket);
1377 gtk_widget_show_all(pi->socket);
1378 gtk_widget_realize(pi->socket);
1381 gchar *pos;
1382 PanelSide side = pi->panel->side;
1384 /* Set a hint to let applets position their menus correctly */
1385 pos = g_strdup_printf("%s,%d",
1386 side == PANEL_TOP ? "Top" :
1387 side == PANEL_BOTTOM ? "Bottom" :
1388 side == PANEL_LEFT ? "Left" :
1389 "Right", MENU_MARGIN);
1390 gdk_property_change(pi->socket->window,
1391 gdk_atom_intern("_ROX_PANEL_MENU_POS", FALSE),
1392 gdk_atom_intern("STRING", FALSE),
1393 8, GDK_PROP_MODE_REPLACE,
1394 pos, strlen(pos));
1395 g_free(pos);
1398 g_object_set_data(G_OBJECT(pi->widget), "icon", pi);
1399 g_object_set_data(G_OBJECT(pi->socket), "panel", pi->panel);
1401 argv[1] = g_strdup_printf("%ld",
1402 GDK_WINDOW_XWINDOW(pi->socket->window));
1403 argv[2] = NULL;
1405 if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
1406 NULL, NULL, &pid, &error))
1408 delayed_error(_("Error running applet:\n%s"), error->message);
1409 g_error_free(error);
1410 gtk_widget_destroy(pi->socket);
1411 pi->socket = NULL;
1413 else
1415 gtk_widget_ref(pi->socket);
1416 on_child_death(pid, (CallbackFn) applet_died, pi->socket);
1418 gtk_widget_ref(pi->socket);
1419 g_signal_connect(pi->socket, "destroy",
1420 G_CALLBACK(socket_destroyed), pi->widget);
1423 g_free(argv[1]);
1426 static void panel_post_resize(GtkWidget *win, GtkRequisition *req, Panel *panel)
1428 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
1430 req->width = screen_width;
1431 req->height += EDGE_WIDTH;
1433 else
1435 int h = screen_height;
1437 if (current_panel[PANEL_TOP])
1439 GtkWidget *win = current_panel[PANEL_TOP]->window;
1440 h -= win->allocation.height;
1443 if (current_panel[PANEL_BOTTOM])
1445 GtkWidget *win = current_panel[PANEL_BOTTOM]->window;
1446 h -= win->allocation.height;
1449 req->height = h;
1450 req->width += EDGE_WIDTH;
1454 /* Tips or style has changed -- update everything on this panel */
1455 static void panel_set_style(Panel *panel)
1457 GList *kids, *next;
1459 kids = gtk_container_get_children(GTK_CONTAINER(panel->before));
1460 for (next = kids; next; next = next->next)
1462 PanelIcon *pi;
1463 pi = g_object_get_data(next->data, "icon");
1464 panel_icon_set_tip(pi);
1466 g_list_free(kids);
1468 kids = gtk_container_get_children(GTK_CONTAINER(panel->after));
1469 for (next = kids; next; next = next->next)
1471 PanelIcon *pi;
1472 pi = g_object_get_data(next->data, "icon");
1473 panel_icon_set_tip(pi);
1475 g_list_free(kids);
1477 gtk_widget_queue_resize(panel->window);
1480 static void panel_style_changed(void)
1482 if (o_panel_style.has_changed)
1484 int i;
1486 for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
1488 if (current_panel[i])
1489 panel_set_style(current_panel[i]);
1494 static gboolean draw_panel_edge(GtkWidget *widget, GdkEventExpose *event,
1495 Panel *panel)
1497 int x, y, width, height;
1499 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
1501 width = screen_width;
1502 height = EDGE_WIDTH;
1504 x = 0;
1505 if (panel->side == PANEL_BOTTOM)
1506 y = 0;
1507 else
1508 y = widget->allocation.height - EDGE_WIDTH;
1510 else
1512 width = EDGE_WIDTH;
1513 height = screen_height;
1515 y = 0;
1516 if (panel->side == PANEL_RIGHT)
1517 x = 0;
1518 else
1519 x = widget->allocation.width - EDGE_WIDTH;
1522 gdk_draw_rectangle(widget->window,
1523 widget->style->fg_gc[GTK_STATE_NORMAL], TRUE,
1524 x, y, width, height);
1526 return FALSE;
1529 static gpointer parent_class;
1531 static void panel_icon_destroy(Icon *icon)
1533 PanelIcon *pi = (PanelIcon *) icon;
1535 g_return_if_fail(pi->widget != NULL);
1537 gtk_widget_destroy(pi->widget);
1540 static void panel_remove_items(void)
1542 Panel *panel;
1544 g_return_if_fail(icon_selection != NULL);
1546 panel = ((PanelIcon *) icon_selection->data)->panel;
1548 while (icon_selection)
1549 icon_destroy((Icon *) icon_selection->data);
1551 panel_save(panel);
1554 static void panel_icon_redraw(Icon *icon)
1556 gtk_widget_queue_draw(PANEL_ICON(icon)->widget);
1559 static void panel_icon_update(Icon *icon)
1561 PanelIcon *pi = (PanelIcon *) icon;
1563 gtk_widget_queue_draw(pi->widget);
1564 gtk_label_set_text(GTK_LABEL(pi->label), icon->item->leafname);
1565 panel_save(pi->panel);
1568 /* The point of this is to clear the selection if the existing icons
1569 * aren't from the same panel...
1571 static gboolean panel_icon_same_group(Icon *icon, Icon *other)
1573 if (IS_PANEL_ICON(other))
1575 PanelIcon *a = (PanelIcon *) icon;
1576 PanelIcon *b = (PanelIcon *) other;
1578 return a->panel == b->panel;
1580 else
1581 return FALSE;
1584 static void panel_icon_class_init(gpointer gclass, gpointer data)
1586 IconClass *icon = (IconClass *) gclass;
1588 parent_class = g_type_class_peek_parent(gclass);
1590 icon->destroy = panel_icon_destroy;
1591 icon->redraw = panel_icon_redraw;
1592 icon->update = panel_icon_update;
1593 icon->remove_items = panel_remove_items;
1594 icon->same_group = panel_icon_same_group;
1597 static void panel_icon_init(GTypeInstance *object, gpointer gclass)
1599 PanelIcon *pi = (PanelIcon *) object;
1601 pi->widget = NULL;
1602 pi->label = NULL;
1603 pi->socket = NULL;
1606 static GType panel_icon_get_type(void)
1608 static GType type = 0;
1610 if (!type)
1612 static const GTypeInfo info =
1614 sizeof (PanelIconClass),
1615 NULL, /* base_init */
1616 NULL, /* base_finalise */
1617 panel_icon_class_init,
1618 NULL, /* class_finalise */
1619 NULL, /* class_data */
1620 sizeof(PanelIcon),
1621 0, /* n_preallocs */
1622 panel_icon_init
1625 type = g_type_register_static(icon_get_type(),
1626 "PanelIcon", &info, 0);
1629 return type;
1632 static PanelIcon *panel_icon_new(Panel *panel,
1633 const char *pathname,
1634 const char *name)
1636 PanelIcon *pi;
1637 Icon *icon;
1639 pi = g_object_new(panel_icon_get_type(), NULL);
1640 icon = (Icon *) pi;
1642 icon_set_path(icon, pathname, name);
1643 pi->panel = panel;
1645 return pi;
1648 static gboolean panel_want_show_text(PanelIcon *pi)
1650 Icon *icon = (Icon *) pi;
1652 if (o_panel_style.int_value == SHOW_BOTH)
1653 return TRUE;
1654 if (o_panel_style.int_value == SHOW_ICON)
1655 return FALSE;
1657 if (icon->item->flags & ITEM_FLAG_APPDIR)
1658 return FALSE;
1660 return TRUE;
1663 static void panel_position_menu(GtkMenu *menu, gint *x, gint *y,
1664 gboolean *push_in, gpointer data)
1666 int *pos = (int *) data;
1667 GtkRequisition requisition;
1669 gtk_widget_size_request(GTK_WIDGET(menu), &requisition);
1671 if (pos[0] == -1)
1672 *x = screen_width - MENU_MARGIN - requisition.width;
1673 else if (pos[0] == -2)
1674 *x = MENU_MARGIN;
1675 else
1676 *x = pos[0] - (requisition.width >> 2);
1678 if (pos[1] == -1)
1679 *y = screen_height - MENU_MARGIN - requisition.height;
1680 else if (pos[1] == -2)
1681 *y = MENU_MARGIN;
1682 else
1683 *y = pos[1] - (requisition.height >> 2);
1685 *x = CLAMP(*x, 0, screen_width - requisition.width);
1686 *y = CLAMP(*y, 0, screen_height - requisition.height);
1688 *push_in = FALSE;
1691 static void panel_show_menu(GdkEventButton *event, PanelIcon *pi, Panel *panel)
1693 PanelSide side = panel->side;
1694 int pos[3];
1696 pos[0] = event->x_root;
1697 pos[1] = event->y_root;
1698 pos[2] = 1;
1700 icon_prepare_menu((Icon *) pi);
1702 if (side == PANEL_LEFT)
1703 pos[0] = -2;
1704 else if (side == PANEL_RIGHT)
1705 pos[0] = -1;
1707 if (side == PANEL_TOP)
1708 pos[1] = -2;
1709 else if (side == PANEL_BOTTOM)
1710 pos[1] = -1;
1712 gtk_menu_popup(GTK_MENU(icon_menu), NULL, NULL,
1713 panel_position_menu,
1714 (gpointer) pos, event->button, event->time);