r2216: Cursor and selection work on view details.
[rox-filer.git] / ROX-Filer / src / view_collection.c
blob1b543a0328aa569bacec9e0f6d3d8f9bdca13a34
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 /* view_collection.c - a subclass of Collection, used for displaying files */
24 #include "config.h"
26 #include <gtk/gtk.h>
27 #include <time.h>
28 #include <math.h>
30 #include "global.h"
32 #include "collection.h"
33 #include "view_iface.h"
34 #include "view_collection.h"
35 #include "type.h"
36 #include "pixmaps.h"
37 #include "dir.h"
38 #include "diritem.h"
39 #include "gui_support.h"
40 #include "support.h"
41 #include "dnd.h"
42 #include "bind.h"
43 #include "options.h"
45 #include "display.h" /* XXX */
46 #include "toolbar.h" /* XXX */
47 #include "filer.h" /* XXX */
48 #include "menu.h" /* XXX */
50 #define MIN_ITEM_WIDTH 64
52 #if 0
53 /* The handler of the signal handler for scroll events.
54 * This is used to cancel spring loading when autoscrolling is used.
56 static gulong scrolled_signal = -1;
57 static GtkObject *scrolled_adj = NULL; /* The object watched */
58 #endif
60 /* Item we are about to display a tooltip for */
61 static DirItem *tip_item = NULL;
63 static gpointer parent_class = NULL;
65 struct _ViewCollectionClass {
66 GtkViewportClass parent;
69 struct _ViewCollection {
70 GtkViewport viewport;
72 Collection *collection;
73 FilerWindow *filer_window; /* Used for styles, etc */
75 int cursor_base; /* Cursor when minibuffer opened */
78 typedef struct _Template Template;
80 struct _Template {
81 GdkRectangle icon;
82 GdkRectangle leafname;
83 GdkRectangle details;
86 /* GC for drawing colour filenames */
87 static GdkGC *type_gc = NULL;
89 /* Static prototypes */
90 static void view_collection_finialize(GObject *object);
91 static void view_collection_class_init(gpointer gclass, gpointer data);
92 static void view_collection_init(GTypeInstance *object, gpointer gclass);
94 static void draw_item(GtkWidget *widget,
95 CollectionItem *item,
96 GdkRectangle *area,
97 gpointer user_data);
98 static void fill_template(GdkRectangle *area, CollectionItem *item,
99 ViewCollection *view_collection, Template *template);
100 static void huge_template(GdkRectangle *area, CollectionItem *colitem,
101 ViewCollection *view_collection, Template *template);
102 static void large_template(GdkRectangle *area, CollectionItem *colitem,
103 ViewCollection *view_collection, Template *template);
104 static void small_template(GdkRectangle *area, CollectionItem *colitem,
105 ViewCollection *view_collection, Template *template);
106 static void huge_full_template(GdkRectangle *area, CollectionItem *colitem,
107 ViewCollection *view_collection, Template *template);
108 static void large_full_template(GdkRectangle *area, CollectionItem *colitem,
109 ViewCollection *view_collection, Template *template);
110 static void small_full_template(GdkRectangle *area, CollectionItem *colitem,
111 ViewCollection *view_collection, Template *template);
112 static gboolean test_point(Collection *collection,
113 int point_x, int point_y,
114 CollectionItem *item,
115 int width, int height,
116 gpointer user_data);
117 static void draw_string(GtkWidget *widget,
118 PangoLayout *layout,
119 GdkRectangle *area, /* Area available on screen */
120 int width, /* Width of the full string */
121 GtkStateType selection_state,
122 gboolean selected,
123 gboolean box);
124 static void draw_small_icon(GtkWidget *widget,
125 GdkRectangle *area,
126 DirItem *item,
127 MaskedPixmap *image,
128 gboolean selected);
129 static void draw_huge_icon(GtkWidget *widget,
130 GdkRectangle *area,
131 DirItem *item,
132 MaskedPixmap *image,
133 gboolean selected);
134 static void view_collection_iface_init(gpointer giface, gpointer iface_data);
135 static gboolean name_is_truncated(ViewCollection *view_collection, int i);
136 static gint coll_motion_notify(GtkWidget *widget,
137 GdkEventMotion *event,
138 ViewCollection *view_collection);
139 static gint coll_button_release(GtkWidget *widget,
140 GdkEventButton *event,
141 ViewCollection *view_collection);
142 static gint coll_button_press(GtkWidget *widget,
143 GdkEventButton *event,
144 ViewCollection *view_collection);
145 static void size_allocate(GtkWidget *w, GtkAllocation *a, gpointer data);
146 static void create_uri_list(ViewCollection *view_collection, GString *string);
147 static void perform_action(ViewCollection *view_collection,
148 GdkEventButton *event);
149 static void style_set(Collection *collection,
150 GtkStyle *style,
151 ViewCollection *view_collection);
152 static void display_free_colitem(Collection *collection,
153 CollectionItem *colitem);
154 static void lost_selection(Collection *collection,
155 guint time,
156 gpointer user_data);
157 static void selection_changed(Collection *collection,
158 gint time,
159 gpointer user_data);
160 static void calc_size(FilerWindow *filer_window, CollectionItem *colitem,
161 int *width, int *height);
162 static gboolean drag_motion(GtkWidget *widget,
163 GdkDragContext *context,
164 gint x,
165 gint y,
166 guint time,
167 ViewCollection *view_collection);
168 static void drag_leave(GtkWidget *widget,
169 GdkDragContext *context,
170 guint32 time,
171 ViewCollection *view_collection);
172 static void drag_end(GtkWidget *widget,
173 GdkDragContext *context,
174 ViewCollection *view_collection);
175 static void make_iter(ViewCollection *view_collection, ViewIter *iter,
176 IterFlags flags);
177 static void make_item_iter(ViewCollection *vc, ViewIter *iter, int i);
179 static void view_collection_sort(ViewIface *view);
180 static void view_collection_style_changed(ViewIface *view, int flags);
181 static gboolean view_collection_autoselect(ViewIface *view, const gchar *leaf);
182 static void view_collection_add_items(ViewIface *view, GPtrArray *items);
183 static void view_collection_update_items(ViewIface *view, GPtrArray *items);
184 static void view_collection_delete_if(ViewIface *view,
185 gboolean (*test)(gpointer item, gpointer data),
186 gpointer data);
187 static void view_collection_clear(ViewIface *view);
188 static void view_collection_select_all(ViewIface *view);
189 static void view_collection_clear_selection(ViewIface *view);
190 static int view_collection_count_items(ViewIface *view);
191 static int view_collection_count_selected(ViewIface *view);
192 static void view_collection_show_cursor(ViewIface *view);
193 static void view_collection_get_iter(ViewIface *view,
194 ViewIter *iter, IterFlags flags);
195 static void view_collection_cursor_to_iter(ViewIface *view, ViewIter *iter);
196 static void view_collection_set_selected(ViewIface *view,
197 ViewIter *iter,
198 gboolean selected);
199 static gboolean view_collection_get_selected(ViewIface *view, ViewIter *iter);
200 static void view_collection_select_only(ViewIface *view, ViewIter *iter);
201 static void view_collection_set_frozen(ViewIface *view, gboolean frozen);
202 static void view_collection_wink_item(ViewIface *view, ViewIter *iter);
203 static void view_collection_autosize(ViewIface *view);
204 static gboolean view_collection_cursor_visible(ViewIface *view);
205 static void view_collection_set_base(ViewIface *view, ViewIter *iter);
207 static DirItem *iter_next(ViewIter *iter);
208 static DirItem *iter_prev(ViewIter *iter);
209 static DirItem *iter_peek(ViewIter *iter);
212 /****************************************************************
213 * EXTERNAL INTERFACE *
214 ****************************************************************/
216 GtkWidget *view_collection_new(FilerWindow *filer_window)
218 ViewCollection *view_collection;
220 view_collection = g_object_new(view_collection_get_type(), NULL);
221 view_collection->filer_window = filer_window;
223 gtk_range_set_adjustment(GTK_RANGE(filer_window->scrollbar),
224 view_collection->collection->vadj);
226 return GTK_WIDGET(view_collection);
229 GType view_collection_get_type(void)
231 static GType type = 0;
233 if (!type)
235 static const GTypeInfo info =
237 sizeof (ViewCollectionClass),
238 NULL, /* base_init */
239 NULL, /* base_finalise */
240 view_collection_class_init,
241 NULL, /* class_finalise */
242 NULL, /* class_data */
243 sizeof(ViewCollection),
244 0, /* n_preallocs */
245 view_collection_init
247 static const GInterfaceInfo iface_info =
249 view_collection_iface_init, NULL, NULL
252 type = g_type_register_static(gtk_viewport_get_type(),
253 "ViewCollection", &info, 0);
254 g_type_add_interface_static(type, VIEW_TYPE_IFACE, &iface_info);
257 return type;
260 /****************************************************************
261 * INTERNAL FUNCTIONS *
262 ****************************************************************/
264 static void view_collection_destroy(GtkObject *view_collection)
266 VIEW_COLLECTION(view_collection)->filer_window = NULL;
269 static void view_collection_finialize(GObject *object)
271 /* ViewCollection *view_collection = (ViewCollection *) object; */
273 G_OBJECT_CLASS(parent_class)->finalize(object);
276 static void view_collection_class_init(gpointer gclass, gpointer data)
278 GObjectClass *object = (GObjectClass *) gclass;
280 parent_class = g_type_class_peek_parent(gclass);
282 object->finalize = view_collection_finialize;
283 GTK_OBJECT_CLASS(object)->destroy = view_collection_destroy;
286 static void view_collection_init(GTypeInstance *object, gpointer gclass)
288 ViewCollection *view_collection = (ViewCollection *) object;
289 GtkViewport *viewport = (GtkViewport *) object;
290 GtkWidget *collection;
291 GtkAdjustment *adj;
293 collection = collection_new();
294 view_collection->collection = COLLECTION(collection);
296 adj = view_collection->collection->vadj;
297 gtk_viewport_set_vadjustment(viewport, adj);
298 gtk_viewport_set_hadjustment(viewport, NULL); /* Or Gtk will crash */
299 gtk_viewport_set_shadow_type(viewport, GTK_SHADOW_NONE);
300 gtk_container_add(GTK_CONTAINER(object), collection);
301 gtk_widget_show(collection);
302 gtk_widget_set_size_request(GTK_WIDGET(view_collection), 4, 4);
304 gtk_container_set_resize_mode(GTK_CONTAINER(viewport),
305 GTK_RESIZE_IMMEDIATE);
307 view_collection->collection->free_item = display_free_colitem;
308 view_collection->collection->draw_item = draw_item;
309 view_collection->collection->test_point = test_point;
310 view_collection->collection->cb_user_data = view_collection;
312 g_signal_connect(collection, "style_set",
313 G_CALLBACK(style_set),
314 view_collection);
316 g_signal_connect(collection, "lose_selection",
317 G_CALLBACK(lost_selection), view_collection);
318 g_signal_connect(collection, "selection_changed",
319 G_CALLBACK(selection_changed), view_collection);
321 g_signal_connect(collection, "button-release-event",
322 G_CALLBACK(coll_button_release), view_collection);
323 g_signal_connect(collection, "button-press-event",
324 G_CALLBACK(coll_button_press), view_collection);
325 g_signal_connect(collection, "motion-notify-event",
326 G_CALLBACK(coll_motion_notify), view_collection);
327 g_signal_connect(viewport, "size-allocate",
328 G_CALLBACK(size_allocate), view_collection);
330 gtk_widget_set_events(collection,
331 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
332 GDK_BUTTON3_MOTION_MASK | GDK_POINTER_MOTION_MASK |
333 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
335 /* Drag and drop events */
336 g_signal_connect(collection, "drag_data_get",
337 GTK_SIGNAL_FUNC(drag_data_get), NULL);
339 make_drop_target(collection, 0);
341 g_signal_connect(collection, "drag_motion",
342 G_CALLBACK(drag_motion), view_collection);
343 g_signal_connect(collection, "drag_leave",
344 G_CALLBACK(drag_leave), view_collection);
345 g_signal_connect(collection, "drag_end",
346 G_CALLBACK(drag_end), view_collection);
350 static void draw_item(GtkWidget *widget,
351 CollectionItem *colitem,
352 GdkRectangle *area,
353 gpointer user_data)
355 DirItem *item = (DirItem *) colitem->data;
356 gboolean selected = colitem->selected;
357 Template template;
358 ViewData *view = (ViewData *) colitem->view_data;
359 ViewCollection *view_collection = (ViewCollection *) user_data;
360 FilerWindow *filer_window = view_collection->filer_window;
362 g_return_if_fail(view != NULL);
364 fill_template(area, colitem, view_collection, &template);
366 /* Set up GC for coloured file types */
367 if (!type_gc)
368 type_gc = gdk_gc_new(widget->window);
370 gdk_gc_set_foreground(type_gc, type_get_colour(item,
371 &widget->style->fg[GTK_STATE_NORMAL]));
373 if (template.icon.width <= SMALL_WIDTH &&
374 template.icon.height <= SMALL_HEIGHT)
376 draw_small_icon(widget, &template.icon,
377 item, view->image, selected);
379 else if (template.icon.width <= ICON_WIDTH &&
380 template.icon.height <= ICON_HEIGHT)
382 draw_large_icon(widget, &template.icon,
383 item, view->image, selected);
385 else
387 draw_huge_icon(widget, &template.icon,
388 item, view->image, selected);
391 draw_string(widget, view->layout,
392 &template.leafname,
393 view->name_width,
394 filer_window->selection_state,
395 selected, TRUE);
396 if (view->details)
397 draw_string(widget, view->details,
398 &template.details,
399 template.details.width,
400 filer_window->selection_state,
401 selected, TRUE);
404 /* A template contains the locations of the three rectangles (for the icon,
405 * name and extra details).
406 * Fill in the empty 'template' with the rectanges for this item.
408 static void fill_template(GdkRectangle *area, CollectionItem *colitem,
409 ViewCollection *view_collection, Template *template)
411 DisplayStyle style = view_collection->filer_window->display_style;
412 ViewData *view = (ViewData *) colitem->view_data;
414 if (view->details)
416 template->details.width = view->details_width;
417 template->details.height = view->details_height;
419 if (style == SMALL_ICONS)
420 small_full_template(area, colitem,
421 view_collection, template);
422 else if (style == LARGE_ICONS)
423 large_full_template(area, colitem,
424 view_collection, template);
425 else
426 huge_full_template(area, colitem,
427 view_collection, template);
429 else
431 if (style == HUGE_ICONS)
432 huge_template(area, colitem,
433 view_collection, template);
434 else if (style == LARGE_ICONS)
435 large_template(area, colitem,
436 view_collection, template);
437 else
438 small_template(area, colitem,
439 view_collection, template);
443 static void huge_template(GdkRectangle *area, CollectionItem *colitem,
444 ViewCollection *view_collection, Template *template)
446 int col_width = view_collection->collection->item_width;
447 int text_x, text_y;
448 ViewData *view = (ViewData *) colitem->view_data;
449 MaskedPixmap *image = view->image;
451 if (image)
453 if (!image->huge_pixbuf)
454 pixmap_make_huge(image);
455 template->icon.width = image->huge_width;
456 template->icon.height = image->huge_height;
458 else
460 template->icon.width = HUGE_WIDTH * 3 / 2;
461 template->icon.height = HUGE_HEIGHT;
464 template->leafname.width = view->name_width;
465 template->leafname.height = view->name_height;
467 text_x = area->x + ((col_width - template->leafname.width) >> 1);
468 text_y = area->y + area->height - template->leafname.height;
470 template->leafname.x = text_x;
471 template->leafname.y = text_y;
473 template->icon.x = area->x + ((col_width - template->icon.width) >> 1);
474 template->icon.y = template->leafname.y - template->icon.height - 2;
477 static void large_template(GdkRectangle *area, CollectionItem *colitem,
478 ViewCollection *view_collection, Template *template)
480 int col_width = view_collection->collection->item_width;
481 int iwidth, iheight;
482 int image_x;
483 int image_y;
484 ViewData *view = (ViewData *) colitem->view_data;
485 MaskedPixmap *image = view->image;
487 int text_x, text_y;
489 if (image)
491 iwidth = MIN(image->width, ICON_WIDTH);
492 iheight = MIN(image->height + 6, ICON_HEIGHT);
494 else
496 iwidth = ICON_WIDTH;
497 iheight = ICON_HEIGHT;
499 image_x = area->x + ((col_width - iwidth) >> 1);
501 template->leafname.width = view->name_width;
502 template->leafname.height = view->name_height;
504 text_x = area->x + ((col_width - template->leafname.width) >> 1);
505 text_y = area->y + ICON_HEIGHT + 2;
507 template->leafname.x = text_x;
508 template->leafname.y = text_y;
510 image_y = text_y - iheight;
511 image_y = MAX(area->y, image_y);
513 template->icon.x = image_x;
514 template->icon.y = image_y;
515 template->icon.width = iwidth;
516 template->icon.height = MIN(ICON_HEIGHT, iheight);
519 static void small_template(GdkRectangle *area, CollectionItem *colitem,
520 ViewCollection *view_collection, Template *template)
522 int text_x = area->x + SMALL_WIDTH + 4;
523 int low_text_y;
524 int max_text_width = area->width - SMALL_WIDTH - 4;
525 ViewData *view = (ViewData *) colitem->view_data;
527 low_text_y = area->y + area->height / 2 - view->name_height / 2;
529 template->leafname.x = text_x;
530 template->leafname.y = low_text_y;
531 template->leafname.width = MIN(max_text_width, view->name_width);
532 template->leafname.height = view->name_height;
534 template->icon.x = area->x;
535 template->icon.y = area->y + 1;
536 template->icon.width = SMALL_WIDTH;
537 template->icon.height = SMALL_HEIGHT;
540 static void huge_full_template(GdkRectangle *area, CollectionItem *colitem,
541 ViewCollection *view_collection, Template *template)
543 int max_text_width = area->width - HUGE_WIDTH - 4;
544 ViewData *view = (ViewData *) colitem->view_data;
545 MaskedPixmap *image = view->image;
547 if (image)
549 if (!image->huge_pixbuf)
550 pixmap_make_huge(image);
551 template->icon.width = image->huge_width;
552 template->icon.height = image->huge_height;
554 else
556 template->icon.width = HUGE_WIDTH * 3 / 2;
557 template->icon.height = HUGE_HEIGHT;
560 template->icon.x = area->x + (HUGE_WIDTH - template->icon.width) / 2;
561 template->icon.y = area->y + (area->height - template->icon.height) / 2;
563 template->leafname.x = area->x + HUGE_WIDTH + 4;
564 template->leafname.y = area->y + area->height / 2
565 - (view->name_height + 2 + view->details_height) / 2;
566 template->leafname.width = MIN(max_text_width, view->name_width);
567 template->leafname.height = view->name_height;
569 if (!image)
570 return; /* Not scanned yet */
572 template->details.x = template->leafname.x;
573 template->details.y = template->leafname.y + view->name_height + 2;
576 static void large_full_template(GdkRectangle *area, CollectionItem *colitem,
577 ViewCollection *view_collection, Template *template)
579 int max_text_width = area->width - ICON_WIDTH - 4;
580 ViewData *view = (ViewData *) colitem->view_data;
581 MaskedPixmap *image = view->image;
583 if (image)
585 template->icon.width = image->width;
586 template->icon.height = image->height;
588 else
590 template->icon.width = ICON_WIDTH;
591 template->icon.height = ICON_HEIGHT;
594 template->icon.x = area->x + (ICON_WIDTH - template->icon.width) / 2;
595 template->icon.y = area->y + (area->height - template->icon.height) / 2;
598 template->leafname.x = area->x + ICON_WIDTH + 4;
599 template->leafname.y = area->y + area->height / 2
600 - (view->name_height + 2 + view->details_height) / 2;
601 template->leafname.width = MIN(max_text_width, view->name_width);
602 template->leafname.height = view->name_height;
604 if (!image)
605 return; /* Not scanned yet */
607 template->details.x = template->leafname.x;
608 template->details.y = template->leafname.y + view->name_height + 2;
611 static void small_full_template(GdkRectangle *area, CollectionItem *colitem,
612 ViewCollection *view_collection, Template *template)
614 int col_width = view_collection->collection->item_width;
615 ViewData *view = (ViewData *) colitem->view_data;
617 small_template(area, colitem, view_collection, template);
619 if (!view->image)
620 return; /* Not scanned yet */
622 template->details.x = area->x + col_width - template->details.width;
623 template->details.y = area->y + area->height / 2 - \
624 view->details_height / 2;
627 #define INSIDE(px, py, area) \
628 (px >= area.x && py >= area.y && \
629 px <= area.x + area.width && py <= area.y + area.height)
631 static gboolean test_point(Collection *collection,
632 int point_x, int point_y,
633 CollectionItem *colitem,
634 int width, int height,
635 gpointer user_data)
637 Template template;
638 GdkRectangle area;
639 ViewData *view = (ViewData *) colitem->view_data;
640 ViewCollection *view_collection = (ViewCollection *) user_data;
642 area.x = 0;
643 area.y = 0;
644 area.width = width;
645 area.height = height;
647 fill_template(&area, colitem, view_collection, &template);
649 return INSIDE(point_x, point_y, template.leafname) ||
650 INSIDE(point_x, point_y, template.icon) ||
651 (view->details && INSIDE(point_x, point_y, template.details));
654 /* 'box' renders a background box if the string is also selected */
655 static void draw_string(GtkWidget *widget,
656 PangoLayout *layout,
657 GdkRectangle *area, /* Area available on screen */
658 int width, /* Width of the full string */
659 GtkStateType selection_state,
660 gboolean selected,
661 gboolean box)
663 GdkGC *gc = selected
664 ? widget->style->fg_gc[selection_state]
665 : type_gc;
667 if (selected && box)
668 gtk_paint_flat_box(widget->style, widget->window,
669 selection_state, GTK_SHADOW_NONE,
670 NULL, widget, "text",
671 area->x, area->y,
672 MIN(width, area->width),
673 area->height);
675 if (width > area->width)
677 gdk_gc_set_clip_origin(gc, 0, 0);
678 gdk_gc_set_clip_rectangle(gc, area);
681 gdk_draw_layout(widget->window, gc, area->x, area->y, layout);
683 if (width > area->width)
685 static GdkGC *red_gc = NULL;
687 if (!red_gc)
689 gboolean success;
690 GdkColor red = {0, 0xffff, 0, 0};
692 red_gc = gdk_gc_new(widget->window);
693 gdk_colormap_alloc_colors(
694 gtk_widget_get_colormap(widget),
695 &red, 1, FALSE, TRUE, &success);
696 gdk_gc_set_foreground(red_gc, &red);
698 gdk_draw_rectangle(widget->window, red_gc, TRUE,
699 area->x + area->width - 1, area->y,
700 1, area->height);
701 gdk_gc_set_clip_rectangle(gc, NULL);
705 static void draw_small_icon(GtkWidget *widget,
706 GdkRectangle *area,
707 DirItem *item,
708 MaskedPixmap *image,
709 gboolean selected)
711 int width, height, image_x, image_y;
713 if (!image)
714 return;
716 if (!image->sm_pixbuf)
717 pixmap_make_small(image);
719 width = MIN(image->sm_width, SMALL_WIDTH);
720 height = MIN(image->sm_height, SMALL_HEIGHT);
721 image_x = area->x + ((area->width - width) >> 1);
722 image_y = MAX(0, SMALL_HEIGHT - image->sm_height);
724 gdk_pixbuf_render_to_drawable_alpha(
725 selected ? image->sm_pixbuf_lit : image->sm_pixbuf,
726 widget->window,
727 0, 0, /* src */
728 image_x, area->y + image_y, /* dest */
729 width, height,
730 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
731 GDK_RGB_DITHER_NORMAL, 0, 0);
733 if (item->flags & ITEM_FLAG_SYMLINK)
735 gdk_pixbuf_render_to_drawable_alpha(im_symlink->pixbuf,
736 widget->window,
737 0, 0, /* src */
738 image_x, area->y + 8, /* dest */
739 -1, -1,
740 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
741 GDK_RGB_DITHER_NORMAL, 0, 0);
743 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
745 MaskedPixmap *mp = item->flags & ITEM_FLAG_MOUNTED
746 ? im_mounted
747 : im_unmounted;
749 gdk_pixbuf_render_to_drawable_alpha(mp->pixbuf,
750 widget->window,
751 0, 0, /* src */
752 image_x + 2, area->y + 2, /* dest */
753 -1, -1,
754 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
755 GDK_RGB_DITHER_NORMAL, 0, 0);
759 /* Draw this icon (including any symlink or mount symbol) inside the
760 * given rectangle.
762 static void draw_huge_icon(GtkWidget *widget,
763 GdkRectangle *area,
764 DirItem *item,
765 MaskedPixmap *image,
766 gboolean selected)
768 int width, height;
769 int image_x;
770 int image_y;
772 if (!image)
773 return;
775 width = image->huge_width;
776 height = image->huge_height;
777 image_x = area->x + ((area->width - width) >> 1);
778 image_y = MAX(0, area->height - height - 6);
780 gdk_pixbuf_render_to_drawable_alpha(
781 selected ? image->huge_pixbuf_lit
782 : image->huge_pixbuf,
783 widget->window,
784 0, 0, /* src */
785 image_x, area->y + image_y, /* dest */
786 width, height,
787 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
788 GDK_RGB_DITHER_NORMAL, 0, 0);
790 if (item->flags & ITEM_FLAG_SYMLINK)
792 gdk_pixbuf_render_to_drawable_alpha(im_symlink->pixbuf,
793 widget->window,
794 0, 0, /* src */
795 image_x, area->y + 2, /* dest */
796 -1, -1,
797 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
798 GDK_RGB_DITHER_NORMAL, 0, 0);
800 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
802 MaskedPixmap *mp = item->flags & ITEM_FLAG_MOUNTED
803 ? im_mounted
804 : im_unmounted;
806 gdk_pixbuf_render_to_drawable_alpha(mp->pixbuf,
807 widget->window,
808 0, 0, /* src */
809 image_x, area->y + 2, /* dest */
810 -1, -1,
811 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
812 GDK_RGB_DITHER_NORMAL, 0, 0);
816 /* Create the handers for the View interface */
817 static void view_collection_iface_init(gpointer giface, gpointer iface_data)
819 ViewIfaceClass *iface = giface;
821 g_assert(G_TYPE_FROM_INTERFACE(iface) == VIEW_TYPE_IFACE);
823 /* override stuff */
824 iface->sort = view_collection_sort;
825 iface->style_changed = view_collection_style_changed;
826 iface->autoselect = view_collection_autoselect;
827 iface->add_items = view_collection_add_items;
828 iface->update_items = view_collection_update_items;
829 iface->delete_if = view_collection_delete_if;
830 iface->clear = view_collection_clear;
831 iface->select_all = view_collection_select_all;
832 iface->clear_selection = view_collection_clear_selection;
833 iface->count_items = view_collection_count_items;
834 iface->count_selected = view_collection_count_selected;
835 iface->show_cursor = view_collection_show_cursor;
836 iface->get_iter = view_collection_get_iter;
837 iface->cursor_to_iter = view_collection_cursor_to_iter;
838 iface->set_selected = view_collection_set_selected;
839 iface->get_selected = view_collection_get_selected;
840 iface->set_frozen = view_collection_set_frozen;
841 iface->select_only = view_collection_select_only;
842 iface->wink_item = view_collection_wink_item;
843 iface->autosize = view_collection_autosize;
844 iface->cursor_visible = view_collection_cursor_visible;
845 iface->set_base = view_collection_set_base;
848 /* It's time to make the tooltip appear. If we're not over the item any
849 * more, or the item doesn't need a tooltip, do nothing.
851 static gboolean tooltip_activate(ViewCollection *view_collection)
853 Collection *collection;
854 gint x, y;
855 int i;
856 GString *tip = NULL;
858 g_return_val_if_fail(tip_item != NULL, 0);
860 if (!view_collection->filer_window)
861 return FALSE; /* Window has been destroyed */
863 tooltip_show(NULL);
865 collection = view_collection->collection;
866 gdk_window_get_pointer(GTK_WIDGET(collection)->window, &x, &y, NULL);
867 i = collection_get_item(collection, x, y);
868 if (i == -1 || ((DirItem *) collection->items[i].data) != tip_item)
869 return FALSE; /* Not still under the pointer */
871 /* OK, the filer window still exists and the pointer is still
872 * over the same item. Do we need to show a tip?
875 tip = g_string_new(NULL);
877 if (name_is_truncated(view_collection, i))
879 g_string_append(tip, tip_item->leafname);
880 g_string_append_c(tip, '\n');
883 filer_add_tip_details(view_collection->filer_window, tip, tip_item);
885 if (tip->len > 1)
887 g_string_truncate(tip, tip->len - 1);
889 tooltip_show(tip->str);
892 g_string_free(tip, TRUE);
894 return FALSE;
897 static gboolean name_is_truncated(ViewCollection *view_collection, int i)
899 Template template;
900 Collection *collection = view_collection->collection;
901 FilerWindow *filer_window = view_collection->filer_window;
902 CollectionItem *colitem = &collection->items[i];
903 int col = i % collection->columns;
904 int row = i / collection->columns;
905 GdkRectangle area;
906 ViewData *view = (ViewData *) colitem->view_data;
908 /* TODO: What if the window is narrower than 1 column? */
909 if (filer_window->display_style == LARGE_ICONS ||
910 filer_window->display_style == HUGE_ICONS)
911 return FALSE; /* These wrap rather than truncate */
913 area.x = col * collection->item_width;
914 area.y = row * collection->item_height;
915 area.height = collection->item_height;
917 if (col == collection->columns - 1)
918 area.width = GTK_WIDGET(collection)->allocation.width - area.x;
919 else
920 area.width = collection->item_width;
922 fill_template(&area, colitem, view_collection, &template);
924 return template.leafname.width < view->name_width;
927 static gint coll_motion_notify(GtkWidget *widget,
928 GdkEventMotion *event,
929 ViewCollection *view_collection)
931 Collection *collection = view_collection->collection;
932 FilerWindow *filer_window = view_collection->filer_window;
933 int i;
935 i = collection_get_item(collection, event->x, event->y);
937 if (i == -1)
939 tooltip_show(NULL);
940 tip_item = NULL;
942 else
944 DirItem *item = (DirItem *) collection->items[i].data;
946 if (item != tip_item)
948 tooltip_show(NULL);
950 tip_item = item;
951 if (item)
952 tooltip_prime((GtkFunction) tooltip_activate,
953 G_OBJECT(view_collection));
957 if (motion_state != MOTION_READY_FOR_DND)
958 return FALSE;
960 if (!dnd_motion_moved(event))
961 return FALSE;
963 i = collection_get_item(collection,
964 event->x - (event->x_root - drag_start_x),
965 event->y - (event->y_root - drag_start_y));
966 if (i == -1)
967 return FALSE;
969 collection_wink_item(collection, -1);
971 if (!collection->items[i].selected)
973 if (event->state & GDK_BUTTON1_MASK)
975 /* Select just this one */
976 filer_window->temp_item_selected = TRUE;
977 collection_clear_except(collection, i);
979 else
981 if (collection->number_selected == 0)
982 filer_window->temp_item_selected = TRUE;
983 collection_select_item(collection, i);
987 g_return_val_if_fail(collection->number_selected > 0, TRUE);
989 if (collection->number_selected == 1)
991 DirItem *item = (DirItem *) collection->items[i].data;
992 ViewData *view = (ViewData *) collection->items[i].view_data;
994 if (!item->image)
995 item = dir_update_item(filer_window->directory,
996 item->leafname);
998 if (!item)
1000 report_error(_("Item no longer exists!"));
1001 return FALSE;
1004 drag_one_item(widget, event,
1005 make_path(filer_window->sym_path, item->leafname),
1006 item, view ? view->image : NULL);
1008 else
1010 GString *uris;
1012 uris = g_string_new(NULL);
1013 create_uri_list(view_collection, uris);
1014 drag_selection(widget, event, uris->str);
1015 g_string_free(uris, TRUE);
1018 return FALSE;
1021 /* Viewport is to be resized, so calculate increments */
1022 static void size_allocate(GtkWidget *w, GtkAllocation *a, gpointer data)
1024 Collection *col = ((ViewCollection *) data)->collection;
1026 col->vadj->step_increment = col->item_height;
1027 col->vadj->page_increment = col->vadj->page_size;
1030 /* Append all the URIs in the selection to the string */
1031 static void create_uri_list(ViewCollection *view_collection, GString *string)
1033 GString *leader;
1034 ViewIter iter;
1035 DirItem *item;
1037 leader = g_string_new("file://");
1038 g_string_append(leader, our_host_name_for_dnd());
1039 g_string_append(leader, view_collection->filer_window->sym_path);
1040 if (leader->str[leader->len - 1] != '/')
1041 g_string_append_c(leader, '/');
1043 make_iter(view_collection, &iter, VIEW_ITER_SELECTED);
1044 while ((item = iter.next(&iter)))
1046 g_string_append(string, leader->str);
1047 g_string_append(string, item->leafname);
1048 g_string_append(string, "\r\n");
1051 g_string_free(leader, TRUE);
1054 static gint coll_button_release(GtkWidget *widget,
1055 GdkEventButton *event,
1056 ViewCollection *view_collection)
1058 if (dnd_motion_release(event))
1060 if (motion_buttons_pressed == 0 &&
1061 view_collection->collection->lasso_box)
1063 collection_end_lasso(view_collection->collection,
1064 event->button == 1 ? GDK_SET : GDK_INVERT);
1066 return FALSE;
1069 perform_action(view_collection, event);
1071 return FALSE;
1074 static gint coll_button_press(GtkWidget *widget,
1075 GdkEventButton *event,
1076 ViewCollection *view_collection)
1078 collection_set_cursor_item(view_collection->collection, -1);
1080 if (dnd_motion_press(widget, event))
1081 perform_action(view_collection, event);
1083 return FALSE;
1086 static void perform_action(ViewCollection *view_collection,
1087 GdkEventButton *event)
1089 Collection *collection = view_collection->collection;
1090 DirItem *dir_item;
1091 int item;
1092 gboolean press = event->type == GDK_BUTTON_PRESS;
1093 gboolean selected = FALSE;
1094 OpenFlags flags = 0;
1095 BindAction action;
1096 FilerWindow *filer_window = view_collection->filer_window;
1098 if (event->button > 3)
1099 return;
1101 item = collection_get_item(collection, event->x, event->y);
1103 if (item != -1 && event->button == 1 &&
1104 collection->items[item].selected &&
1105 filer_window->selection_state == GTK_STATE_INSENSITIVE)
1107 /* Possibly a really slow DnD operation? */
1108 filer_window->temp_item_selected = FALSE;
1110 filer_selection_changed(filer_window, event->time);
1111 return;
1114 if (filer_window->target_cb)
1116 dnd_motion_ungrab();
1117 if (item != -1 && press && event->button == 1)
1119 ViewIter iter;
1120 make_item_iter(view_collection, &iter, item);
1122 filer_window->target_cb(filer_window, &iter,
1123 filer_window->target_data);
1125 filer_target_mode(filer_window, NULL, NULL, NULL);
1127 return;
1130 action = bind_lookup_bev(
1131 item == -1 ? BIND_DIRECTORY : BIND_DIRECTORY_ICON,
1132 event);
1134 if (item != -1)
1136 dir_item = (DirItem *) collection->items[item].data;
1137 selected = collection->items[item].selected;
1139 else
1140 dir_item = NULL;
1142 switch (action)
1144 case ACT_CLEAR_SELECTION:
1145 collection_clear_selection(collection);
1146 break;
1147 case ACT_TOGGLE_SELECTED:
1148 collection_toggle_item(collection, item);
1149 break;
1150 case ACT_SELECT_EXCL:
1151 collection_clear_except(collection, item);
1152 break;
1153 case ACT_EDIT_ITEM:
1154 flags |= OPEN_SHIFT;
1155 /* (no break) */
1156 case ACT_OPEN_ITEM:
1158 ViewIter iter;
1160 make_item_iter(view_collection, &iter, item);
1162 if (event->button != 1 || event->state & GDK_MOD1_MASK)
1163 flags |= OPEN_CLOSE_WINDOW;
1164 else
1165 flags |= OPEN_SAME_WINDOW;
1166 if (o_new_button_1.int_value)
1167 flags ^= OPEN_SAME_WINDOW;
1168 if (event->type == GDK_2BUTTON_PRESS)
1169 collection_unselect_item(collection, item);
1170 dnd_motion_ungrab();
1172 filer_openitem(filer_window, &iter, flags);
1173 break;
1175 case ACT_POPUP_MENU:
1177 ViewIter iter;
1179 dnd_motion_ungrab();
1180 tooltip_show(NULL);
1182 make_item_iter(view_collection, &iter, item);
1183 show_filer_menu(filer_window,
1184 (GdkEvent *) event, &iter);
1185 break;
1187 case ACT_PRIME_AND_SELECT:
1188 if (!selected)
1189 collection_clear_except(collection, item);
1190 dnd_motion_start(MOTION_READY_FOR_DND);
1191 break;
1192 case ACT_PRIME_AND_TOGGLE:
1193 collection_toggle_item(collection, item);
1194 dnd_motion_start(MOTION_READY_FOR_DND);
1195 break;
1196 case ACT_PRIME_FOR_DND:
1197 dnd_motion_start(MOTION_READY_FOR_DND);
1198 break;
1199 case ACT_IGNORE:
1200 if (press && event->button < 4)
1202 if (item)
1203 collection_wink_item(collection, item);
1204 dnd_motion_start(MOTION_NONE);
1206 break;
1207 case ACT_LASSO_CLEAR:
1208 collection_clear_selection(collection);
1209 /* (no break) */
1210 case ACT_LASSO_MODIFY:
1211 collection_lasso_box(collection, event->x, event->y);
1212 break;
1213 case ACT_RESIZE:
1214 filer_window_autosize(filer_window);
1215 break;
1216 default:
1217 g_warning("Unsupported action : %d\n", action);
1218 break;
1222 /* Nothing is selected anymore - give up primary */
1223 static void lost_selection(Collection *collection,
1224 guint time,
1225 gpointer user_data)
1227 ViewCollection *view_collection = VIEW_COLLECTION(user_data);
1229 filer_lost_selection(view_collection->filer_window, time);
1232 static void selection_changed(Collection *collection,
1233 gint time,
1234 gpointer user_data)
1236 ViewCollection *view_collection = VIEW_COLLECTION(user_data);
1238 filer_selection_changed(view_collection->filer_window, time);
1241 static void display_free_colitem(Collection *collection,
1242 CollectionItem *colitem)
1244 ViewData *view = (ViewData *) colitem->view_data;
1246 if (!view)
1247 return;
1249 if (view->layout)
1251 g_object_unref(G_OBJECT(view->layout));
1252 view->layout = NULL;
1254 if (view->details)
1255 g_object_unref(G_OBJECT(view->details));
1257 if (view->image)
1258 g_object_unref(view->image);
1260 g_free(view);
1263 static void add_item(ViewCollection *view_collection, DirItem *item)
1265 Collection *collection = view_collection->collection;
1266 FilerWindow *filer_window = view_collection->filer_window;
1267 int old_w = collection->item_width;
1268 int old_h = collection->item_height;
1269 int w, h, i;
1271 i = collection_insert(collection, item,
1272 display_create_viewdata(filer_window, item));
1274 calc_size(filer_window, &collection->items[i], &w, &h);
1276 if (w > old_w || h > old_h)
1277 collection_set_item_size(collection,
1278 MAX(old_w, w),
1279 MAX(old_h, h));
1282 static void style_set(Collection *collection,
1283 GtkStyle *style,
1284 ViewCollection *view_collection)
1286 view_collection_style_changed(VIEW(view_collection),
1287 VIEW_UPDATE_VIEWDATA | VIEW_UPDATE_NAME);
1290 /* Return the size needed for this item */
1291 static void calc_size(FilerWindow *filer_window, CollectionItem *colitem,
1292 int *width, int *height)
1294 int pix_width, pix_height;
1295 int w;
1296 DisplayStyle style = filer_window->display_style;
1297 ViewData *view = (ViewData *) colitem->view_data;
1299 if (filer_window->details_type == DETAILS_NONE)
1301 if (style == HUGE_ICONS)
1303 if (view->image)
1305 if (!view->image->huge_pixbuf)
1306 pixmap_make_huge(view->image);
1307 pix_width = view->image->huge_width;
1308 pix_height = view->image->huge_height;
1310 else
1312 pix_width = HUGE_WIDTH * 3 / 2;
1313 pix_height = HUGE_HEIGHT * 3 / 2;
1315 *width = MAX(pix_width, view->name_width) + 4;
1316 *height = view->name_height + pix_height + 4;
1318 else if (style == SMALL_ICONS)
1320 w = MIN(view->name_width, o_small_width.int_value);
1321 *width = SMALL_WIDTH + 12 + w;
1322 *height = MAX(view->name_height, SMALL_HEIGHT) + 4;
1324 else
1326 if (view->image)
1327 pix_width = view->image->width;
1328 else
1329 pix_width = ICON_WIDTH;
1330 *width = MAX(pix_width, view->name_width) + 4;
1331 *height = view->name_height + ICON_HEIGHT + 2;
1334 else
1336 w = view->details_width;
1337 if (style == HUGE_ICONS)
1339 *width = HUGE_WIDTH + 12 + MAX(w, view->name_width);
1340 *height = HUGE_HEIGHT - 4;
1342 else if (style == SMALL_ICONS)
1344 int text_height;
1346 *width = SMALL_WIDTH + view->name_width + 12 + w;
1347 text_height = MAX(view->name_height,
1348 view->details_height);
1349 *height = MAX(text_height, SMALL_HEIGHT) + 4;
1351 else
1353 *width = ICON_WIDTH + 12 + MAX(w, view->name_width);
1354 *height = ICON_HEIGHT;
1359 static void update_item(ViewCollection *view_collection, int i)
1361 Collection *collection = view_collection->collection;
1362 int old_w = collection->item_width;
1363 int old_h = collection->item_height;
1364 int w, h;
1365 CollectionItem *colitem;
1366 FilerWindow *filer_window = view_collection->filer_window;
1368 g_return_if_fail(i >= 0 && i < collection->number_of_items);
1370 colitem = &collection->items[i];
1372 display_update_view(filer_window,
1373 (DirItem *) colitem->data,
1374 (ViewData *) colitem->view_data,
1375 FALSE);
1377 calc_size(filer_window, colitem, &w, &h);
1378 if (w > old_w || h > old_h)
1379 collection_set_item_size(collection,
1380 MAX(old_w, w),
1381 MAX(old_h, h));
1383 collection_draw_item(collection, i, TRUE);
1386 /* Implementations of the View interface. See view_iface.c for comments. */
1388 static void view_collection_style_changed(ViewIface *view, int flags)
1390 ViewCollection *view_collection = VIEW_COLLECTION(view);
1391 FilerWindow *filer_window = view_collection->filer_window;
1392 int i;
1393 Collection *col = view_collection->collection;
1394 int width = MIN_ITEM_WIDTH;
1395 int height = SMALL_HEIGHT;
1396 int n = col->number_of_items;
1398 if (n == 0 && filer_window->display_style != SMALL_ICONS)
1399 height = ICON_HEIGHT;
1401 /* Recalculate all the ViewData structs for this window
1402 * (needed if the text or image has changed in any way) and
1403 * get the size of each item.
1405 for (i = 0; i < n; i++)
1407 CollectionItem *ci = &col->items[i];
1408 int w, h;
1410 if (flags & (VIEW_UPDATE_VIEWDATA | VIEW_UPDATE_NAME))
1411 display_update_view(filer_window,
1412 (DirItem *) ci->data,
1413 (ViewData *) ci->view_data,
1414 (flags & VIEW_UPDATE_NAME) != 0);
1416 calc_size(filer_window, ci, &w, &h);
1417 if (w > width)
1418 width = w;
1419 if (h > height)
1420 height = h;
1423 collection_set_item_size(col, width, height);
1425 gtk_widget_queue_draw(GTK_WIDGET(view_collection));
1428 static void view_collection_sort(ViewIface *view)
1430 ViewCollection *view_collection = VIEW_COLLECTION(view);
1432 collection_qsort(view_collection->collection,
1433 view_collection->filer_window->sort_fn);
1436 static gboolean view_collection_autoselect(ViewIface *view, const gchar *leaf)
1438 ViewCollection *view_collection = VIEW_COLLECTION(view);
1439 Collection *col = view_collection->collection;
1440 int i;
1442 for (i = 0; i < col->number_of_items; i++)
1444 DirItem *item = (DirItem *) col->items[i].data;
1446 if (strcmp(item->leafname, leaf) == 0)
1448 if (col->cursor_item != -1)
1449 collection_set_cursor_item(col, i);
1450 else
1451 collection_wink_item(col, i);
1452 return TRUE;
1456 return FALSE;
1459 static void view_collection_add_items(ViewIface *view, GPtrArray *items)
1461 ViewCollection *view_collection = VIEW_COLLECTION(view);
1462 Collection *collection = view_collection->collection;
1463 FilerWindow *filer_window = view_collection->filer_window;
1464 int old_num, i;
1466 old_num = collection->number_of_items;
1467 for (i = 0; i < items->len; i++)
1469 DirItem *item = (DirItem *) items->pdata[i];
1470 char *leafname = item->leafname;
1472 if (leafname[0] == '.' && !filer_window->show_hidden)
1473 continue;
1475 add_item(view_collection, item);
1478 if (old_num != collection->number_of_items)
1479 view_collection_sort(view);
1482 static void view_collection_update_items(ViewIface *view, GPtrArray *items)
1484 ViewCollection *view_collection = VIEW_COLLECTION(view);
1485 Collection *collection = view_collection->collection;
1486 FilerWindow *filer_window = view_collection->filer_window;
1487 int i;
1489 g_return_if_fail(items->len > 0);
1491 /* The item data has already been modified, so this gives the
1492 * final sort order...
1494 collection_qsort(collection, filer_window->sort_fn);
1496 for (i = 0; i < items->len; i++)
1498 DirItem *item = (DirItem *) items->pdata[i];
1499 const gchar *leafname = item->leafname;
1500 int j;
1502 if (leafname[0] == '.' && filer_window->show_hidden == FALSE)
1503 continue;
1505 j = collection_find_item(collection, item,
1506 filer_window->sort_fn);
1508 if (j < 0)
1509 g_warning("Failed to find '%s'\n", leafname);
1510 else
1511 update_item(view_collection, j);
1515 static void view_collection_delete_if(ViewIface *view,
1516 gboolean (*test)(gpointer item, gpointer data),
1517 gpointer data)
1519 ViewCollection *view_collection = VIEW_COLLECTION(view);
1520 Collection *collection = view_collection->collection;
1522 collection_delete_if(collection, test, data);
1525 static void view_collection_clear(ViewIface *view)
1527 ViewCollection *view_collection = VIEW_COLLECTION(view);
1528 Collection *collection = view_collection->collection;
1530 collection_clear(collection);
1533 static void view_collection_select_all(ViewIface *view)
1535 ViewCollection *view_collection = VIEW_COLLECTION(view);
1536 Collection *collection = view_collection->collection;
1538 collection_select_all(collection);
1541 static void view_collection_clear_selection(ViewIface *view)
1543 ViewCollection *view_collection = VIEW_COLLECTION(view);
1544 Collection *collection = view_collection->collection;
1546 collection_clear_selection(collection);
1549 static int view_collection_count_items(ViewIface *view)
1551 ViewCollection *view_collection = VIEW_COLLECTION(view);
1552 Collection *collection = view_collection->collection;
1554 return collection->number_of_items;
1557 static int view_collection_count_selected(ViewIface *view)
1559 ViewCollection *view_collection = VIEW_COLLECTION(view);
1560 Collection *collection = view_collection->collection;
1562 return collection->number_selected;
1565 static void view_collection_show_cursor(ViewIface *view)
1567 ViewCollection *view_collection = VIEW_COLLECTION(view);
1568 Collection *collection = view_collection->collection;
1570 collection_move_cursor(collection, 0, 0);
1573 /* The first time the next() method is used, this is called */
1574 static DirItem *iter_init(ViewIter *iter)
1576 ViewCollection *view_collection = iter->view_collection;
1577 Collection *collection = view_collection->collection;
1578 int i = -1;
1579 int n = collection->number_of_items;
1580 int flags = iter->flags;
1582 iter->peek = iter_peek;
1584 if (iter->n_remaining == 0)
1585 return NULL;
1587 if (flags & VIEW_ITER_FROM_CURSOR)
1589 i = collection->cursor_item;
1590 if (i == -1)
1591 return NULL; /* No cursor */
1593 else if (flags & VIEW_ITER_FROM_BASE)
1594 i = view_collection->cursor_base;
1596 if (i < 0 || i >= n)
1598 /* Either a normal iteration, or an iteration from an
1599 * invalid starting point.
1601 if (flags & VIEW_ITER_BACKWARDS)
1602 i = n - 1;
1603 else
1604 i = 0;
1607 if (i < 0 || i >= n)
1608 return NULL; /* No items at all! */
1610 iter->next = flags & VIEW_ITER_BACKWARDS ? iter_prev : iter_next;
1611 iter->n_remaining--;
1612 iter->i = i;
1614 if (flags & VIEW_ITER_SELECTED && !collection->items[i].selected)
1615 return iter->next(iter);
1616 return iter->peek(iter);
1618 /* Advance iter to point to the next item and return the new item
1619 * (this saves you calling peek after next each time).
1621 static DirItem *iter_next(ViewIter *iter)
1623 Collection *collection = iter->view_collection->collection;
1624 int n = collection->number_of_items;
1625 int i = iter->i;
1627 g_return_val_if_fail(iter->n_remaining >= 0, NULL);
1629 /* i is the last item returned (always valid) */
1631 g_return_val_if_fail(i >= 0 && i < n, NULL);
1633 while (iter->n_remaining)
1635 i++;
1636 iter->n_remaining--;
1638 if (i == n)
1639 i = 0;
1641 g_return_val_if_fail(i >= 0 && i < n, NULL);
1643 if (iter->flags & VIEW_ITER_SELECTED &&
1644 !collection->items[i].selected)
1645 continue;
1647 iter->i = i;
1648 return collection->items[i].data;
1651 iter->i = -1;
1652 return NULL;
1655 /* Like iter_next, but in the other direction */
1656 static DirItem *iter_prev(ViewIter *iter)
1658 Collection *collection = iter->view_collection->collection;
1659 int n = collection->number_of_items;
1660 int i = iter->i;
1662 g_return_val_if_fail(iter->n_remaining >= 0, NULL);
1664 /* i is the last item returned (always valid) */
1666 g_return_val_if_fail(i >= 0 && i < n, NULL);
1668 while (iter->n_remaining)
1670 i--;
1671 iter->n_remaining--;
1673 if (i == -1)
1674 i = collection->number_of_items - 1;
1676 g_return_val_if_fail(i >= 0 && i < n, NULL);
1678 if (iter->flags & VIEW_ITER_SELECTED &&
1679 !collection->items[i].selected)
1680 continue;
1682 iter->i = i;
1683 return collection->items[i].data;
1686 iter->i = -1;
1687 return NULL;
1690 static DirItem *iter_peek(ViewIter *iter)
1692 Collection *collection = iter->view_collection->collection;
1693 int i = iter->i;
1695 if (i == -1)
1696 return NULL;
1698 g_return_val_if_fail(i >= 0 && i < collection->number_of_items, NULL);
1700 return collection->items[i].data;
1703 static void make_iter(ViewCollection *view_collection, ViewIter *iter,
1704 IterFlags flags)
1706 Collection *collection = view_collection->collection;
1708 iter->view_collection = view_collection;
1709 iter->next = iter_init;
1710 iter->peek = NULL;
1711 iter->i = -1;
1713 iter->flags = flags;
1715 if (flags & VIEW_ITER_ONE_ONLY)
1717 iter->n_remaining = 1;
1718 iter->next(iter);
1720 else
1721 iter->n_remaining = collection->number_of_items;
1724 /* Set the iterator to return 'i' on the next peek() */
1725 static void make_item_iter(ViewCollection *view_collection,
1726 ViewIter *iter, int i)
1728 Collection *collection = view_collection->collection;
1730 g_return_if_fail(i >= -1 && i < collection->number_of_items);
1732 make_iter(view_collection, iter, 0);
1734 iter->i = i;
1735 iter->next = iter_next;
1736 iter->peek = iter_peek;
1737 iter->n_remaining = 0;
1740 static void view_collection_get_iter(ViewIface *view,
1741 ViewIter *iter, IterFlags flags)
1743 ViewCollection *view_collection = VIEW_COLLECTION(view);
1745 make_iter(view_collection, iter, flags);
1748 static void view_collection_cursor_to_iter(ViewIface *view, ViewIter *iter)
1750 ViewCollection *view_collection = VIEW_COLLECTION(view);
1751 Collection *collection = view_collection->collection;
1753 g_return_if_fail(iter == NULL ||
1754 iter->view_collection == view_collection);
1756 collection_set_cursor_item(collection, iter ? iter->i : -1);
1759 static void view_collection_set_selected(ViewIface *view,
1760 ViewIter *iter,
1761 gboolean selected)
1763 ViewCollection *view_collection = VIEW_COLLECTION(view);
1764 Collection *collection = view_collection->collection;
1766 g_return_if_fail(iter != NULL &&
1767 iter->view_collection == view_collection);
1768 g_return_if_fail(iter->i >= 0 && iter->i < collection->number_of_items);
1770 if (selected)
1771 collection_select_item(collection, iter->i);
1772 else
1773 collection_unselect_item(collection, iter->i);
1776 static gboolean view_collection_get_selected(ViewIface *view, ViewIter *iter)
1778 ViewCollection *view_collection = VIEW_COLLECTION(view);
1779 Collection *collection = view_collection->collection;
1781 g_return_val_if_fail(iter != NULL &&
1782 iter->view_collection == view_collection, FALSE);
1783 g_return_val_if_fail(iter->i >= 0 &&
1784 iter->i < collection->number_of_items, FALSE);
1786 return collection->items[iter->i].selected;
1789 static void view_collection_select_only(ViewIface *view, ViewIter *iter)
1791 ViewCollection *view_collection = VIEW_COLLECTION(view);
1792 Collection *collection = view_collection->collection;
1794 g_return_if_fail(iter != NULL &&
1795 iter->view_collection == view_collection);
1796 g_return_if_fail(iter->i >= 0 && iter->i < collection->number_of_items);
1798 collection_clear_except(collection, iter->i);
1801 static void view_collection_set_frozen(ViewIface *view, gboolean frozen)
1803 ViewCollection *view_collection = VIEW_COLLECTION(view);
1804 Collection *collection = view_collection->collection;
1806 if (frozen)
1807 collection->block_selection_changed++;
1808 else
1809 collection_unblock_selection_changed(collection,
1810 gtk_get_current_event_time(), TRUE);
1813 static void view_collection_wink_item(ViewIface *view, ViewIter *iter)
1815 ViewCollection *view_collection = VIEW_COLLECTION(view);
1816 Collection *collection = view_collection->collection;
1818 g_return_if_fail(iter != NULL &&
1819 iter->view_collection == view_collection);
1820 g_return_if_fail(iter->i >= 0 && iter->i < collection->number_of_items);
1822 collection_wink_item(collection, iter->i);
1825 static void view_collection_autosize(ViewIface *view)
1827 ViewCollection *view_collection = VIEW_COLLECTION(view);
1828 FilerWindow *filer_window = view_collection->filer_window;
1829 Collection *collection = view_collection->collection;
1830 int n;
1831 int w = collection->item_width;
1832 int h = collection->item_height;
1833 int x;
1834 int rows, cols;
1835 int max_x, max_rows;
1836 const float r = 2.5;
1837 int t = 0;
1838 int space = 0;
1840 /* Get the extra height required for the toolbar and minibuffer,
1841 * if visible.
1843 if (o_toolbar.int_value != TOOLBAR_NONE)
1844 t = filer_window->toolbar->allocation.height;
1845 if (filer_window->message)
1846 t += filer_window->message->allocation.height;
1847 if (GTK_WIDGET_VISIBLE(filer_window->minibuffer_area))
1849 GtkRequisition req;
1851 gtk_widget_size_request(filer_window->minibuffer_area, &req);
1852 space = req.height + 2;
1853 t += space;
1856 n = collection->number_of_items;
1857 if (n == 0)
1858 h = ICON_HEIGHT * 1.5;
1859 n = MAX(n, 2);
1861 max_x = (o_filer_size_limit.int_value * screen_width) / 100;
1862 max_rows = (o_filer_size_limit.int_value * screen_height) / (h * 100);
1864 /* Aim for a size where
1865 * x = r(y + t + h), (1)
1866 * unless that's too wide.
1868 * t = toolbar (and minibuffer) height
1869 * r = desired (width / height) ratio
1871 * Want to display all items:
1872 * (x/w)(y/h) = n
1873 * => xy = nwh
1874 * => x(x/r - t - h) = nwh (from 1)
1875 * => xx - x.rt - hr(1 - nw) = 0
1876 * => 2x = rt +/- sqrt(rt.rt + 4hr(nw - 1))
1877 * Now,
1878 * 4hr(nw - 1) > 0
1879 * so
1880 * sqrt(rt.rt + ...) > rt
1882 * So, the +/- must be +:
1884 * => x = (rt + sqrt(rt.rt + 4hr(nw - 1))) / 2
1886 * ( + w - 1 to round up)
1888 x = (r * t + sqrt(r*r*t*t + 4*h*r * (n*w - 1))) / 2 + w - 1;
1890 /* Limit x */
1891 if (x > max_x)
1892 x = max_x;
1894 cols = x / w;
1895 cols = MAX(cols, 1);
1897 /* Choose rows to display all items given our chosen x.
1898 * Don't make the window *too* big!
1900 rows = (n + cols - 1) / cols;
1901 if (rows > max_rows)
1902 rows = max_rows;
1904 /* Leave some room for extra icons, but only in Small Icons mode
1905 * otherwise it takes up too much space.
1906 * Also, don't add space if the minibuffer is open.
1908 if (space == 0)
1909 space = filer_window->display_style == SMALL_ICONS ? h : 2;
1911 filer_window_set_size(filer_window,
1912 w * MAX(cols, 1),
1913 h * MAX(rows, 1) + space);
1916 static gboolean view_collection_cursor_visible(ViewIface *view)
1918 ViewCollection *view_collection = VIEW_COLLECTION(view);
1919 Collection *collection = view_collection->collection;
1921 return collection->cursor_item != -1;
1924 static void view_collection_set_base(ViewIface *view, ViewIter *iter)
1926 ViewCollection *view_collection = VIEW_COLLECTION(view);
1928 view_collection->cursor_base = iter->i;
1931 static void drag_end(GtkWidget *widget,
1932 GdkDragContext *context,
1933 ViewCollection *view_collection)
1935 if (view_collection->filer_window->temp_item_selected)
1937 view_collection_clear_selection(VIEW(view_collection));
1938 view_collection->filer_window->temp_item_selected = FALSE;
1942 /* Remove highlights */
1943 static void drag_leave(GtkWidget *widget,
1944 GdkDragContext *context,
1945 guint32 time,
1946 ViewCollection *view_collection)
1948 dnd_spring_abort();
1949 #if 0
1950 if (scrolled_adj)
1952 g_signal_handler_disconnect(scrolled_adj, scrolled_signal);
1953 scrolled_adj = NULL;
1955 #endif
1958 /* Called during the drag when the mouse is in a widget registered
1959 * as a drop target. Returns TRUE if we can accept the drop.
1961 static gboolean drag_motion(GtkWidget *widget,
1962 GdkDragContext *context,
1963 gint x,
1964 gint y,
1965 guint time,
1966 ViewCollection *view_collection)
1968 DirItem *item;
1969 int item_number;
1970 GdkDragAction action = context->suggested_action;
1971 const guchar *new_path = NULL;
1972 const char *type = NULL;
1973 gboolean retval = FALSE;
1974 Collection *collection = view_collection->collection;
1976 if (o_dnd_drag_to_icons.int_value)
1977 item_number = collection_get_item(collection, x, y);
1978 else
1979 item_number = -1;
1981 item = item_number >= 0
1982 ? (DirItem *) collection->items[item_number].data
1983 : NULL;
1985 if (item && collection->items[item_number].selected)
1986 type = NULL;
1987 else
1988 type = dnd_motion_item(context, &item);
1990 if (!type)
1991 item = NULL;
1993 /* Don't allow drops to non-writeable directories. BUT, still
1994 * allow drops on non-writeable SUBdirectories so that we can
1995 * do the spring-open thing.
1997 if (item && type == drop_dest_dir &&
1998 !(item->flags & ITEM_FLAG_APPDIR))
2000 #if 0
2001 /* XXX: This is needed so that directories don't
2002 * spring open while we scroll. Should go in
2003 * view_collection.c, I think.
2006 /* XXX: Now it IS in view_collection, maybe we should
2007 * fix it?
2010 GtkObject *vadj = GTK_OBJECT(collection->vadj);
2012 /* Subdir: prepare for spring-open */
2013 if (scrolled_adj != vadj)
2015 if (scrolled_adj)
2016 gtk_signal_disconnect(scrolled_adj,
2017 scrolled_signal);
2018 scrolled_adj = vadj;
2019 scrolled_signal = gtk_signal_connect(
2020 scrolled_adj,
2021 "value_changed",
2022 GTK_SIGNAL_FUNC(scrolled),
2023 collection);
2025 #endif
2026 dnd_spring_load(context, view_collection->filer_window);
2028 else
2029 dnd_spring_abort();
2031 if (item)
2033 collection_set_cursor_item(collection,
2034 item_number);
2036 else
2038 collection_set_cursor_item(collection, -1);
2040 /* Disallow background drops within a single window */
2041 if (type && gtk_drag_get_source_widget(context) == widget)
2042 type = NULL;
2045 if (type)
2047 if (item)
2048 new_path = make_path(
2049 view_collection->filer_window->sym_path,
2050 item->leafname);
2051 else
2052 new_path = view_collection->filer_window->sym_path;
2055 g_dataset_set_data(context, "drop_dest_type", (gpointer) type);
2056 if (type)
2058 gdk_drag_status(context, action, time);
2059 g_dataset_set_data_full(context, "drop_dest_path",
2060 g_strdup(new_path), g_free);
2061 retval = TRUE;
2064 return retval;