r1996: Cope slightly better with invalid filenames in various places (reported by
[rox-filer.git] / ROX-Filer / src / view_collection.c
blob915b957ba52853662ec1b88ac74c19769182fb67
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_shadow_type(viewport, GTK_SHADOW_NONE);
299 gtk_container_add(GTK_CONTAINER(object), collection);
300 gtk_widget_show(collection);
301 gtk_widget_set_size_request(GTK_WIDGET(view_collection), 4, 4);
303 gtk_container_set_resize_mode(GTK_CONTAINER(viewport),
304 GTK_RESIZE_IMMEDIATE);
306 view_collection->collection->free_item = display_free_colitem;
307 view_collection->collection->draw_item = draw_item;
308 view_collection->collection->test_point = test_point;
309 view_collection->collection->cb_user_data = view_collection;
311 g_signal_connect(collection, "style_set",
312 G_CALLBACK(style_set),
313 view_collection);
315 g_signal_connect(collection, "lose_selection",
316 G_CALLBACK(lost_selection), view_collection);
317 g_signal_connect(collection, "selection_changed",
318 G_CALLBACK(selection_changed), view_collection);
320 g_signal_connect(collection, "button-release-event",
321 G_CALLBACK(coll_button_release), view_collection);
322 g_signal_connect(collection, "button-press-event",
323 G_CALLBACK(coll_button_press), view_collection);
324 g_signal_connect(collection, "motion-notify-event",
325 G_CALLBACK(coll_motion_notify), view_collection);
326 g_signal_connect(viewport, "size-allocate",
327 G_CALLBACK(size_allocate), view_collection);
329 gtk_widget_set_events(collection,
330 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
331 GDK_BUTTON3_MOTION_MASK | GDK_POINTER_MOTION_MASK |
332 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
334 /* Drag and drop events */
335 g_signal_connect(collection, "drag_data_get",
336 GTK_SIGNAL_FUNC(drag_data_get), NULL);
338 make_drop_target(collection, 0);
340 g_signal_connect(collection, "drag_motion",
341 G_CALLBACK(drag_motion), view_collection);
342 g_signal_connect(collection, "drag_leave",
343 G_CALLBACK(drag_leave), view_collection);
344 g_signal_connect(collection, "drag_end",
345 G_CALLBACK(drag_end), view_collection);
349 static void draw_item(GtkWidget *widget,
350 CollectionItem *colitem,
351 GdkRectangle *area,
352 gpointer user_data)
354 DirItem *item = (DirItem *) colitem->data;
355 gboolean selected = colitem->selected;
356 Template template;
357 ViewData *view = (ViewData *) colitem->view_data;
358 ViewCollection *view_collection = (ViewCollection *) user_data;
359 FilerWindow *filer_window = view_collection->filer_window;
361 g_return_if_fail(view != NULL);
363 fill_template(area, colitem, view_collection, &template);
365 /* Set up GC for coloured file types */
366 if (!type_gc)
367 type_gc = gdk_gc_new(widget->window);
369 gdk_gc_set_foreground(type_gc, type_get_colour(item,
370 &widget->style->fg[GTK_STATE_NORMAL]));
372 if (template.icon.width <= SMALL_WIDTH &&
373 template.icon.height <= SMALL_HEIGHT)
375 draw_small_icon(widget, &template.icon,
376 item, view->image, selected);
378 else if (template.icon.width <= ICON_WIDTH &&
379 template.icon.height <= ICON_HEIGHT)
381 draw_large_icon(widget, &template.icon,
382 item, view->image, selected);
384 else
386 draw_huge_icon(widget, &template.icon,
387 item, view->image, selected);
390 draw_string(widget, view->layout,
391 &template.leafname,
392 view->name_width,
393 filer_window->selection_state,
394 selected, TRUE);
395 if (view->details)
396 draw_string(widget, view->details,
397 &template.details,
398 template.details.width,
399 filer_window->selection_state,
400 selected, TRUE);
403 /* A template contains the locations of the three rectangles (for the icon,
404 * name and extra details).
405 * Fill in the empty 'template' with the rectanges for this item.
407 static void fill_template(GdkRectangle *area, CollectionItem *colitem,
408 ViewCollection *view_collection, Template *template)
410 DisplayStyle style = view_collection->filer_window->display_style;
411 ViewData *view = (ViewData *) colitem->view_data;
413 if (view->details)
415 template->details.width = view->details_width;
416 template->details.height = view->details_height;
418 if (style == SMALL_ICONS)
419 small_full_template(area, colitem,
420 view_collection, template);
421 else if (style == LARGE_ICONS)
422 large_full_template(area, colitem,
423 view_collection, template);
424 else
425 huge_full_template(area, colitem,
426 view_collection, template);
428 else
430 if (style == HUGE_ICONS)
431 huge_template(area, colitem,
432 view_collection, template);
433 else if (style == LARGE_ICONS)
434 large_template(area, colitem,
435 view_collection, template);
436 else
437 small_template(area, colitem,
438 view_collection, template);
442 static void huge_template(GdkRectangle *area, CollectionItem *colitem,
443 ViewCollection *view_collection, Template *template)
445 int col_width = view_collection->collection->item_width;
446 int text_x, text_y;
447 ViewData *view = (ViewData *) colitem->view_data;
448 MaskedPixmap *image = view->image;
450 if (image)
452 if (!image->huge_pixbuf)
453 pixmap_make_huge(image);
454 template->icon.width = image->huge_width;
455 template->icon.height = image->huge_height;
457 else
459 template->icon.width = HUGE_WIDTH * 3 / 2;
460 template->icon.height = HUGE_HEIGHT;
463 template->leafname.width = view->name_width;
464 template->leafname.height = view->name_height;
466 text_x = area->x + ((col_width - template->leafname.width) >> 1);
467 text_y = area->y + area->height - template->leafname.height;
469 template->leafname.x = text_x;
470 template->leafname.y = text_y;
472 template->icon.x = area->x + ((col_width - template->icon.width) >> 1);
473 template->icon.y = template->leafname.y - template->icon.height - 2;
476 static void large_template(GdkRectangle *area, CollectionItem *colitem,
477 ViewCollection *view_collection, Template *template)
479 int col_width = view_collection->collection->item_width;
480 int iwidth, iheight;
481 int image_x;
482 int image_y;
483 ViewData *view = (ViewData *) colitem->view_data;
484 MaskedPixmap *image = view->image;
486 int text_x, text_y;
488 if (image)
490 iwidth = MIN(image->width, ICON_WIDTH);
491 iheight = MIN(image->height + 6, ICON_HEIGHT);
493 else
495 iwidth = ICON_WIDTH;
496 iheight = ICON_HEIGHT;
498 image_x = area->x + ((col_width - iwidth) >> 1);
500 template->leafname.width = view->name_width;
501 template->leafname.height = view->name_height;
503 text_x = area->x + ((col_width - template->leafname.width) >> 1);
504 text_y = area->y + ICON_HEIGHT + 2;
506 template->leafname.x = text_x;
507 template->leafname.y = text_y;
509 image_y = text_y - iheight;
510 image_y = MAX(area->y, image_y);
512 template->icon.x = image_x;
513 template->icon.y = image_y;
514 template->icon.width = iwidth;
515 template->icon.height = MIN(ICON_HEIGHT, iheight);
518 static void small_template(GdkRectangle *area, CollectionItem *colitem,
519 ViewCollection *view_collection, Template *template)
521 int text_x = area->x + SMALL_WIDTH + 4;
522 int low_text_y;
523 int max_text_width = area->width - SMALL_WIDTH - 4;
524 ViewData *view = (ViewData *) colitem->view_data;
526 low_text_y = area->y + area->height / 2 - view->name_height / 2;
528 template->leafname.x = text_x;
529 template->leafname.y = low_text_y;
530 template->leafname.width = MIN(max_text_width, view->name_width);
531 template->leafname.height = view->name_height;
533 template->icon.x = area->x;
534 template->icon.y = area->y + 1;
535 template->icon.width = SMALL_WIDTH;
536 template->icon.height = SMALL_HEIGHT;
539 static void huge_full_template(GdkRectangle *area, CollectionItem *colitem,
540 ViewCollection *view_collection, Template *template)
542 int max_text_width = area->width - HUGE_WIDTH - 4;
543 ViewData *view = (ViewData *) colitem->view_data;
544 MaskedPixmap *image = view->image;
546 if (image)
548 if (!image->huge_pixbuf)
549 pixmap_make_huge(image);
550 template->icon.width = image->huge_width;
551 template->icon.height = image->huge_height;
553 else
555 template->icon.width = HUGE_WIDTH * 3 / 2;
556 template->icon.height = HUGE_HEIGHT;
559 template->icon.x = area->x + (HUGE_WIDTH - template->icon.width) / 2;
560 template->icon.y = area->y + (area->height - template->icon.height) / 2;
562 template->leafname.x = area->x + HUGE_WIDTH + 4;
563 template->leafname.y = area->y + area->height / 2
564 - (view->name_height + 2 + view->details_height) / 2;
565 template->leafname.width = MIN(max_text_width, view->name_width);
566 template->leafname.height = view->name_height;
568 if (!image)
569 return; /* Not scanned yet */
571 template->details.x = template->leafname.x;
572 template->details.y = template->leafname.y + view->name_height + 2;
575 static void large_full_template(GdkRectangle *area, CollectionItem *colitem,
576 ViewCollection *view_collection, Template *template)
578 int max_text_width = area->width - ICON_WIDTH - 4;
579 ViewData *view = (ViewData *) colitem->view_data;
580 MaskedPixmap *image = view->image;
582 if (image)
584 template->icon.width = image->width;
585 template->icon.height = image->height;
587 else
589 template->icon.width = ICON_WIDTH;
590 template->icon.height = ICON_HEIGHT;
593 template->icon.x = area->x + (ICON_WIDTH - template->icon.width) / 2;
594 template->icon.y = area->y + (area->height - template->icon.height) / 2;
597 template->leafname.x = area->x + ICON_WIDTH + 4;
598 template->leafname.y = area->y + area->height / 2
599 - (view->name_height + 2 + view->details_height) / 2;
600 template->leafname.width = MIN(max_text_width, view->name_width);
601 template->leafname.height = view->name_height;
603 if (!image)
604 return; /* Not scanned yet */
606 template->details.x = template->leafname.x;
607 template->details.y = template->leafname.y + view->name_height + 2;
610 static void small_full_template(GdkRectangle *area, CollectionItem *colitem,
611 ViewCollection *view_collection, Template *template)
613 int col_width = view_collection->collection->item_width;
614 ViewData *view = (ViewData *) colitem->view_data;
616 small_template(area, colitem, view_collection, template);
618 if (!view->image)
619 return; /* Not scanned yet */
621 template->details.x = area->x + col_width - template->details.width;
622 template->details.y = area->y + area->height / 2 - \
623 view->details_height / 2;
626 #define INSIDE(px, py, area) \
627 (px >= area.x && py >= area.y && \
628 px <= area.x + area.width && py <= area.y + area.height)
630 static gboolean test_point(Collection *collection,
631 int point_x, int point_y,
632 CollectionItem *colitem,
633 int width, int height,
634 gpointer user_data)
636 Template template;
637 GdkRectangle area;
638 ViewData *view = (ViewData *) colitem->view_data;
639 ViewCollection *view_collection = (ViewCollection *) user_data;
641 area.x = 0;
642 area.y = 0;
643 area.width = width;
644 area.height = height;
646 fill_template(&area, colitem, view_collection, &template);
648 return INSIDE(point_x, point_y, template.leafname) ||
649 INSIDE(point_x, point_y, template.icon) ||
650 (view->details && INSIDE(point_x, point_y, template.details));
653 /* 'box' renders a background box if the string is also selected */
654 static void draw_string(GtkWidget *widget,
655 PangoLayout *layout,
656 GdkRectangle *area, /* Area available on screen */
657 int width, /* Width of the full string */
658 GtkStateType selection_state,
659 gboolean selected,
660 gboolean box)
662 GdkGC *gc = selected
663 ? widget->style->fg_gc[selection_state]
664 : type_gc;
666 if (selected && box)
667 gtk_paint_flat_box(widget->style, widget->window,
668 selection_state, GTK_SHADOW_NONE,
669 NULL, widget, "text",
670 area->x, area->y,
671 MIN(width, area->width),
672 area->height);
674 if (width > area->width)
676 gdk_gc_set_clip_origin(gc, 0, 0);
677 gdk_gc_set_clip_rectangle(gc, area);
680 gdk_draw_layout(widget->window, gc, area->x, area->y, layout);
682 if (width > area->width)
684 static GdkGC *red_gc = NULL;
686 if (!red_gc)
688 gboolean success;
689 GdkColor red = {0, 0xffff, 0, 0};
691 red_gc = gdk_gc_new(widget->window);
692 gdk_colormap_alloc_colors(
693 gtk_widget_get_colormap(widget),
694 &red, 1, FALSE, TRUE, &success);
695 gdk_gc_set_foreground(red_gc, &red);
697 gdk_draw_rectangle(widget->window, red_gc, TRUE,
698 area->x + area->width - 1, area->y,
699 1, area->height);
700 gdk_gc_set_clip_rectangle(gc, NULL);
704 static void draw_small_icon(GtkWidget *widget,
705 GdkRectangle *area,
706 DirItem *item,
707 MaskedPixmap *image,
708 gboolean selected)
710 int width, height, image_x, image_y;
712 if (!image)
713 return;
715 if (!image->sm_pixbuf)
716 pixmap_make_small(image);
718 width = MIN(image->sm_width, SMALL_WIDTH);
719 height = MIN(image->sm_height, SMALL_HEIGHT);
720 image_x = area->x + ((area->width - width) >> 1);
721 image_y = MAX(0, SMALL_HEIGHT - image->sm_height);
723 gdk_pixbuf_render_to_drawable_alpha(
724 selected ? image->sm_pixbuf_lit : image->sm_pixbuf,
725 widget->window,
726 0, 0, /* src */
727 image_x, area->y + image_y, /* dest */
728 width, height,
729 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
730 GDK_RGB_DITHER_NORMAL, 0, 0);
732 if (item->flags & ITEM_FLAG_SYMLINK)
734 gdk_pixbuf_render_to_drawable_alpha(im_symlink->pixbuf,
735 widget->window,
736 0, 0, /* src */
737 image_x, area->y + 8, /* dest */
738 -1, -1,
739 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
740 GDK_RGB_DITHER_NORMAL, 0, 0);
742 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
744 MaskedPixmap *mp = item->flags & ITEM_FLAG_MOUNTED
745 ? im_mounted
746 : im_unmounted;
748 gdk_pixbuf_render_to_drawable_alpha(mp->pixbuf,
749 widget->window,
750 0, 0, /* src */
751 image_x + 2, area->y + 2, /* dest */
752 -1, -1,
753 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
754 GDK_RGB_DITHER_NORMAL, 0, 0);
758 /* Draw this icon (including any symlink or mount symbol) inside the
759 * given rectangle.
761 static void draw_huge_icon(GtkWidget *widget,
762 GdkRectangle *area,
763 DirItem *item,
764 MaskedPixmap *image,
765 gboolean selected)
767 int width, height;
768 int image_x;
769 int image_y;
771 if (!image)
772 return;
774 width = image->huge_width;
775 height = image->huge_height;
776 image_x = area->x + ((area->width - width) >> 1);
777 image_y = MAX(0, area->height - height - 6);
779 gdk_pixbuf_render_to_drawable_alpha(
780 selected ? image->huge_pixbuf_lit
781 : image->huge_pixbuf,
782 widget->window,
783 0, 0, /* src */
784 image_x, area->y + image_y, /* dest */
785 width, height,
786 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
787 GDK_RGB_DITHER_NORMAL, 0, 0);
789 if (item->flags & ITEM_FLAG_SYMLINK)
791 gdk_pixbuf_render_to_drawable_alpha(im_symlink->pixbuf,
792 widget->window,
793 0, 0, /* src */
794 image_x, area->y + 2, /* dest */
795 -1, -1,
796 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
797 GDK_RGB_DITHER_NORMAL, 0, 0);
799 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
801 MaskedPixmap *mp = item->flags & ITEM_FLAG_MOUNTED
802 ? im_mounted
803 : im_unmounted;
805 gdk_pixbuf_render_to_drawable_alpha(mp->pixbuf,
806 widget->window,
807 0, 0, /* src */
808 image_x, area->y + 2, /* dest */
809 -1, -1,
810 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
811 GDK_RGB_DITHER_NORMAL, 0, 0);
815 /* Create the handers for the View interface */
816 static void view_collection_iface_init(gpointer giface, gpointer iface_data)
818 ViewIfaceClass *iface = giface;
820 g_assert(G_TYPE_FROM_INTERFACE(iface) == VIEW_TYPE_IFACE);
822 /* override stuff */
823 iface->sort = view_collection_sort;
824 iface->style_changed = view_collection_style_changed;
825 iface->autoselect = view_collection_autoselect;
826 iface->add_items = view_collection_add_items;
827 iface->update_items = view_collection_update_items;
828 iface->delete_if = view_collection_delete_if;
829 iface->clear = view_collection_clear;
830 iface->select_all = view_collection_select_all;
831 iface->clear_selection = view_collection_clear_selection;
832 iface->count_items = view_collection_count_items;
833 iface->count_selected = view_collection_count_selected;
834 iface->show_cursor = view_collection_show_cursor;
835 iface->get_iter = view_collection_get_iter;
836 iface->cursor_to_iter = view_collection_cursor_to_iter;
837 iface->set_selected = view_collection_set_selected;
838 iface->get_selected = view_collection_get_selected;
839 iface->set_frozen = view_collection_set_frozen;
840 iface->select_only = view_collection_select_only;
841 iface->wink_item = view_collection_wink_item;
842 iface->autosize = view_collection_autosize;
843 iface->cursor_visible = view_collection_cursor_visible;
844 iface->set_base = view_collection_set_base;
847 /* It's time to make the tooltip appear. If we're not over the item any
848 * more, or the item doesn't need a tooltip, do nothing.
850 static gboolean tooltip_activate(ViewCollection *view_collection)
852 Collection *collection;
853 gint x, y;
854 int i;
855 GString *tip = NULL;
857 g_return_val_if_fail(tip_item != NULL, 0);
859 if (!view_collection->filer_window)
860 return FALSE; /* Window has been destroyed */
862 tooltip_show(NULL);
864 collection = view_collection->collection;
865 gdk_window_get_pointer(GTK_WIDGET(collection)->window, &x, &y, NULL);
866 i = collection_get_item(collection, x, y);
867 if (i == -1 || ((DirItem *) collection->items[i].data) != tip_item)
868 return FALSE; /* Not still under the pointer */
870 /* OK, the filer window still exists and the pointer is still
871 * over the same item. Do we need to show a tip?
874 tip = g_string_new(NULL);
876 if (name_is_truncated(view_collection, i))
878 g_string_append(tip, tip_item->leafname);
879 g_string_append_c(tip, '\n');
882 filer_add_tip_details(view_collection->filer_window, tip, tip_item);
884 if (tip->len > 1)
886 g_string_truncate(tip, tip->len - 1);
888 tooltip_show(tip->str);
891 g_string_free(tip, TRUE);
893 return FALSE;
896 static gboolean name_is_truncated(ViewCollection *view_collection, int i)
898 Template template;
899 Collection *collection = view_collection->collection;
900 FilerWindow *filer_window = view_collection->filer_window;
901 CollectionItem *colitem = &collection->items[i];
902 int col = i % collection->columns;
903 int row = i / collection->columns;
904 GdkRectangle area;
905 ViewData *view = (ViewData *) colitem->view_data;
907 /* TODO: What if the window is narrower than 1 column? */
908 if (filer_window->display_style == LARGE_ICONS ||
909 filer_window->display_style == HUGE_ICONS)
910 return FALSE; /* These wrap rather than truncate */
912 area.x = col * collection->item_width;
913 area.y = row * collection->item_height;
914 area.height = collection->item_height;
916 if (col == collection->columns - 1)
917 area.width = GTK_WIDGET(collection)->allocation.width - area.x;
918 else
919 area.width = collection->item_width;
921 fill_template(&area, colitem, view_collection, &template);
923 return template.leafname.width < view->name_width;
926 static gint coll_motion_notify(GtkWidget *widget,
927 GdkEventMotion *event,
928 ViewCollection *view_collection)
930 Collection *collection = view_collection->collection;
931 FilerWindow *filer_window = view_collection->filer_window;
932 int i;
934 i = collection_get_item(collection, event->x, event->y);
936 if (i == -1)
938 tooltip_show(NULL);
939 tip_item = NULL;
941 else
943 DirItem *item = (DirItem *) collection->items[i].data;
945 if (item != tip_item)
947 tooltip_show(NULL);
949 tip_item = item;
950 if (item)
951 tooltip_prime((GtkFunction) tooltip_activate,
952 G_OBJECT(view_collection));
956 if (motion_state != MOTION_READY_FOR_DND)
957 return FALSE;
959 if (!dnd_motion_moved(event))
960 return FALSE;
962 i = collection_get_item(collection,
963 event->x - (event->x_root - drag_start_x),
964 event->y - (event->y_root - drag_start_y));
965 if (i == -1)
966 return FALSE;
968 collection_wink_item(collection, -1);
970 if (!collection->items[i].selected)
972 if (event->state & GDK_BUTTON1_MASK)
974 /* Select just this one */
975 filer_window->temp_item_selected = TRUE;
976 collection_clear_except(collection, i);
978 else
980 if (collection->number_selected == 0)
981 filer_window->temp_item_selected = TRUE;
982 collection_select_item(collection, i);
986 g_return_val_if_fail(collection->number_selected > 0, TRUE);
988 if (collection->number_selected == 1)
990 DirItem *item = (DirItem *) collection->items[i].data;
991 ViewData *view = (ViewData *) collection->items[i].view_data;
993 if (!item->image)
994 item = dir_update_item(filer_window->directory,
995 item->leafname);
997 if (!item)
999 report_error(_("Item no longer exists!"));
1000 return FALSE;
1003 drag_one_item(widget, event,
1004 make_path(filer_window->sym_path, item->leafname)->str,
1005 item, view ? view->image : NULL);
1007 else
1009 GString *uris;
1011 uris = g_string_new(NULL);
1012 create_uri_list(view_collection, uris);
1013 drag_selection(widget, event, uris->str);
1014 g_string_free(uris, TRUE);
1017 return FALSE;
1020 /* Viewport is to be resized, so calculate increments */
1021 static void size_allocate(GtkWidget *w, GtkAllocation *a, gpointer data)
1023 Collection *col = ((ViewCollection *) data)->collection;
1025 col->vadj->step_increment = col->item_height;
1026 col->vadj->page_increment = col->vadj->page_size;
1029 /* Append all the URIs in the selection to the string */
1030 static void create_uri_list(ViewCollection *view_collection, GString *string)
1032 GString *leader;
1033 ViewIter iter;
1034 DirItem *item;
1036 leader = g_string_new("file://");
1037 g_string_append(leader, our_host_name_for_dnd());
1038 g_string_append(leader, view_collection->filer_window->sym_path);
1039 if (leader->str[leader->len - 1] != '/')
1040 g_string_append_c(leader, '/');
1042 make_iter(view_collection, &iter, VIEW_ITER_SELECTED);
1043 while ((item = iter.next(&iter)))
1045 g_string_append(string, leader->str);
1046 g_string_append(string, item->leafname);
1047 g_string_append(string, "\r\n");
1050 g_string_free(leader, TRUE);
1053 static gint coll_button_release(GtkWidget *widget,
1054 GdkEventButton *event,
1055 ViewCollection *view_collection)
1057 if (dnd_motion_release(event))
1059 if (motion_buttons_pressed == 0 &&
1060 view_collection->collection->lasso_box)
1062 collection_end_lasso(view_collection->collection,
1063 event->button == 1 ? GDK_SET : GDK_INVERT);
1065 return FALSE;
1068 perform_action(view_collection, event);
1070 return FALSE;
1073 static gint coll_button_press(GtkWidget *widget,
1074 GdkEventButton *event,
1075 ViewCollection *view_collection)
1077 collection_set_cursor_item(view_collection->collection, -1);
1079 if (dnd_motion_press(widget, event))
1080 perform_action(view_collection, event);
1082 return FALSE;
1085 static void perform_action(ViewCollection *view_collection,
1086 GdkEventButton *event)
1088 Collection *collection = view_collection->collection;
1089 DirItem *dir_item;
1090 int item;
1091 gboolean press = event->type == GDK_BUTTON_PRESS;
1092 gboolean selected = FALSE;
1093 OpenFlags flags = 0;
1094 BindAction action;
1095 FilerWindow *filer_window = view_collection->filer_window;
1097 if (event->button > 3)
1098 return;
1100 item = collection_get_item(collection, event->x, event->y);
1102 if (item != -1 && event->button == 1 &&
1103 collection->items[item].selected &&
1104 filer_window->selection_state == GTK_STATE_INSENSITIVE)
1106 /* Possibly a really slow DnD operation? */
1107 filer_window->temp_item_selected = FALSE;
1109 filer_selection_changed(filer_window, event->time);
1110 return;
1113 if (filer_window->target_cb)
1115 dnd_motion_ungrab();
1116 if (item != -1 && press && event->button == 1)
1118 ViewIter iter;
1119 make_item_iter(view_collection, &iter, item);
1121 filer_window->target_cb(filer_window, &iter,
1122 filer_window->target_data);
1124 filer_target_mode(filer_window, NULL, NULL, NULL);
1126 return;
1129 action = bind_lookup_bev(
1130 item == -1 ? BIND_DIRECTORY : BIND_DIRECTORY_ICON,
1131 event);
1133 if (item != -1)
1135 dir_item = (DirItem *) collection->items[item].data;
1136 selected = collection->items[item].selected;
1138 else
1139 dir_item = NULL;
1141 switch (action)
1143 case ACT_CLEAR_SELECTION:
1144 collection_clear_selection(collection);
1145 break;
1146 case ACT_TOGGLE_SELECTED:
1147 collection_toggle_item(collection, item);
1148 break;
1149 case ACT_SELECT_EXCL:
1150 collection_clear_except(collection, item);
1151 break;
1152 case ACT_EDIT_ITEM:
1153 flags |= OPEN_SHIFT;
1154 /* (no break) */
1155 case ACT_OPEN_ITEM:
1157 ViewIter iter;
1159 make_item_iter(view_collection, &iter, item);
1161 if (event->button != 1 || event->state & GDK_MOD1_MASK)
1162 flags |= OPEN_CLOSE_WINDOW;
1163 else
1164 flags |= OPEN_SAME_WINDOW;
1165 if (o_new_button_1.int_value)
1166 flags ^= OPEN_SAME_WINDOW;
1167 if (event->type == GDK_2BUTTON_PRESS)
1168 collection_unselect_item(collection, item);
1169 dnd_motion_ungrab();
1171 filer_openitem(filer_window, &iter, flags);
1172 break;
1174 case ACT_POPUP_MENU:
1176 ViewIter iter;
1178 dnd_motion_ungrab();
1179 tooltip_show(NULL);
1181 make_item_iter(view_collection, &iter, item);
1182 show_filer_menu(filer_window,
1183 (GdkEvent *) event, &iter);
1184 break;
1186 case ACT_PRIME_AND_SELECT:
1187 if (!selected)
1188 collection_clear_except(collection, item);
1189 dnd_motion_start(MOTION_READY_FOR_DND);
1190 break;
1191 case ACT_PRIME_AND_TOGGLE:
1192 collection_toggle_item(collection, item);
1193 dnd_motion_start(MOTION_READY_FOR_DND);
1194 break;
1195 case ACT_PRIME_FOR_DND:
1196 dnd_motion_start(MOTION_READY_FOR_DND);
1197 break;
1198 case ACT_IGNORE:
1199 if (press && event->button < 4)
1201 if (item)
1202 collection_wink_item(collection, item);
1203 dnd_motion_start(MOTION_NONE);
1205 break;
1206 case ACT_LASSO_CLEAR:
1207 collection_clear_selection(collection);
1208 /* (no break) */
1209 case ACT_LASSO_MODIFY:
1210 collection_lasso_box(collection, event->x, event->y);
1211 break;
1212 case ACT_RESIZE:
1213 filer_window_autosize(filer_window);
1214 break;
1215 default:
1216 g_warning("Unsupported action : %d\n", action);
1217 break;
1221 /* Nothing is selected anymore - give up primary */
1222 static void lost_selection(Collection *collection,
1223 guint time,
1224 gpointer user_data)
1226 ViewCollection *view_collection = VIEW_COLLECTION(user_data);
1228 filer_lost_selection(view_collection->filer_window, time);
1231 static void selection_changed(Collection *collection,
1232 gint time,
1233 gpointer user_data)
1235 ViewCollection *view_collection = VIEW_COLLECTION(user_data);
1237 filer_selection_changed(view_collection->filer_window, time);
1240 static void display_free_colitem(Collection *collection,
1241 CollectionItem *colitem)
1243 ViewData *view = (ViewData *) colitem->view_data;
1245 if (!view)
1246 return;
1248 if (view->layout)
1250 g_object_unref(G_OBJECT(view->layout));
1251 view->layout = NULL;
1253 if (view->details)
1254 g_object_unref(G_OBJECT(view->details));
1256 if (view->image)
1257 g_object_unref(view->image);
1259 g_free(view);
1262 static void add_item(ViewCollection *view_collection, DirItem *item)
1264 Collection *collection = view_collection->collection;
1265 FilerWindow *filer_window = view_collection->filer_window;
1266 int old_w = collection->item_width;
1267 int old_h = collection->item_height;
1268 int w, h, i;
1270 i = collection_insert(collection, item,
1271 display_create_viewdata(filer_window, item));
1273 calc_size(filer_window, &collection->items[i], &w, &h);
1275 if (w > old_w || h > old_h)
1276 collection_set_item_size(collection,
1277 MAX(old_w, w),
1278 MAX(old_h, h));
1281 static void style_set(Collection *collection,
1282 GtkStyle *style,
1283 ViewCollection *view_collection)
1285 view_collection_style_changed(VIEW(view_collection),
1286 VIEW_UPDATE_VIEWDATA | VIEW_UPDATE_NAME);
1289 /* Return the size needed for this item */
1290 static void calc_size(FilerWindow *filer_window, CollectionItem *colitem,
1291 int *width, int *height)
1293 int pix_width, pix_height;
1294 int w;
1295 DisplayStyle style = filer_window->display_style;
1296 ViewData *view = (ViewData *) colitem->view_data;
1298 if (filer_window->details_type == DETAILS_NONE)
1300 if (style == HUGE_ICONS)
1302 if (view->image)
1304 if (!view->image->huge_pixbuf)
1305 pixmap_make_huge(view->image);
1306 pix_width = view->image->huge_width;
1307 pix_height = view->image->huge_height;
1309 else
1311 pix_width = HUGE_WIDTH * 3 / 2;
1312 pix_height = HUGE_HEIGHT * 3 / 2;
1314 *width = MAX(pix_width, view->name_width) + 4;
1315 *height = view->name_height + pix_height + 4;
1317 else if (style == SMALL_ICONS)
1319 w = MIN(view->name_width, o_small_width.int_value);
1320 *width = SMALL_WIDTH + 12 + w;
1321 *height = MAX(view->name_height, SMALL_HEIGHT) + 4;
1323 else
1325 if (view->image)
1326 pix_width = view->image->width;
1327 else
1328 pix_width = ICON_WIDTH;
1329 *width = MAX(pix_width, view->name_width) + 4;
1330 *height = view->name_height + ICON_HEIGHT + 2;
1333 else
1335 w = view->details_width;
1336 if (style == HUGE_ICONS)
1338 *width = HUGE_WIDTH + 12 + MAX(w, view->name_width);
1339 *height = HUGE_HEIGHT - 4;
1341 else if (style == SMALL_ICONS)
1343 int text_height;
1345 *width = SMALL_WIDTH + view->name_width + 12 + w;
1346 text_height = MAX(view->name_height,
1347 view->details_height);
1348 *height = MAX(text_height, SMALL_HEIGHT) + 4;
1350 else
1352 *width = ICON_WIDTH + 12 + MAX(w, view->name_width);
1353 *height = ICON_HEIGHT;
1358 static void update_item(ViewCollection *view_collection, int i)
1360 Collection *collection = view_collection->collection;
1361 int old_w = collection->item_width;
1362 int old_h = collection->item_height;
1363 int w, h;
1364 CollectionItem *colitem;
1365 FilerWindow *filer_window = view_collection->filer_window;
1367 g_return_if_fail(i >= 0 && i < collection->number_of_items);
1369 colitem = &collection->items[i];
1371 display_update_view(filer_window,
1372 (DirItem *) colitem->data,
1373 (ViewData *) colitem->view_data,
1374 FALSE);
1376 calc_size(filer_window, colitem, &w, &h);
1377 if (w > old_w || h > old_h)
1378 collection_set_item_size(collection,
1379 MAX(old_w, w),
1380 MAX(old_h, h));
1382 collection_draw_item(collection, i, TRUE);
1385 /* Implementations of the View interface. See view_iface.c for comments. */
1387 static void view_collection_style_changed(ViewIface *view, int flags)
1389 ViewCollection *view_collection = VIEW_COLLECTION(view);
1390 FilerWindow *filer_window = view_collection->filer_window;
1391 int i;
1392 Collection *col = view_collection->collection;
1393 int width = MIN_ITEM_WIDTH;
1394 int height = SMALL_HEIGHT;
1395 int n = col->number_of_items;
1397 if (n == 0 && filer_window->display_style != SMALL_ICONS)
1398 height = ICON_HEIGHT;
1400 /* Recalculate all the ViewData structs for this window
1401 * (needed if the text or image has changed in any way) and
1402 * get the size of each item.
1404 for (i = 0; i < n; i++)
1406 CollectionItem *ci = &col->items[i];
1407 int w, h;
1409 if (flags & (VIEW_UPDATE_VIEWDATA | VIEW_UPDATE_NAME))
1410 display_update_view(filer_window,
1411 (DirItem *) ci->data,
1412 (ViewData *) ci->view_data,
1413 (flags & VIEW_UPDATE_NAME) != 0);
1415 calc_size(filer_window, ci, &w, &h);
1416 if (w > width)
1417 width = w;
1418 if (h > height)
1419 height = h;
1422 collection_set_item_size(col, width, height);
1424 gtk_widget_queue_draw(GTK_WIDGET(view_collection));
1427 static void view_collection_sort(ViewIface *view)
1429 ViewCollection *view_collection = VIEW_COLLECTION(view);
1431 collection_qsort(view_collection->collection,
1432 view_collection->filer_window->sort_fn);
1435 static gboolean view_collection_autoselect(ViewIface *view, const gchar *leaf)
1437 ViewCollection *view_collection = VIEW_COLLECTION(view);
1438 Collection *col = view_collection->collection;
1439 int i;
1441 for (i = 0; i < col->number_of_items; i++)
1443 DirItem *item = (DirItem *) col->items[i].data;
1445 if (strcmp(item->leafname, leaf) == 0)
1447 if (col->cursor_item != -1)
1448 collection_set_cursor_item(col, i);
1449 else
1450 collection_wink_item(col, i);
1451 return TRUE;
1455 return FALSE;
1458 static void view_collection_add_items(ViewIface *view, GPtrArray *items)
1460 ViewCollection *view_collection = VIEW_COLLECTION(view);
1461 Collection *collection = view_collection->collection;
1462 FilerWindow *filer_window = view_collection->filer_window;
1463 int old_num, i;
1465 old_num = collection->number_of_items;
1466 for (i = 0; i < items->len; i++)
1468 DirItem *item = (DirItem *) items->pdata[i];
1469 char *leafname = item->leafname;
1471 if (leafname[0] == '.')
1473 if (!filer_window->show_hidden)
1474 continue;
1476 if (leafname[1] == '\0')
1477 continue; /* Never show '.' */
1479 if (leafname[1] == '.' &&
1480 leafname[2] == '\0')
1481 continue; /* Never show '..' */
1484 add_item(view_collection, item);
1487 if (old_num != collection->number_of_items)
1488 view_collection_sort(view);
1491 static void view_collection_update_items(ViewIface *view, GPtrArray *items)
1493 ViewCollection *view_collection = VIEW_COLLECTION(view);
1494 Collection *collection = view_collection->collection;
1495 FilerWindow *filer_window = view_collection->filer_window;
1496 int i;
1498 g_return_if_fail(items->len > 0);
1500 /* The item data has already been modified, so this gives the
1501 * final sort order...
1503 collection_qsort(collection, filer_window->sort_fn);
1505 for (i = 0; i < items->len; i++)
1507 DirItem *item = (DirItem *) items->pdata[i];
1508 const gchar *leafname = item->leafname;
1509 int j;
1511 if (leafname[0] == '.' && filer_window->show_hidden == FALSE)
1512 continue;
1514 j = collection_find_item(collection, item,
1515 filer_window->sort_fn);
1517 if (j < 0)
1518 g_warning("Failed to find '%s'\n", leafname);
1519 else
1520 update_item(view_collection, j);
1524 static void view_collection_delete_if(ViewIface *view,
1525 gboolean (*test)(gpointer item, gpointer data),
1526 gpointer data)
1528 ViewCollection *view_collection = VIEW_COLLECTION(view);
1529 Collection *collection = view_collection->collection;
1531 collection_delete_if(collection, test, data);
1534 static void view_collection_clear(ViewIface *view)
1536 ViewCollection *view_collection = VIEW_COLLECTION(view);
1537 Collection *collection = view_collection->collection;
1539 collection_clear(collection);
1542 static void view_collection_select_all(ViewIface *view)
1544 ViewCollection *view_collection = VIEW_COLLECTION(view);
1545 Collection *collection = view_collection->collection;
1547 collection_select_all(collection);
1550 static void view_collection_clear_selection(ViewIface *view)
1552 ViewCollection *view_collection = VIEW_COLLECTION(view);
1553 Collection *collection = view_collection->collection;
1555 collection_clear_selection(collection);
1558 static int view_collection_count_items(ViewIface *view)
1560 ViewCollection *view_collection = VIEW_COLLECTION(view);
1561 Collection *collection = view_collection->collection;
1563 return collection->number_of_items;
1566 static int view_collection_count_selected(ViewIface *view)
1568 ViewCollection *view_collection = VIEW_COLLECTION(view);
1569 Collection *collection = view_collection->collection;
1571 return collection->number_selected;
1574 static void view_collection_show_cursor(ViewIface *view)
1576 ViewCollection *view_collection = VIEW_COLLECTION(view);
1577 Collection *collection = view_collection->collection;
1579 collection_move_cursor(collection, 0, 0);
1582 /* The first time the next() method is used, this is called */
1583 static DirItem *iter_init(ViewIter *iter)
1585 ViewCollection *view_collection = iter->view_collection;
1586 Collection *collection = view_collection->collection;
1587 int i = -1;
1588 int n = collection->number_of_items;
1589 int flags = iter->flags;
1591 iter->peek = iter_peek;
1593 if (iter->n_remaining == 0)
1594 return NULL;
1596 if (flags & VIEW_ITER_FROM_CURSOR)
1598 i = collection->cursor_item;
1599 if (i == -1)
1600 return NULL; /* No cursor */
1602 else if (flags & VIEW_ITER_FROM_BASE)
1603 i = view_collection->cursor_base;
1605 if (i < 0 || i >= n)
1607 /* Either a normal iteraction, or an iteration from an
1608 * invalid starting point.
1610 if (flags & VIEW_ITER_BACKWARDS)
1611 i = n - 1;
1612 else
1613 i = 0;
1616 if (i < 0 || i >= n)
1617 return NULL; /* No items at all! */
1619 iter->next = flags & VIEW_ITER_BACKWARDS ? iter_prev : iter_next;
1620 iter->n_remaining--;
1621 iter->i = i;
1623 if (flags & VIEW_ITER_SELECTED && !collection->items[i].selected)
1624 return iter->next(iter);
1625 return iter->peek(iter);
1627 /* Advance iter to point to the next item and return the new item
1628 * (this saves you calling peek after next each time).
1630 static DirItem *iter_next(ViewIter *iter)
1632 Collection *collection = iter->view_collection->collection;
1633 int n = collection->number_of_items;
1634 int i = iter->i;
1636 g_return_val_if_fail(iter->n_remaining >= 0, NULL);
1638 /* i is the last item returned (always valid) */
1640 g_return_val_if_fail(i >= 0 && i < n, NULL);
1642 while (iter->n_remaining)
1644 i++;
1645 iter->n_remaining--;
1647 if (i == n)
1648 i = 0;
1650 g_return_val_if_fail(i >= 0 && i < n, NULL);
1652 if (iter->flags & VIEW_ITER_SELECTED &&
1653 !collection->items[i].selected)
1654 continue;
1656 iter->i = i;
1657 return collection->items[i].data;
1660 iter->i = -1;
1661 return NULL;
1664 /* Like iter_next, but in the other direction */
1665 static DirItem *iter_prev(ViewIter *iter)
1667 Collection *collection = iter->view_collection->collection;
1668 int n = collection->number_of_items;
1669 int i = iter->i;
1671 g_return_val_if_fail(iter->n_remaining >= 0, NULL);
1673 /* i is the last item returned (always valid) */
1675 g_return_val_if_fail(i >= 0 && i < n, NULL);
1677 while (iter->n_remaining)
1679 i--;
1680 iter->n_remaining--;
1682 if (i == -1)
1683 i = collection->number_of_items - 1;
1685 g_return_val_if_fail(i >= 0 && i < n, NULL);
1687 if (iter->flags & VIEW_ITER_SELECTED &&
1688 !collection->items[i].selected)
1689 continue;
1691 iter->i = i;
1692 return collection->items[i].data;
1695 iter->i = -1;
1696 return NULL;
1699 static DirItem *iter_peek(ViewIter *iter)
1701 Collection *collection = iter->view_collection->collection;
1702 int i = iter->i;
1704 if (i == -1)
1705 return NULL;
1707 g_return_val_if_fail(i >= 0 && i < collection->number_of_items, NULL);
1709 return collection->items[i].data;
1712 static void make_iter(ViewCollection *view_collection, ViewIter *iter,
1713 IterFlags flags)
1715 Collection *collection = view_collection->collection;
1717 iter->view_collection = view_collection;
1718 iter->next = iter_init;
1719 iter->peek = NULL;
1720 iter->i = -1;
1722 iter->flags = flags;
1724 if (flags & VIEW_ITER_ONE_ONLY)
1726 iter->n_remaining = 1;
1727 iter->next(iter);
1729 else
1730 iter->n_remaining = collection->number_of_items;
1733 /* Set the iterator to return 'i' on the next peek() */
1734 static void make_item_iter(ViewCollection *view_collection,
1735 ViewIter *iter, int i)
1737 Collection *collection = view_collection->collection;
1739 g_return_if_fail(i >= -1 && i < collection->number_of_items);
1741 make_iter(view_collection, iter, 0);
1743 iter->i = i;
1744 iter->next = iter_next;
1745 iter->peek = iter_peek;
1746 iter->n_remaining = 0;
1749 static void view_collection_get_iter(ViewIface *view,
1750 ViewIter *iter, IterFlags flags)
1752 ViewCollection *view_collection = VIEW_COLLECTION(view);
1754 make_iter(view_collection, iter, flags);
1757 static void view_collection_cursor_to_iter(ViewIface *view, ViewIter *iter)
1759 ViewCollection *view_collection = VIEW_COLLECTION(view);
1760 Collection *collection = view_collection->collection;
1762 g_return_if_fail(iter == NULL ||
1763 iter->view_collection == view_collection);
1765 collection_set_cursor_item(collection, iter ? iter->i : -1);
1768 static void view_collection_set_selected(ViewIface *view,
1769 ViewIter *iter,
1770 gboolean selected)
1772 ViewCollection *view_collection = VIEW_COLLECTION(view);
1773 Collection *collection = view_collection->collection;
1775 g_return_if_fail(iter != NULL &&
1776 iter->view_collection == view_collection);
1777 g_return_if_fail(iter->i >= 0 && iter->i < collection->number_of_items);
1779 if (selected)
1780 collection_select_item(collection, iter->i);
1781 else
1782 collection_unselect_item(collection, iter->i);
1785 static gboolean view_collection_get_selected(ViewIface *view, ViewIter *iter)
1787 ViewCollection *view_collection = VIEW_COLLECTION(view);
1788 Collection *collection = view_collection->collection;
1790 g_return_val_if_fail(iter != NULL &&
1791 iter->view_collection == view_collection, FALSE);
1792 g_return_val_if_fail(iter->i >= 0 &&
1793 iter->i < collection->number_of_items, FALSE);
1795 return collection->items[iter->i].selected;
1798 static void view_collection_select_only(ViewIface *view, ViewIter *iter)
1800 ViewCollection *view_collection = VIEW_COLLECTION(view);
1801 Collection *collection = view_collection->collection;
1803 g_return_if_fail(iter != NULL &&
1804 iter->view_collection == view_collection);
1805 g_return_if_fail(iter->i >= 0 && iter->i < collection->number_of_items);
1807 collection_clear_except(collection, iter->i);
1810 static void view_collection_set_frozen(ViewIface *view, gboolean frozen)
1812 ViewCollection *view_collection = VIEW_COLLECTION(view);
1813 Collection *collection = view_collection->collection;
1815 if (frozen)
1816 collection->block_selection_changed++;
1817 else
1818 collection_unblock_selection_changed(collection,
1819 gtk_get_current_event_time(), TRUE);
1822 static void view_collection_wink_item(ViewIface *view, ViewIter *iter)
1824 ViewCollection *view_collection = VIEW_COLLECTION(view);
1825 Collection *collection = view_collection->collection;
1827 g_return_if_fail(iter != NULL &&
1828 iter->view_collection == view_collection);
1829 g_return_if_fail(iter->i >= 0 && iter->i < collection->number_of_items);
1831 collection_wink_item(collection, iter->i);
1834 static void view_collection_autosize(ViewIface *view)
1836 ViewCollection *view_collection = VIEW_COLLECTION(view);
1837 FilerWindow *filer_window = view_collection->filer_window;
1838 Collection *collection = view_collection->collection;
1839 int n;
1840 int w = collection->item_width;
1841 int h = collection->item_height;
1842 int x;
1843 int rows, cols;
1844 int max_x, max_rows;
1845 const float r = 2.5;
1846 int t = 0;
1847 int space = 0;
1849 /* Get the extra height required for the toolbar and minibuffer,
1850 * if visible.
1852 if (o_toolbar.int_value != TOOLBAR_NONE)
1853 t = filer_window->toolbar->allocation.height;
1854 if (filer_window->message)
1855 t += filer_window->message->allocation.height;
1856 if (GTK_WIDGET_VISIBLE(filer_window->minibuffer_area))
1858 GtkRequisition req;
1860 gtk_widget_size_request(filer_window->minibuffer_area, &req);
1861 space = req.height + 2;
1862 t += space;
1865 n = collection->number_of_items;
1866 n = MAX(n, 2);
1868 max_x = (o_filer_size_limit.int_value * screen_width) / 100;
1869 max_rows = (o_filer_size_limit.int_value * screen_height) / (h * 100);
1871 /* Aim for a size where
1872 * x = r(y + t + h), (1)
1873 * unless that's too wide.
1875 * t = toolbar (and minibuffer) height
1876 * r = desired (width / height) ratio
1878 * Want to display all items:
1879 * (x/w)(y/h) = n
1880 * => xy = nwh
1881 * => x(x/r - t - h) = nwh (from 1)
1882 * => xx - x.rt - hr(1 - nw) = 0
1883 * => 2x = rt +/- sqrt(rt.rt + 4hr(nw - 1))
1884 * Now,
1885 * 4hr(nw - 1) > 0
1886 * so
1887 * sqrt(rt.rt + ...) > rt
1889 * So, the +/- must be +:
1891 * => x = (rt + sqrt(rt.rt + 4hr(nw - 1))) / 2
1893 * ( + w - 1 to round up)
1895 x = (r * t + sqrt(r*r*t*t + 4*h*r * (n*w - 1))) / 2 + w - 1;
1897 /* Limit x */
1898 if (x > max_x)
1899 x = max_x;
1901 cols = x / w;
1902 cols = MAX(cols, 1);
1904 /* Choose rows to display all items given our chosen x.
1905 * Don't make the window *too* big!
1907 rows = (n + cols - 1) / cols;
1908 if (rows > max_rows)
1909 rows = max_rows;
1911 /* Leave some room for extra icons, but only in Small Icons mode
1912 * otherwise it takes up too much space.
1913 * Also, don't add space if the minibuffer is open.
1915 if (space == 0)
1916 space = filer_window->display_style == SMALL_ICONS ? h : 2;
1918 filer_window_set_size(filer_window,
1919 w * MAX(cols, 1),
1920 h * MAX(rows, 1) + space);
1923 static gboolean view_collection_cursor_visible(ViewIface *view)
1925 ViewCollection *view_collection = VIEW_COLLECTION(view);
1926 Collection *collection = view_collection->collection;
1928 return collection->cursor_item != -1;
1931 static void view_collection_set_base(ViewIface *view, ViewIter *iter)
1933 ViewCollection *view_collection = VIEW_COLLECTION(view);
1935 view_collection->cursor_base = iter->i;
1938 static void drag_end(GtkWidget *widget,
1939 GdkDragContext *context,
1940 ViewCollection *view_collection)
1942 if (view_collection->filer_window->temp_item_selected)
1944 view_collection_clear_selection(VIEW(view_collection));
1945 view_collection->filer_window->temp_item_selected = FALSE;
1949 /* Remove highlights */
1950 static void drag_leave(GtkWidget *widget,
1951 GdkDragContext *context,
1952 guint32 time,
1953 ViewCollection *view_collection)
1955 dnd_spring_abort();
1956 #if 0
1957 if (scrolled_adj)
1959 g_signal_handler_disconnect(scrolled_adj, scrolled_signal);
1960 scrolled_adj = NULL;
1962 #endif
1965 /* Called during the drag when the mouse is in a widget registered
1966 * as a drop target. Returns TRUE if we can accept the drop.
1968 static gboolean drag_motion(GtkWidget *widget,
1969 GdkDragContext *context,
1970 gint x,
1971 gint y,
1972 guint time,
1973 ViewCollection *view_collection)
1975 DirItem *item;
1976 int item_number;
1977 GdkDragAction action = context->suggested_action;
1978 char *new_path = NULL;
1979 const char *type = NULL;
1980 gboolean retval = FALSE;
1981 Collection *collection = view_collection->collection;
1983 if (o_dnd_drag_to_icons.int_value)
1984 item_number = collection_get_item(collection, x, y);
1985 else
1986 item_number = -1;
1988 item = item_number >= 0
1989 ? (DirItem *) collection->items[item_number].data
1990 : NULL;
1992 if (item && collection->items[item_number].selected)
1993 type = NULL;
1994 else
1995 type = dnd_motion_item(context, &item);
1997 if (!type)
1998 item = NULL;
2000 /* Don't allow drops to non-writeable directories. BUT, still
2001 * allow drops on non-writeable SUBdirectories so that we can
2002 * do the spring-open thing.
2004 if (item && type == drop_dest_dir &&
2005 !(item->flags & ITEM_FLAG_APPDIR))
2007 #if 0
2008 /* XXX: This is needed so that directories don't
2009 * spring open while we scroll. Should go in
2010 * view_collection.c, I think.
2013 /* XXX: Now it IS in view_collection, maybe we should
2014 * fix it?
2017 GtkObject *vadj = GTK_OBJECT(collection->vadj);
2019 /* Subdir: prepare for spring-open */
2020 if (scrolled_adj != vadj)
2022 if (scrolled_adj)
2023 gtk_signal_disconnect(scrolled_adj,
2024 scrolled_signal);
2025 scrolled_adj = vadj;
2026 scrolled_signal = gtk_signal_connect(
2027 scrolled_adj,
2028 "value_changed",
2029 GTK_SIGNAL_FUNC(scrolled),
2030 collection);
2032 #endif
2033 dnd_spring_load(context, view_collection->filer_window);
2035 else
2036 dnd_spring_abort();
2038 if (item)
2040 collection_set_cursor_item(collection,
2041 item_number);
2043 else
2045 collection_set_cursor_item(collection, -1);
2047 /* Disallow background drops within a single window */
2048 if (type && gtk_drag_get_source_widget(context) == widget)
2049 type = NULL;
2052 if (type)
2054 if (item)
2055 new_path = make_path(
2056 view_collection->filer_window->sym_path,
2057 item->leafname)->str;
2058 else
2059 new_path = view_collection->filer_window->sym_path;
2062 g_dataset_set_data(context, "drop_dest_type", (gpointer) type);
2063 if (type)
2065 gdk_drag_status(context, action, time);
2066 g_dataset_set_data_full(context, "drop_dest_path",
2067 g_strdup(new_path), g_free);
2068 retval = TRUE;
2071 return retval;