r523: Converted AppMenu file format to XML.
[rox-filer.git] / ROX-Filer / src / panel.c
blobca49b7da934a0394c61b582af7daa6e386cdfb42
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2001, 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>
31 #include <gtk/gtkinvisible.h>
32 #include <gtk/gtk.h>
34 #include "global.h"
36 #include "panel.h"
37 #include "options.h"
38 #include "choices.h"
39 #include "main.h"
40 #include "gui_support.h"
41 #include "dir.h"
42 #include "pixmaps.h"
43 #include "display.h"
44 #include "bind.h"
45 #include "run.h"
46 #include "dnd.h"
47 #include "menu.h"
48 #include "support.h"
49 #include "mount.h"
50 #include "filer.h"
51 #include "icon.h"
52 #include "appmenu.h"
54 static Panel *current_panel[PANEL_NUMBER_OF_SIDES];
56 /* Widget which holds the selection when we have it */
57 static GtkWidget *selection_invisible = NULL;
58 static guint losing_selection = 0; /* > 0 => Don't send events */
60 struct _Panel {
61 GtkWidget *window;
62 int height;
63 GtkAdjustment *adj; /* Scroll position of the bar */
64 PanelSide side;
65 guchar *name; /* Leaf name */
67 GtkWidget *before; /* Icons at the left/top end */
68 GtkWidget *after; /* Icons at the right/bottom end */
70 GtkWidget *gap; /* Event box between sides */
73 /* NULL => Not loading a panel */
74 static Panel *loading_panel = NULL;
76 /* Static prototypes */
77 static int panel_delete(GtkWidget *widget, GdkEvent *event, Panel *panel);
78 static void panel_destroyed(GtkWidget *widget, Panel *panel);
79 static char *pan_from_file(guchar *line);
80 static void icon_destroyed(Icon *icon);
81 static gint icon_button_release(GtkWidget *widget,
82 GdkEventButton *event,
83 Icon *icon);
84 static gint icon_button_press(GtkWidget *widget,
85 GdkEventButton *event,
86 Icon *icon);
87 static void reposition_panel(Panel *panel);
88 static void icon_set_selected(Icon *icon, gboolean selected);
89 static gint expose_icon(GtkWidget *widget,
90 GdkEventExpose *event,
91 Icon *icon);
92 static gint draw_icon(GtkWidget *widget,
93 GdkRectangle *badarea,
94 Icon *icon);
95 static void popup_panel_menu(GdkEventButton *event,
96 Panel *panel,
97 Icon *icon);
98 static gint panel_button_release(GtkWidget *widget,
99 GdkEventButton *event,
100 Panel *panel);
101 static gint panel_button_press(GtkWidget *widget,
102 GdkEventButton *event,
103 Panel *panel);
104 static void size_icon(Icon *icon);
105 static void drag_set_panel_dest(Icon *icon);
106 static void add_uri_list(GtkWidget *widget,
107 GdkDragContext *context,
108 gint x,
109 gint y,
110 GtkSelectionData *selection_data,
111 guint info,
112 guint32 time,
113 Panel *panel);
114 static void panel_add_item(Panel *panel,
115 guchar *path,
116 guchar *name,
117 gboolean after);
118 static void menu_closed(GtkWidget *widget);
119 static void remove_items(gpointer data, guint action, GtkWidget *widget);
120 static void edit_icon(gpointer data, guint action, GtkWidget *widget);
121 static void show_location(gpointer data, guint action, GtkWidget *widget);
122 static void show_help(gpointer data, guint action, GtkWidget *widget);
123 static gboolean drag_motion(GtkWidget *widget,
124 GdkDragContext *context,
125 gint x,
126 gint y,
127 guint time,
128 Icon *icon);
129 static void drag_leave(GtkWidget *widget,
130 GdkDragContext *context,
131 guint32 time,
132 Icon *icon);
133 static void panel_save(Panel *panel);
134 static GList *get_widget_list(Panel *panel);
135 static GtkWidget *make_insert_frame(Panel *panel);
136 static gboolean enter_icon(GtkWidget *widget,
137 GdkEventCrossing *event,
138 Icon *icon);
139 static gint lose_selection(GtkWidget *widget, GdkEventSelection *event);
140 static void selection_get(GtkWidget *widget,
141 GtkSelectionData *selection_data,
142 guint info,
143 guint time,
144 gpointer data);
145 static gint icon_motion_event(GtkWidget *widget,
146 GdkEventMotion *event,
147 Icon *icon);
148 static gint panel_motion_event(GtkWidget *widget,
149 GdkEventMotion *event,
150 Panel *panel);
151 static void reposition_icon(Icon *icon, int index);
152 static void start_drag(Icon *icon, GdkEventMotion *event);
153 static guchar *create_uri_list(GList *list);
154 static void drag_end(GtkWidget *widget,
155 GdkDragContext *context,
156 Icon *icon);
157 static void perform_action(Panel *panel,
158 Icon *icon,
159 GdkEventButton *event);
162 static GtkItemFactoryEntry menu_def[] = {
163 {N_("ROX-Filer Help"), NULL, menu_rox_help, 0, NULL},
164 {N_("ROX-Filer Options..."), NULL, menu_show_options, 0, NULL},
165 {N_("Open Home Directory"), NULL, open_home, 0, NULL},
166 {"", NULL, NULL, 0, "<Separator>"},
167 {N_("Edit Icon"), NULL, edit_icon, 0, NULL},
168 {N_("Show Location"), NULL, show_location, 0, NULL},
169 {N_("Show Help"), NULL, show_help, 0, NULL},
170 {N_("Remove Item(s)"), NULL, remove_items, 0, NULL},
173 static GtkWidget *panel_menu = NULL;
174 static gboolean tmp_icon_selected = FALSE;
176 /* A list of selected Icons */
177 static GList *panel_selection = NULL;
179 static GtkWidget *dnd_highlight = NULL; /* (stops flickering) */
181 /* When sliding the panel, records where the panel was before */
182 static gint slide_from_value = 0;
185 /****************************************************************
186 * EXTERNAL INTERFACE *
187 ****************************************************************/
189 void panel_init(void)
191 GtkTargetEntry target_table[] =
193 {"text/uri-list", 0, TARGET_URI_LIST},
194 {"STRING", 0, TARGET_STRING},
197 panel_menu = menu_create(menu_def,
198 sizeof(menu_def) / sizeof(*menu_def),
199 "<panel>");
200 gtk_signal_connect(GTK_OBJECT(panel_menu), "unmap_event",
201 GTK_SIGNAL_FUNC(menu_closed), NULL);
203 selection_invisible = gtk_invisible_new();
205 gtk_signal_connect(GTK_OBJECT(selection_invisible),
206 "selection_clear_event",
207 GTK_SIGNAL_FUNC(lose_selection),
208 NULL);
210 gtk_signal_connect(GTK_OBJECT(selection_invisible),
211 "selection_get",
212 GTK_SIGNAL_FUNC(selection_get), NULL);
214 gtk_selection_add_targets(selection_invisible,
215 GDK_SELECTION_PRIMARY,
216 target_table,
217 sizeof(target_table) / sizeof(*target_table));
220 /* 'name' may be NULL or "" to remove the panel */
221 Panel *panel_new(guchar *name, PanelSide side)
223 guchar *load_path;
224 Panel *panel;
225 GtkWidget *vp, *box, *frame;
227 g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, NULL);
228 g_return_val_if_fail(loading_panel == NULL, NULL);
230 if (name && *name == '\0')
231 name = NULL;
233 if (current_panel[side])
235 if (name)
236 number_of_windows++;
237 gtk_widget_destroy(current_panel[side]->window);
238 if (name)
239 number_of_windows--;
242 if (name == NULL || *name == '\0')
243 return NULL;
245 panel = g_new(Panel, 1);
246 panel->name = g_strdup(name);
247 panel->side = side;
248 panel->height = 0;
249 panel->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
250 gtk_window_set_wmclass(GTK_WINDOW(panel->window), "ROX-Panel", PROJECT);
251 gtk_widget_set_events(panel->window,
252 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
253 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
254 GDK_BUTTON2_MOTION_MASK);
256 gtk_signal_connect(GTK_OBJECT(panel->window), "delete-event",
257 GTK_SIGNAL_FUNC(panel_delete), panel);
258 gtk_signal_connect(GTK_OBJECT(panel->window), "destroy",
259 GTK_SIGNAL_FUNC(panel_destroyed), panel);
260 gtk_signal_connect(GTK_OBJECT(panel->window), "button_press_event",
261 GTK_SIGNAL_FUNC(panel_button_press), panel);
262 gtk_signal_connect(GTK_OBJECT(panel->window), "button_release_event",
263 GTK_SIGNAL_FUNC(panel_button_release), panel);
264 gtk_signal_connect(GTK_OBJECT(panel->window), "motion-notify-event",
265 GTK_SIGNAL_FUNC(panel_motion_event), panel);
267 if (strchr(name, '/'))
268 load_path = g_strdup(name);
269 else
271 guchar *leaf;
273 leaf = g_strconcat("pan_", name, NULL);
274 load_path = choices_find_path_load(leaf, "ROX-Filer");
275 g_free(leaf);
278 vp = gtk_viewport_new(NULL, NULL);
279 gtk_viewport_set_shadow_type(GTK_VIEWPORT(vp), GTK_SHADOW_OUT);
280 gtk_container_add(GTK_CONTAINER(panel->window), vp);
282 if (side == PANEL_TOP || side == PANEL_BOTTOM)
284 panel->adj = gtk_viewport_get_hadjustment(GTK_VIEWPORT(vp));
285 box = gtk_hbox_new(FALSE, 0);
286 panel->before = gtk_hbox_new(FALSE, 0);
287 panel->after = gtk_hbox_new(FALSE, 0);
289 else
291 panel->adj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(vp));
292 box = gtk_vbox_new(FALSE, 0);
293 panel->before = gtk_vbox_new(FALSE, 0);
294 panel->after = gtk_vbox_new(FALSE, 0);
297 gtk_container_add(GTK_CONTAINER(vp), box);
298 gtk_box_pack_start(GTK_BOX(box), panel->before, FALSE, TRUE, 0);
299 gtk_box_pack_end(GTK_BOX(box), panel->after, FALSE, TRUE, 0);
301 frame = make_insert_frame(panel);
302 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
304 /* This is used so that we can find the middle easily! */
305 panel->gap = gtk_event_box_new();
306 gtk_box_pack_start(GTK_BOX(box), panel->gap, FALSE, FALSE, 0);
308 frame = make_insert_frame(panel);
309 gtk_object_set_data(GTK_OBJECT(frame), "after", "yes");
310 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
312 gtk_widget_realize(panel->window);
313 make_panel_window(panel->window->window);
315 loading_panel = panel;
316 if (load_path && access(load_path, F_OK) == 0)
317 parse_file(load_path, pan_from_file);
318 else
320 /* Don't scare users with an empty panel... */
321 guchar *apps;
323 panel_add_item(panel, "~", "Home", FALSE);
325 apps = pathdup(make_path(app_dir, "..")->str);
326 if (apps)
328 panel_add_item(panel, apps, "Apps", FALSE);
329 g_free(apps);
332 loading_panel = NULL;
333 g_free(load_path);
335 current_panel[side] = panel;
337 reposition_panel(panel);
339 number_of_windows++;
340 gtk_widget_show_all(panel->window);
342 return panel;
345 /* See if the file the icon points to has changed. Update the icon
346 * if so.
348 void panel_icon_may_update(Icon *icon)
350 MaskedPixmap *image = icon->item.image;
351 int flags = icon->item.flags;
353 pixmap_ref(image);
354 mount_update(FALSE);
355 dir_restat(icon->path, &icon->item, FALSE);
357 if (icon->item.image != image || icon->item.flags != flags)
359 size_icon(icon);
360 gtk_widget_queue_clear(icon->widget);
361 reposition_panel(icon->panel);
364 pixmap_unref(image);
369 /****************************************************************
370 * INTERNAL FUNCTIONS *
371 ****************************************************************/
373 /* User has tried to close the panel via the window manager - confirm */
374 static int panel_delete(GtkWidget *widget, GdkEvent *event, Panel *panel)
376 /* TODO: We can open lots of these - very irritating! */
377 return get_choice(_("Close panel?"),
378 _("You have tried to close a panel via the window "
379 "manager - I usually find that this is accidental... "
380 "really close?"),
381 2, _("Remove"), _("Cancel")) != 0;
384 static void panel_destroyed(GtkWidget *widget, Panel *panel)
386 if (current_panel[panel->side] == panel)
387 current_panel[panel->side] = NULL;
389 if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
391 if (current_panel[PANEL_RIGHT])
392 reposition_panel(current_panel[PANEL_RIGHT]);
393 if (current_panel[PANEL_LEFT])
394 reposition_panel(current_panel[PANEL_LEFT]);
397 g_free(panel->name);
398 g_free(panel);
400 if (--number_of_windows < 1)
401 gtk_main_quit();
404 /* Called for each line in the config file while loading a new panel */
405 static char *pan_from_file(guchar *line)
407 guchar *sep, *leaf;
409 g_return_val_if_fail(line != NULL, NULL);
410 g_return_val_if_fail(loading_panel != NULL, NULL);
412 if (*line == '\0')
413 return NULL;
415 sep = strpbrk(line, "<>");
416 if (!sep)
417 return _("Missing < or > in panel config file");
419 if (sep != line)
420 leaf = g_strndup(line, sep - line);
421 else
422 leaf = NULL;
424 panel_add_item(loading_panel,
425 sep + 1,
426 leaf,
427 sep[0] == '>');
429 g_free(leaf);
431 return NULL;
434 /* Add an icon with this path to the panel. If after is TRUE then the
435 * icon is added to the right/bottom end of the panel.
437 * If name is NULL a suitable name is taken from path.
439 static void panel_add_item(Panel *panel,
440 guchar *path,
441 guchar *name,
442 gboolean after)
444 GtkWidget *widget;
445 Icon *icon;
446 GdkFont *font;
448 widget = gtk_event_box_new();
449 gtk_widget_set_events(widget,
450 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
451 GDK_BUTTON3_MOTION_MASK |
452 GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
453 GDK_BUTTON_RELEASE_MASK);
455 gtk_box_pack_start(GTK_BOX(after ? panel->after : panel->before),
456 widget, FALSE, TRUE, 4);
457 if (after)
458 gtk_box_reorder_child(GTK_BOX(panel->after), widget, 0);
460 gtk_widget_realize(widget);
461 font = widget->style->font;
463 icon = g_new(Icon, 1);
464 icon->type = ICON_PANEL;
465 icon->panel = panel;
466 icon->src_path = g_strdup(path);
467 icon->path = icon_convert_path(path);
469 gtk_object_set_data(GTK_OBJECT(widget), "icon", icon);
471 icon_hash_path(icon);
473 icon->widget = widget;
474 icon->selected = FALSE;
475 dir_stat(icon->path, &icon->item, FALSE);
477 if (name)
478 icon->item.leafname = g_strdup(name);
479 else
481 guchar *slash;
483 slash = strrchr(icon->path, '/');
484 icon->item.leafname = g_strdup(slash && slash[1] ? slash + 1
485 : path);
488 icon->item.name_width = gdk_string_width(font, icon->item.leafname);
490 gtk_signal_connect_after(GTK_OBJECT(widget), "enter-notify-event",
491 GTK_SIGNAL_FUNC(enter_icon), icon);
492 gtk_signal_connect(GTK_OBJECT(icon->widget), "motion-notify-event",
493 GTK_SIGNAL_FUNC(icon_motion_event), icon);
494 gtk_signal_connect_after(GTK_OBJECT(widget), "draw",
495 GTK_SIGNAL_FUNC(draw_icon), icon);
496 gtk_signal_connect_after(GTK_OBJECT(widget), "expose_event",
497 GTK_SIGNAL_FUNC(expose_icon), icon);
498 gtk_signal_connect(GTK_OBJECT(widget), "button_release_event",
499 GTK_SIGNAL_FUNC(icon_button_release), icon);
500 gtk_signal_connect(GTK_OBJECT(widget), "button_press_event",
501 GTK_SIGNAL_FUNC(icon_button_press), icon);
502 gtk_signal_connect(GTK_OBJECT(widget), "drag_data_get",
503 drag_data_get, NULL);
505 gtk_signal_connect_object(GTK_OBJECT(widget), "destroy",
506 GTK_SIGNAL_FUNC(icon_destroyed), (gpointer) icon);
508 drag_set_panel_dest(icon);
510 size_icon(icon);
512 if (!loading_panel)
514 reposition_panel(panel);
515 panel_save(panel);
518 gtk_widget_show(widget);
521 /* Set the size of the widget for this icon.
522 * You should call reposition_panel() sometime after doing this...
524 static void size_icon(Icon *icon)
526 int im_height = MIN(icon->item.image->height, MAX_ICON_HEIGHT - 4);
527 GdkFont *font = icon->widget->style->font;
528 int width, height;
530 width = MAX(icon->item.image->width, icon->item.name_width) + 4;
531 height = font->ascent + font->descent + 2 + im_height;
533 gtk_widget_set_usize(icon->widget, width, height);
536 static gint expose_icon(GtkWidget *widget,
537 GdkEventExpose *event,
538 Icon *icon)
540 return draw_icon(widget, &event->area, icon);
543 static gint draw_icon(GtkWidget *widget, GdkRectangle *badarea, Icon *icon)
545 GdkFont *font = widget->style->font;
546 GdkRectangle area;
547 int width, height;
548 int text_x, text_y;
550 gdk_window_get_size(widget->window, &width, &height);
552 area.x = 0;
553 area.y = 0;
554 area.width = width;
555 area.height = height - font->ascent - font->descent - 2;
557 draw_large_icon(widget, &area, &icon->item, icon->selected);
559 text_x = (area.width - icon->item.name_width) >> 1;
561 text_y = height - font->descent;
562 if (icon->panel->side == PANEL_TOP || icon->panel->side == PANEL_BOTTOM)
563 text_y -= 4;
565 draw_string(widget,
566 font,
567 icon->item.leafname,
568 MAX(0, text_x),
569 text_y,
570 icon->item.name_width,
571 area.width,
572 icon->selected, TRUE);
574 return TRUE;
577 static void icon_destroyed(Icon *icon)
579 icon_unhash_path(icon);
581 if (g_list_find(panel_selection, icon))
583 panel_selection = g_list_remove(panel_selection, icon);
585 if (!panel_selection)
586 gtk_selection_owner_set(NULL,
587 GDK_SELECTION_PRIMARY,
588 gdk_event_get_time(gtk_get_current_event()));
591 dir_item_clear(&icon->item);
592 g_free(icon->path);
593 g_free(icon->src_path);
594 g_free(icon);
597 /* Clear everything, except 'select', which is selected.
598 * If select is NULL, unselects everything.
600 static void panel_clear_selection(Icon *select)
602 GList *to_clear, *next;
604 if (select)
605 icon_set_selected(select, TRUE);
607 to_clear = g_list_copy(panel_selection), select;
609 if (select)
610 to_clear = g_list_remove(g_list_copy(panel_selection), select);
612 for (next = to_clear; next; next = next->next)
613 icon_set_selected((Icon *) next->data, FALSE);
615 g_list_free(to_clear);
618 static void icon_set_selected(Icon *icon, gboolean selected)
620 gboolean clear = FALSE;
622 g_return_if_fail(icon != NULL);
624 if (icon->selected == selected)
625 return;
627 /* When selecting an icon on another panel, we need to unselect
628 * everything else afterwards.
630 if (selected && panel_selection)
632 Icon *current = (Icon *) panel_selection->data;
634 if (icon->panel != current->panel)
635 clear = TRUE;
638 icon->selected = selected;
639 gtk_widget_queue_clear(icon->widget);
641 if (selected)
643 panel_selection = g_list_prepend(panel_selection, icon);
644 if (losing_selection == 0 && !panel_selection->next)
646 /* Grab selection */
647 gtk_selection_owner_set(selection_invisible,
648 GDK_SELECTION_PRIMARY,
649 gdk_event_get_time(gtk_get_current_event()));
652 else
654 panel_selection = g_list_remove(panel_selection, icon);
655 if (losing_selection == 0 && !panel_selection)
657 /* Release selection */
658 gtk_selection_owner_set(NULL,
659 GDK_SELECTION_PRIMARY,
660 gdk_event_get_time(gtk_get_current_event()));
664 if (clear)
665 panel_clear_selection(icon);
668 /* icon may be NULL if the event is on the background */
669 static void perform_action(Panel *panel, Icon *icon, GdkEventButton *event)
671 GtkWidget *widget = icon ? icon->widget : NULL;
672 BindAction action;
674 action = bind_lookup_bev(icon ? BIND_PANEL_ICON : BIND_PANEL, event);
676 switch (action)
678 case ACT_OPEN_ITEM:
679 dnd_motion_ungrab();
680 wink_widget(icon->widget);
681 run_diritem(icon->path, &icon->item, NULL, FALSE);
682 break;
683 case ACT_EDIT_ITEM:
684 dnd_motion_ungrab();
685 wink_widget(icon->widget);
686 run_diritem(icon->path, &icon->item, NULL, TRUE);
687 break;
688 case ACT_POPUP_MENU:
689 dnd_motion_ungrab();
690 popup_panel_menu(event, panel, icon);
691 break;
692 case ACT_MOVE_ICON:
693 dnd_motion_start(MOTION_REPOSITION);
694 break;
695 case ACT_PRIME_AND_SELECT:
696 if (!icon->selected)
697 panel_clear_selection(icon);
698 dnd_motion_start(MOTION_READY_FOR_DND);
699 break;
700 case ACT_PRIME_AND_TOGGLE:
701 icon_set_selected(icon, !icon->selected);
702 dnd_motion_start(MOTION_READY_FOR_DND);
703 break;
704 case ACT_PRIME_FOR_DND:
705 wink_widget(widget);
706 dnd_motion_start(MOTION_READY_FOR_DND);
707 break;
708 case ACT_TOGGLE_SELECTED:
709 icon_set_selected(icon, !icon->selected);
710 break;
711 case ACT_SELECT_EXCL:
712 icon_set_selected(icon, TRUE);
713 break;
714 case ACT_SLIDE_CLEAR_PANEL:
715 panel_clear_selection(NULL);
716 /* (no break) */
717 case ACT_SLIDE_PANEL:
718 dnd_motion_grab_pointer();
719 slide_from_value = panel->adj->value;
720 dnd_motion_start(MOTION_REPOSITION);
721 break;
722 case ACT_IGNORE:
723 break;
724 case ACT_CLEAR_SELECTION:
725 panel_clear_selection(NULL);
726 break;
727 default:
728 g_warning("Unsupported action : %d\n", action);
729 break;
733 static gint panel_button_release(GtkWidget *widget,
734 GdkEventButton *event,
735 Panel *panel)
737 if (dnd_motion_release(event))
738 return TRUE;
740 perform_action(panel, NULL, event);
742 return TRUE;
745 static gint panel_button_press(GtkWidget *widget,
746 GdkEventButton *event,
747 Panel *panel)
749 if (dnd_motion_press(panel->window, event))
750 perform_action(panel, NULL, event);
752 return TRUE;
755 static gint icon_button_release(GtkWidget *widget,
756 GdkEventButton *event,
757 Icon *icon)
759 if (dnd_motion_release(event))
760 return TRUE;
762 perform_action(icon->panel, icon, event);
764 return TRUE;
767 static gint icon_button_press(GtkWidget *widget,
768 GdkEventButton *event,
769 Icon *icon)
771 if (dnd_motion_press(widget, event))
772 perform_action(icon->panel, icon, event);
774 return TRUE;
777 /* Difference between height of an icon and height of panel (or width) */
778 #define MARGIN 8
780 static void reposition_panel(Panel *panel)
782 int x = 0, y = 0;
783 int w = 32, h = 32;
784 GList *next, *children;
785 PanelSide side = panel->side;
787 children = get_widget_list(panel);
789 for (next = children; next; next = next->next)
791 GtkWidget *widget = (GtkWidget *) next->data;
792 GtkRequisition req;
794 gtk_widget_get_child_requisition(widget, &req);
796 if (req.width > w)
797 w = req.width;
798 if (req.height > h)
799 h = req.height;
802 g_list_free(children);
804 if (side == PANEL_TOP || side == PANEL_BOTTOM)
806 w = screen_width;
807 h += MARGIN;
809 else
811 w += MARGIN;
812 if (side == PANEL_RIGHT)
813 x = screen_width - w;
815 h = screen_height;
817 if (current_panel[PANEL_TOP])
819 int ph;
821 ph = current_panel[PANEL_TOP]->height;
822 y += ph;
823 h -= ph;
826 if (current_panel[PANEL_BOTTOM])
827 h -= current_panel[PANEL_BOTTOM]->height;
830 if (side == PANEL_BOTTOM)
831 y = screen_height - h;
833 gtk_widget_set_uposition(panel->window, x, y);
834 panel->height = h;
835 gtk_widget_set_usize(panel->window, w, h);
836 gdk_window_move_resize(panel->window->window, x, y, w, h);
838 if (side == PANEL_BOTTOM || side == PANEL_TOP)
840 if (current_panel[PANEL_RIGHT])
841 reposition_panel(current_panel[PANEL_RIGHT]);
842 if (current_panel[PANEL_LEFT])
843 reposition_panel(current_panel[PANEL_LEFT]);
847 static void panel_position_menu(GtkMenu *menu, gint *x, gint *y, gpointer data)
849 int *pos = (int *) data;
850 GtkRequisition requisition;
852 gtk_widget_size_request(GTK_WIDGET(menu), &requisition);
854 if (pos[0] == -1)
855 *x = screen_width - MENU_MARGIN - requisition.width;
856 else if (pos[0] == -2)
857 *x = MENU_MARGIN;
858 else
859 *x = pos[0] - (requisition.width >> 2);
861 if (pos[1] == -1)
862 *y = screen_height - MENU_MARGIN - requisition.height;
863 else if (pos[1] == -2)
864 *y = MENU_MARGIN;
865 else
866 *y = pos[1] - (requisition.height >> 2);
868 *x = CLAMP(*x, 0, screen_width - requisition.width);
869 *y = CLAMP(*y, 0, screen_height - requisition.height);
873 static void popup_panel_menu(GdkEventButton *event,
874 Panel *panel,
875 Icon *icon)
877 int pos[2];
878 PanelSide side = panel->side;
880 appmenu_remove();
882 if (icon != NULL)
884 Icon *current = panel_selection
885 ? ((Icon *) panel_selection->data)
886 : NULL;
888 if (current && current->panel != panel)
889 panel_clear_selection(icon);
891 if (panel_selection == NULL)
893 icon_set_selected(icon, TRUE);
895 /* Unselect when panel closes */
896 tmp_icon_selected = TRUE;
900 if (side == PANEL_LEFT)
901 pos[0] = -2;
902 else if (side == PANEL_RIGHT)
903 pos[0] = -1;
904 else
905 pos[0] = event->x_root;
907 if (side == PANEL_TOP)
908 pos[1] = -2;
909 else if (side == PANEL_BOTTOM)
910 pos[1] = -1;
911 else
912 pos[1] = event->y_root;
914 /* Shade Remove Item(s) unless there is a selection */
915 menu_set_items_shaded(panel_menu,
916 panel_selection ? FALSE : TRUE,
917 7, 1);
919 /* Shade the Rename/Location/Help items unless exactly one item is
920 * selected.
922 if (panel_selection == NULL || panel_selection->next)
923 menu_set_items_shaded(panel_menu, TRUE, 4, 3);
924 else
926 Icon *icon = (Icon *) panel_selection->data;
928 menu_set_items_shaded(panel_menu, FALSE, 4, 3);
930 /* Check for app-specific menu */
931 appmenu_add(icon->path, &icon->item, panel_menu);
934 gtk_menu_popup(GTK_MENU(panel_menu), NULL, NULL, panel_position_menu,
935 (gpointer) pos, event->button, event->time);
938 static void rename_cb(Icon *icon)
942 size_icon(icon);
943 gtk_widget_queue_clear(icon->widget);
944 reposition_panel(icon->panel);
946 panel_save(icon->panel);
949 static void edit_icon(gpointer data, guint action, GtkWidget *widget)
951 Icon *icon;
953 if (panel_selection == NULL || panel_selection->next)
955 delayed_error(PROJECT,
956 _("First, select a single item to edit"));
957 return;
960 icon = (Icon *) panel_selection->data;
961 show_rename_box(icon->widget, icon, rename_cb);
964 static void show_location(gpointer data, guint action, GtkWidget *widget)
966 Icon *icon;
968 if (panel_selection == NULL || panel_selection->next)
970 delayed_error(PROJECT,
971 _("Select a single item, then use this to find out "
972 "where it is in the filesystem."));
973 return;
976 icon = (Icon *) panel_selection->data;
978 open_to_show(icon->path);
981 static void show_help(gpointer data, guint action, GtkWidget *widget)
983 Icon *icon;
985 if (panel_selection == NULL || panel_selection->next)
987 delayed_error(PROJECT,
988 _("You must select a single item to get help on"));
989 return;
992 icon = (Icon *) panel_selection->data;
993 show_item_help(icon->path, &icon->item);
996 static void remove_items(gpointer data, guint action, GtkWidget *widget)
998 Panel *panel;
999 GList *next = panel_selection;
1001 if (!next)
1003 delayed_error(PROJECT,
1004 _("You must first select some icons to remove"));
1005 return;
1008 panel = ((Icon *) next->data)->panel;
1010 while (next)
1012 Icon *icon = (Icon *) next->data;
1014 next = next->next;
1016 gtk_widget_destroy(icon->widget);
1019 panel_save(panel);
1021 reposition_panel(panel);
1024 /* Same as drag_set_dest(), but for panel icons */
1025 static void drag_set_panel_dest(Icon *icon)
1027 GtkObject *obj = GTK_OBJECT(icon->widget);
1029 make_drop_target(icon->widget, 0);
1031 gtk_signal_connect(obj, "drag_motion",
1032 GTK_SIGNAL_FUNC(drag_motion), icon);
1033 gtk_signal_connect(obj, "drag_leave",
1034 GTK_SIGNAL_FUNC(drag_leave), icon);
1035 gtk_signal_connect(obj, "drag_end",
1036 GTK_SIGNAL_FUNC(drag_end), icon);
1039 static gboolean drag_motion(GtkWidget *widget,
1040 GdkDragContext *context,
1041 gint x,
1042 gint y,
1043 guint time,
1044 Icon *icon)
1046 GdkDragAction action = context->suggested_action;
1047 char *type = NULL;
1048 DirItem *item = &icon->item;
1050 if (icon->selected)
1051 goto out; /* Can't drag a selection to itself */
1053 type = dnd_motion_item(context, &item);
1055 if (!item)
1056 type = NULL;
1057 out:
1058 /* We actually must pretend to accept the drop, even if the
1059 * directory isn't writeable, so that the spring-opening
1060 * thing works.
1063 /* Don't allow drops to non-writeable directories */
1064 if (option_get_int("dnd_spring_open") == FALSE &&
1065 type == drop_dest_dir &&
1066 access(icon->path, W_OK) != 0)
1068 type = NULL;
1071 g_dataset_set_data(context, "drop_dest_type", type);
1072 if (type)
1074 gdk_drag_status(context, action, time);
1075 g_dataset_set_data_full(context, "drop_dest_path",
1076 g_strdup(icon->path), g_free);
1077 if (type == drop_dest_dir)
1078 dnd_spring_load(context);
1080 if (dnd_highlight && dnd_highlight != icon->widget)
1082 gtk_drag_unhighlight(dnd_highlight);
1083 dnd_highlight = NULL;
1086 if (dnd_highlight == NULL)
1088 gtk_drag_highlight(icon->widget);
1089 dnd_highlight = icon->widget;
1093 return type != NULL;
1097 static void add_uri_list(GtkWidget *widget,
1098 GdkDragContext *context,
1099 gint x,
1100 gint y,
1101 GtkSelectionData *selection_data,
1102 guint info,
1103 guint32 time,
1104 Panel *panel)
1106 gboolean after = FALSE;
1107 GSList *uris, *next;
1109 if (!selection_data->data)
1110 return;
1112 g_return_if_fail(selection_data->data[selection_data->length] == '\0');
1114 if (gtk_object_get_data(GTK_OBJECT(widget), "after"))
1115 after = TRUE;
1117 uris = uri_list_to_gslist(selection_data->data);
1119 for (next = uris; next; next = next->next)
1121 guchar *path;
1123 path = get_local_path((guchar *) next->data);
1125 if (path)
1126 panel_add_item(panel, path, NULL, after);
1129 g_slist_free(uris);
1132 static void drag_end(GtkWidget *widget,
1133 GdkDragContext *context,
1134 Icon *icon)
1136 if (tmp_icon_selected)
1138 panel_clear_selection(NULL);
1139 tmp_icon_selected = FALSE;
1143 static void menu_closed(GtkWidget *widget)
1145 if (tmp_icon_selected)
1147 panel_clear_selection(NULL);
1148 tmp_icon_selected = FALSE;
1152 static void drag_leave(GtkWidget *widget,
1153 GdkDragContext *context,
1154 guint32 time,
1155 Icon *icon)
1157 if (dnd_highlight && dnd_highlight == widget)
1159 gtk_drag_unhighlight(dnd_highlight);
1160 dnd_highlight = NULL;
1163 dnd_spring_abort();
1166 /* Writes lines to the file, one for each widget, prefixed by 'side'.
1167 * Returns TRUE on success, or FALSE on error (and sets errno).
1168 * Always frees the widgets list.
1170 static gboolean write_widgets(FILE *file, GList *widgets, guchar side)
1172 GList *next;
1173 GString *tmp;
1175 tmp = g_string_new(NULL);
1177 for (next = widgets; next; next = next->next)
1179 Icon *icon;
1181 icon = gtk_object_get_data(GTK_OBJECT(next->data), "icon");
1183 if (!icon)
1185 g_warning("Can't find Icon from widget\n");
1186 continue;
1189 g_string_sprintf(tmp, "%s%c%s\n",
1190 icon->item.leafname,
1191 side, icon->src_path);
1192 if (fwrite(tmp->str, 1, tmp->len, file) < tmp->len)
1194 g_list_free(widgets);
1195 return FALSE;
1199 g_string_free(tmp, TRUE);
1201 if (widgets)
1202 g_list_free(widgets);
1204 return TRUE;
1207 static void panel_save(Panel *panel)
1209 guchar *save = NULL;
1210 FILE *file = NULL;
1211 guchar *save_new = NULL;
1213 g_return_if_fail(panel != NULL);
1215 if (strchr(panel->name, '/'))
1216 save = g_strdup(panel->name);
1217 else
1219 guchar *leaf;
1221 leaf = g_strconcat("pan_", panel->name, NULL);
1222 save = choices_find_path_save(leaf, "ROX-Filer", TRUE);
1223 g_free(leaf);
1226 if (!save)
1227 return;
1229 save_new = g_strconcat(save, ".new", NULL);
1230 file = fopen(save_new, "wb");
1231 if (!file)
1232 goto err;
1234 if (!write_widgets(file,
1235 gtk_container_children(GTK_CONTAINER(panel->before)),
1236 '<'))
1237 goto err;
1239 if (!write_widgets(file,
1240 g_list_reverse(gtk_container_children(
1241 GTK_CONTAINER(panel->after))),
1242 '>'))
1243 goto err;
1245 if (fclose(file))
1247 file = NULL;
1248 goto err;
1251 file = NULL;
1253 if (rename(save_new, save))
1254 goto err;
1256 goto out;
1257 err:
1258 delayed_error(_("Error saving panel"), g_strerror(errno));
1259 out:
1260 if (file)
1261 fclose(file);
1262 g_free(save);
1263 g_free(save_new);
1266 static GList *get_widget_list(Panel *panel)
1268 GList *list;
1270 list = gtk_container_children(GTK_CONTAINER(panel->before));
1271 list = g_list_concat(list,
1272 gtk_container_children(GTK_CONTAINER(panel->after)));
1274 return list;
1277 /* Create a frame widget which can be used to add icons to the panel */
1278 static GtkWidget *make_insert_frame(Panel *panel)
1280 GtkWidget *frame;
1281 GtkTargetEntry target_table[] = {
1282 {"text/uri-list", 0, TARGET_URI_LIST},
1285 frame = gtk_frame_new(NULL);
1286 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
1287 gtk_widget_set_usize(frame, 16, 16);
1289 gtk_signal_connect(GTK_OBJECT(frame), "drag-data-received",
1290 GTK_SIGNAL_FUNC(add_uri_list), panel);
1291 gtk_drag_dest_set(frame,
1292 GTK_DEST_DEFAULT_ALL,
1293 target_table,
1294 sizeof(target_table) / sizeof(*target_table),
1295 GDK_ACTION_COPY);
1297 return frame;
1300 static gboolean enter_icon(GtkWidget *widget,
1301 GdkEventCrossing *event,
1302 Icon *icon)
1304 panel_icon_may_update(icon);
1306 return FALSE;
1309 /* Called when another application wants the contents of our selection */
1310 static void selection_get(GtkWidget *widget,
1311 GtkSelectionData *selection_data,
1312 guint info,
1313 guint time,
1314 gpointer data)
1316 GString *str;
1317 GList *next;
1318 guchar *leader = NULL;
1320 str = g_string_new(NULL);
1322 if (info == TARGET_URI_LIST)
1323 leader = g_strdup_printf("file://%s", our_host_name());
1325 for (next = panel_selection; next; next = next->next)
1327 Icon *icon = (Icon *) next->data;
1329 if (leader)
1330 g_string_append(str, leader);
1331 g_string_append(str, icon->path);
1332 g_string_append_c(str, ' ');
1335 g_free(leader);
1337 gtk_selection_data_set(selection_data,
1338 gdk_atom_intern("STRING", FALSE),
1340 str->str,
1341 str->len ? str->len - 1 : 0);
1343 g_string_free(str, TRUE);
1346 /* Called when another application takes the selection away from us */
1347 static gint lose_selection(GtkWidget *widget, GdkEventSelection *event)
1349 /* Don't send any events */
1351 losing_selection++;
1352 panel_clear_selection(NULL);
1353 losing_selection--;
1355 return TRUE;
1358 static gint panel_motion_event(GtkWidget *widget,
1359 GdkEventMotion *event,
1360 Panel *panel)
1362 gint delta, new;
1363 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1365 if (motion_state != MOTION_REPOSITION)
1366 return FALSE;
1368 if (horz)
1369 delta = event->x_root - drag_start_x;
1370 else
1371 delta = event->y_root - drag_start_y;
1373 new = slide_from_value - delta;
1374 new = CLAMP(new, 0, panel->adj->upper - panel->adj->page_size);
1376 gtk_adjustment_set_value(panel->adj, new);
1378 return TRUE;
1381 static gint icon_motion_event(GtkWidget *widget,
1382 GdkEventMotion *event,
1383 Icon *icon)
1385 Panel *panel = icon->panel;
1386 GList *list, *me;
1387 gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1388 int val;
1389 int dir = 0;
1391 if (motion_state == MOTION_READY_FOR_DND)
1393 if (dnd_motion_moved(event))
1394 start_drag(icon, event);
1395 return TRUE;
1397 else if (motion_state != MOTION_REPOSITION)
1398 return FALSE;
1400 list = gtk_container_children(GTK_CONTAINER(panel->before));
1401 list = g_list_append(list, NULL); /* The gap in the middle */
1402 list = g_list_concat(list,
1403 gtk_container_children(GTK_CONTAINER(panel->after)));
1404 me = g_list_find(list, widget);
1406 g_return_val_if_fail(me != NULL, TRUE);
1408 val = horz ? event->x_root : event->y_root;
1410 if (me->prev)
1412 GtkWidget *prev;
1413 int x, y;
1415 if (me->prev->data)
1416 prev = GTK_WIDGET(me->prev->data);
1417 else
1418 prev = panel->gap;
1420 gdk_window_get_deskrelative_origin(prev->window, &x, &y);
1422 if (val <= (horz ? x : y))
1423 dir = -1;
1426 if (dir == 0 && me->next)
1428 GtkWidget *next;
1429 int x, y, w, h;
1431 if (me->next->data)
1432 next = GTK_WIDGET(me->next->data);
1433 else
1434 next = panel->gap;
1436 gdk_window_get_deskrelative_origin(next->window, &x, &y);
1438 gdk_window_get_size(next->window, &w, &h);
1440 x += w;
1441 y += h;
1443 if (val >= (horz ? x : y))
1445 if (next == panel->gap)
1446 dir = +2;
1447 else
1448 dir = +1;
1452 if (dir)
1453 reposition_icon(icon, g_list_index(list, widget) + dir);
1455 return TRUE;
1458 /* Move icon to this index in the complete widget list.
1459 * 0 makes the icon the left-most icon. The gap in the middle has
1460 * an index number, which allows you to specify that the icon should
1461 * go on the left or right side.
1463 static void reposition_icon(Icon *icon, int index)
1465 Panel *panel = icon->panel;
1466 GtkWidget *widget = icon->widget;
1467 GList *list;
1468 int before_len;
1470 list = gtk_container_children(GTK_CONTAINER(panel->before));
1471 before_len = g_list_length(list);
1473 if (index <= before_len)
1475 /* Want to move icon to the 'before' list. Is it there
1476 * already?
1479 if (!g_list_find(list, widget))
1481 /* No, reparent */
1482 gtk_grab_remove(widget);
1483 gtk_widget_reparent(widget, panel->before);
1484 dnd_motion_grab_pointer();
1485 gtk_grab_add(widget);
1488 gtk_box_reorder_child(GTK_BOX(panel->before), widget, index);
1490 else
1492 /* Else, we need it in the 'after' list. */
1494 index -= before_len + 1;
1496 g_list_free(list);
1498 list = gtk_container_children(GTK_CONTAINER(panel->after));
1500 if (!g_list_find(list, widget))
1502 /* Not already there, reparent */
1503 gtk_grab_remove(widget);
1504 gtk_widget_reparent(widget, panel->after);
1505 dnd_motion_grab_pointer();
1506 gtk_grab_add(widget);
1509 gtk_box_reorder_child(GTK_BOX(panel->after), widget, index);
1512 g_list_free(list);
1514 panel_save(panel);
1517 static void start_drag(Icon *icon, GdkEventMotion *event)
1519 GtkWidget *widget = icon->widget;
1521 if (!icon->selected)
1523 if (event->state & GDK_BUTTON1_MASK)
1525 /* Select just this one */
1526 panel_clear_selection(icon);
1527 tmp_icon_selected = TRUE;
1529 else
1530 icon_set_selected(icon, TRUE);
1533 g_return_if_fail(panel_selection != NULL);
1535 if (panel_selection->next == NULL)
1536 drag_one_item(widget, event, icon->path, &icon->item);
1537 else
1539 guchar *uri_list;
1541 uri_list = create_uri_list(panel_selection);
1542 drag_selection(widget, event, uri_list);
1543 g_free(uri_list);
1547 /* Return a text/uri-list of all the icons in the list */
1548 static guchar *create_uri_list(GList *list)
1550 GString *tmp;
1551 guchar *retval;
1552 guchar *leader;
1554 tmp = g_string_new(NULL);
1555 leader = g_strdup_printf("file://%s", our_host_name());
1557 for (; list; list = list->next)
1559 Icon *icon = (Icon *) list->data;
1561 g_string_append(tmp, leader);
1562 g_string_append(tmp, icon->path);
1563 g_string_append(tmp, "\r\n");
1566 g_free(leader);
1567 retval = tmp->str;
1568 g_string_free(tmp, FALSE);
1570 return retval;