r3423: Added Xinerama support (Tony Houghton).
[rox-filer.git] / ROX-Filer / src / view_collection.c
blobda91880f8b06c93cba589d7408af9669d35d278c
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2003, 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>
29 #include <string.h>
31 #include "global.h"
33 #include "collection.h"
34 #include "view_iface.h"
35 #include "view_collection.h"
36 #include "type.h"
37 #include "pixmaps.h"
38 #include "dir.h"
39 #include "diritem.h"
40 #include "gui_support.h"
41 #include "support.h"
42 #include "dnd.h"
43 #include "bind.h"
44 #include "options.h"
46 #include "toolbar.h" /* for resizing */
47 #include "filer.h"
48 #include "display.h"
50 #define MIN_ITEM_WIDTH 64
52 static gpointer parent_class = NULL;
54 struct _ViewCollectionClass {
55 GtkViewportClass parent;
58 struct _ViewCollection {
59 GtkViewport viewport;
61 Collection *collection;
62 FilerWindow *filer_window; /* Used for styles, etc */
64 int cursor_base; /* Cursor when minibuffer opened */
67 typedef struct _Template Template;
69 struct _Template {
70 GdkRectangle icon;
71 GdkRectangle leafname;
72 GdkRectangle details;
75 /* GC for drawing colour filenames */
76 static GdkGC *type_gc = NULL;
78 /* Static prototypes */
79 static void view_collection_finialize(GObject *object);
80 static void view_collection_class_init(gpointer gclass, gpointer data);
81 static void view_collection_init(GTypeInstance *object, gpointer gclass);
83 static void draw_item(GtkWidget *widget,
84 CollectionItem *item,
85 GdkRectangle *area,
86 gpointer user_data);
87 static void fill_template(GdkRectangle *area, CollectionItem *item,
88 ViewCollection *view_collection, Template *template);
89 static void huge_template(GdkRectangle *area, CollectionItem *colitem,
90 ViewCollection *view_collection, Template *template);
91 static void large_template(GdkRectangle *area, CollectionItem *colitem,
92 ViewCollection *view_collection, Template *template);
93 static void small_template(GdkRectangle *area, CollectionItem *colitem,
94 ViewCollection *view_collection, Template *template);
95 static void huge_full_template(GdkRectangle *area, CollectionItem *colitem,
96 ViewCollection *view_collection, Template *template);
97 static void large_full_template(GdkRectangle *area, CollectionItem *colitem,
98 ViewCollection *view_collection, Template *template);
99 static void small_full_template(GdkRectangle *area, CollectionItem *colitem,
100 ViewCollection *view_collection, Template *template);
101 static gboolean test_point(Collection *collection,
102 int point_x, int point_y,
103 CollectionItem *item,
104 int width, int height,
105 gpointer user_data);
106 static void draw_string(GtkWidget *widget,
107 PangoLayout *layout,
108 GdkRectangle *area, /* Area available on screen */
109 int width, /* Width of the full string */
110 GtkStateType selection_state,
111 gboolean box);
112 static void view_collection_iface_init(gpointer giface, gpointer iface_data);
113 static gint coll_motion_notify(GtkWidget *widget,
114 GdkEventMotion *event,
115 ViewCollection *view_collection);
116 static gint coll_button_release(GtkWidget *widget,
117 GdkEventButton *event,
118 ViewCollection *view_collection);
119 static gint coll_button_press(GtkWidget *widget,
120 GdkEventButton *event,
121 ViewCollection *view_collection);
122 static void size_allocate(GtkWidget *w, GtkAllocation *a, gpointer data);
123 static void style_set(Collection *collection,
124 GtkStyle *style,
125 ViewCollection *view_collection);
126 static void display_free_colitem(Collection *collection,
127 CollectionItem *colitem);
128 static void lost_selection(Collection *collection,
129 guint time,
130 gpointer user_data);
131 static void selection_changed(Collection *collection,
132 gint time,
133 gpointer user_data);
134 static void calc_size(FilerWindow *filer_window, CollectionItem *colitem,
135 int *width, int *height);
136 static void make_iter(ViewCollection *view_collection, ViewIter *iter,
137 IterFlags flags);
138 static void make_item_iter(ViewCollection *vc, ViewIter *iter, int i);
140 static void view_collection_sort(ViewIface *view);
141 static void view_collection_style_changed(ViewIface *view, int flags);
142 static void view_collection_add_items(ViewIface *view, GPtrArray *items);
143 static void view_collection_update_items(ViewIface *view, GPtrArray *items);
144 static void view_collection_delete_if(ViewIface *view,
145 gboolean (*test)(gpointer item, gpointer data),
146 gpointer data);
147 static void view_collection_clear(ViewIface *view);
148 static void view_collection_select_all(ViewIface *view);
149 static void view_collection_clear_selection(ViewIface *view);
150 static int view_collection_count_items(ViewIface *view);
151 static int view_collection_count_selected(ViewIface *view);
152 static void view_collection_show_cursor(ViewIface *view);
153 static void view_collection_get_iter(ViewIface *view,
154 ViewIter *iter, IterFlags flags);
155 static void view_collection_get_iter_at_point(ViewIface *view, ViewIter *iter,
156 GdkWindow *src, int x, int y);
157 static void view_collection_cursor_to_iter(ViewIface *view, ViewIter *iter);
158 static void view_collection_set_selected(ViewIface *view,
159 ViewIter *iter,
160 gboolean selected);
161 static gboolean view_collection_get_selected(ViewIface *view, ViewIter *iter);
162 static void view_collection_select_only(ViewIface *view, ViewIter *iter);
163 static void view_collection_set_frozen(ViewIface *view, gboolean frozen);
164 static void view_collection_wink_item(ViewIface *view, ViewIter *iter);
165 static void view_collection_autosize(ViewIface *view);
166 static gboolean view_collection_cursor_visible(ViewIface *view);
167 static void view_collection_set_base(ViewIface *view, ViewIter *iter);
168 static void view_collection_start_lasso_box(ViewIface *view,
169 GdkEventButton *event);
170 static void view_collection_extend_tip(ViewIface *view, ViewIter *iter,
171 GString *tip);
172 static gboolean view_collection_auto_scroll_callback(ViewIface *view);
174 static DirItem *iter_next(ViewIter *iter);
175 static DirItem *iter_prev(ViewIter *iter);
176 static DirItem *iter_peek(ViewIter *iter);
179 /****************************************************************
180 * EXTERNAL INTERFACE *
181 ****************************************************************/
183 GtkWidget *view_collection_new(FilerWindow *filer_window)
185 ViewCollection *view_collection;
187 view_collection = g_object_new(view_collection_get_type(), NULL);
188 view_collection->filer_window = filer_window;
190 /* Starting with GTK+-2.2.2, the vadjustment is reset after init
191 * (even though it's already set during init) to a new adjustment.
192 * Change it back:
194 gtk_viewport_set_vadjustment(GTK_VIEWPORT(view_collection),
195 view_collection->collection->vadj);
197 gtk_range_set_adjustment(GTK_RANGE(filer_window->scrollbar),
198 view_collection->collection->vadj);
200 return GTK_WIDGET(view_collection);
203 GType view_collection_get_type(void)
205 static GType type = 0;
207 if (!type)
209 static const GTypeInfo info =
211 sizeof (ViewCollectionClass),
212 NULL, /* base_init */
213 NULL, /* base_finalise */
214 view_collection_class_init,
215 NULL, /* class_finalise */
216 NULL, /* class_data */
217 sizeof(ViewCollection),
218 0, /* n_preallocs */
219 view_collection_init
221 static const GInterfaceInfo iface_info =
223 view_collection_iface_init, NULL, NULL
226 type = g_type_register_static(gtk_viewport_get_type(),
227 "ViewCollection", &info, 0);
228 g_type_add_interface_static(type, VIEW_TYPE_IFACE, &iface_info);
231 return type;
234 /****************************************************************
235 * INTERNAL FUNCTIONS *
236 ****************************************************************/
238 static void view_collection_destroy(GtkObject *view_collection)
240 VIEW_COLLECTION(view_collection)->filer_window = NULL;
243 static void view_collection_finialize(GObject *object)
245 /* ViewCollection *view_collection = (ViewCollection *) object; */
247 G_OBJECT_CLASS(parent_class)->finalize(object);
250 static void view_collection_grab_focus(GtkWidget *focus_widget)
252 ViewCollection *view_collection = VIEW_COLLECTION(focus_widget);
253 gtk_widget_grab_focus(GTK_WIDGET(view_collection->collection));
256 static void view_collection_class_init(gpointer gclass, gpointer data)
258 GObjectClass *object = (GObjectClass *) gclass;
259 GtkWidgetClass *widget = (GtkWidgetClass *) gclass;
261 parent_class = g_type_class_peek_parent(gclass);
263 widget->grab_focus = view_collection_grab_focus;
265 object->finalize = view_collection_finialize;
266 GTK_OBJECT_CLASS(object)->destroy = view_collection_destroy;
269 static void view_collection_init(GTypeInstance *object, gpointer gclass)
271 ViewCollection *view_collection = (ViewCollection *) object;
272 GtkViewport *viewport = (GtkViewport *) object;
273 GtkWidget *collection;
274 GtkAdjustment *adj;
276 collection = collection_new();
277 view_collection->collection = COLLECTION(collection);
279 adj = view_collection->collection->vadj;
280 gtk_viewport_set_vadjustment(viewport, adj);
281 gtk_viewport_set_hadjustment(viewport, NULL); /* Or Gtk will crash */
282 gtk_viewport_set_shadow_type(viewport, GTK_SHADOW_NONE);
283 gtk_container_add(GTK_CONTAINER(object), collection);
284 gtk_widget_show(collection);
285 gtk_widget_set_size_request(GTK_WIDGET(view_collection), 4, 4);
287 gtk_container_set_resize_mode(GTK_CONTAINER(viewport),
288 GTK_RESIZE_IMMEDIATE);
290 view_collection->collection->free_item = display_free_colitem;
291 view_collection->collection->draw_item = draw_item;
292 view_collection->collection->test_point = test_point;
293 view_collection->collection->cb_user_data = view_collection;
295 g_signal_connect(collection, "style_set",
296 G_CALLBACK(style_set),
297 view_collection);
299 g_signal_connect(collection, "lose_selection",
300 G_CALLBACK(lost_selection), view_collection);
301 g_signal_connect(collection, "selection_changed",
302 G_CALLBACK(selection_changed), view_collection);
304 g_signal_connect(collection, "button-release-event",
305 G_CALLBACK(coll_button_release), view_collection);
306 g_signal_connect(collection, "button-press-event",
307 G_CALLBACK(coll_button_press), view_collection);
308 g_signal_connect(collection, "motion-notify-event",
309 G_CALLBACK(coll_motion_notify), view_collection);
310 g_signal_connect(viewport, "size-allocate",
311 G_CALLBACK(size_allocate), view_collection);
313 gtk_widget_set_events(collection,
314 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
315 GDK_BUTTON3_MOTION_MASK | GDK_POINTER_MOTION_MASK |
316 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
320 static void draw_item(GtkWidget *widget,
321 CollectionItem *colitem,
322 GdkRectangle *area,
323 gpointer user_data)
325 DirItem *item = (DirItem *) colitem->data;
326 gboolean selected = colitem->selected;
327 Template template;
328 ViewData *view = (ViewData *) colitem->view_data;
329 ViewCollection *view_collection = (ViewCollection *) user_data;
330 FilerWindow *filer_window = view_collection->filer_window;
331 GtkStateType selection_state;
333 g_return_if_fail(view != NULL);
335 if (selected)
336 selection_state = filer_window->selection_state;
337 else
338 selection_state = GTK_STATE_NORMAL;
340 fill_template(area, colitem, view_collection, &template);
342 /* Set up GC for coloured file types */
343 if (!type_gc)
344 type_gc = gdk_gc_new(widget->window);
346 gdk_gc_set_foreground(type_gc, type_get_colour(item,
347 &widget->style->fg[GTK_STATE_NORMAL]));
349 if (template.icon.width <= SMALL_WIDTH &&
350 template.icon.height <= SMALL_HEIGHT)
352 draw_small_icon(widget->window, &template.icon,
353 item, view->image, selected);
355 else if (template.icon.width <= ICON_WIDTH &&
356 template.icon.height <= ICON_HEIGHT)
358 draw_large_icon(widget->window, &template.icon,
359 item, view->image, selected);
361 else
363 draw_huge_icon(widget->window, &template.icon,
364 item, view->image, selected);
367 draw_string(widget, view->layout,
368 &template.leafname,
369 view->name_width,
370 selection_state,
371 TRUE);
372 if (view->details)
373 draw_string(widget, view->details,
374 &template.details,
375 template.details.width,
376 selection_state,
377 TRUE);
380 /* A template contains the locations of the three rectangles (for the icon,
381 * name and extra details).
382 * Fill in the empty 'template' with the rectanges for this item.
384 static void fill_template(GdkRectangle *area, CollectionItem *colitem,
385 ViewCollection *view_collection, Template *template)
387 DisplayStyle style = view_collection->filer_window->display_style;
388 ViewData *view = (ViewData *) colitem->view_data;
390 if (view->details)
392 template->details.width = view->details_width;
393 template->details.height = view->details_height;
395 if (style == SMALL_ICONS)
396 small_full_template(area, colitem,
397 view_collection, template);
398 else if (style == LARGE_ICONS)
399 large_full_template(area, colitem,
400 view_collection, template);
401 else
402 huge_full_template(area, colitem,
403 view_collection, template);
405 else
407 if (style == HUGE_ICONS)
408 huge_template(area, colitem,
409 view_collection, template);
410 else if (style == LARGE_ICONS)
411 large_template(area, colitem,
412 view_collection, template);
413 else
414 small_template(area, colitem,
415 view_collection, template);
419 static void huge_template(GdkRectangle *area, CollectionItem *colitem,
420 ViewCollection *view_collection, Template *template)
422 int col_width = view_collection->collection->item_width;
423 int text_x, text_y;
424 ViewData *view = (ViewData *) colitem->view_data;
425 MaskedPixmap *image = view->image;
427 if (image)
429 if (!image->huge_pixbuf)
430 pixmap_make_huge(image);
431 template->icon.width = image->huge_width;
432 template->icon.height = image->huge_height;
434 else
436 template->icon.width = HUGE_WIDTH * 3 / 2;
437 template->icon.height = HUGE_HEIGHT;
440 template->leafname.width = view->name_width;
441 template->leafname.height = view->name_height;
443 text_x = area->x + ((col_width - template->leafname.width) >> 1);
444 text_y = area->y + area->height - template->leafname.height;
446 template->leafname.x = text_x;
447 template->leafname.y = text_y;
449 template->icon.x = area->x + ((col_width - template->icon.width) >> 1);
450 template->icon.y = template->leafname.y - template->icon.height - 2;
453 static void large_template(GdkRectangle *area, CollectionItem *colitem,
454 ViewCollection *view_collection, Template *template)
456 int col_width = view_collection->collection->item_width;
457 int iwidth, iheight;
458 int image_x;
459 int image_y;
460 ViewData *view = (ViewData *) colitem->view_data;
461 MaskedPixmap *image = view->image;
463 int text_x, text_y;
465 if (image)
467 iwidth = MIN(image->width, ICON_WIDTH);
468 iheight = MIN(image->height + 6, ICON_HEIGHT);
470 else
472 iwidth = ICON_WIDTH;
473 iheight = ICON_HEIGHT;
475 image_x = area->x + ((col_width - iwidth) >> 1);
477 template->leafname.width = view->name_width;
478 template->leafname.height = view->name_height;
480 text_x = area->x + ((col_width - template->leafname.width) >> 1);
481 text_y = area->y + ICON_HEIGHT + 2;
483 template->leafname.x = text_x;
484 template->leafname.y = text_y;
486 image_y = text_y - iheight;
487 image_y = MAX(area->y, image_y);
489 template->icon.x = image_x;
490 template->icon.y = image_y;
491 template->icon.width = iwidth;
492 template->icon.height = MIN(ICON_HEIGHT, iheight);
495 static void small_template(GdkRectangle *area, CollectionItem *colitem,
496 ViewCollection *view_collection, Template *template)
498 int text_x = area->x + SMALL_WIDTH + 4;
499 int low_text_y;
500 int max_text_width = area->width - SMALL_WIDTH - 4;
501 ViewData *view = (ViewData *) colitem->view_data;
503 low_text_y = area->y + area->height / 2 - view->name_height / 2;
505 template->leafname.x = text_x;
506 template->leafname.y = low_text_y;
507 template->leafname.width = MIN(max_text_width, view->name_width);
508 template->leafname.height = view->name_height;
510 template->icon.x = area->x;
511 template->icon.y = area->y + 1;
512 template->icon.width = SMALL_WIDTH;
513 template->icon.height = SMALL_HEIGHT;
516 static void huge_full_template(GdkRectangle *area, CollectionItem *colitem,
517 ViewCollection *view_collection, Template *template)
519 int max_text_width = area->width - HUGE_WIDTH - 4;
520 ViewData *view = (ViewData *) colitem->view_data;
521 MaskedPixmap *image = view->image;
523 if (image)
525 if (!image->huge_pixbuf)
526 pixmap_make_huge(image);
527 template->icon.width = image->huge_width;
528 template->icon.height = image->huge_height;
530 else
532 template->icon.width = HUGE_WIDTH * 3 / 2;
533 template->icon.height = HUGE_HEIGHT;
536 template->icon.x = area->x + (HUGE_WIDTH - template->icon.width) / 2;
537 template->icon.y = area->y + (area->height - template->icon.height) / 2;
539 template->leafname.x = area->x + HUGE_WIDTH + 4;
540 template->leafname.y = area->y + area->height / 2
541 - (view->name_height + 2 + view->details_height) / 2;
542 template->leafname.width = MIN(max_text_width, view->name_width);
543 template->leafname.height = view->name_height;
545 if (!image)
546 return; /* Not scanned yet */
548 template->details.x = template->leafname.x;
549 template->details.y = template->leafname.y + view->name_height + 2;
552 static void large_full_template(GdkRectangle *area, CollectionItem *colitem,
553 ViewCollection *view_collection, Template *template)
555 int max_text_width = area->width - ICON_WIDTH - 4;
556 ViewData *view = (ViewData *) colitem->view_data;
557 MaskedPixmap *image = view->image;
559 if (image)
561 template->icon.width = image->width;
562 template->icon.height = image->height;
564 else
566 template->icon.width = ICON_WIDTH;
567 template->icon.height = ICON_HEIGHT;
570 template->icon.x = area->x + (ICON_WIDTH - template->icon.width) / 2;
571 template->icon.y = area->y + (area->height - template->icon.height) / 2;
574 template->leafname.x = area->x + ICON_WIDTH + 4;
575 template->leafname.y = area->y + area->height / 2
576 - (view->name_height + 2 + view->details_height) / 2;
577 template->leafname.width = MIN(max_text_width, view->name_width);
578 template->leafname.height = view->name_height;
580 if (!image)
581 return; /* Not scanned yet */
583 template->details.x = template->leafname.x;
584 template->details.y = template->leafname.y + view->name_height + 2;
587 static void small_full_template(GdkRectangle *area, CollectionItem *colitem,
588 ViewCollection *view_collection, Template *template)
590 int col_width = view_collection->collection->item_width;
591 ViewData *view = (ViewData *) colitem->view_data;
593 small_template(area, colitem, view_collection, template);
595 if (!view->image)
596 return; /* Not scanned yet */
598 template->details.x = area->x + col_width - template->details.width;
599 template->details.y = area->y + area->height / 2 - \
600 view->details_height / 2;
603 #define INSIDE(px, py, area) \
604 (px >= area.x && py >= area.y && \
605 px <= area.x + area.width && py <= area.y + area.height)
607 static gboolean test_point(Collection *collection,
608 int point_x, int point_y,
609 CollectionItem *colitem,
610 int width, int height,
611 gpointer user_data)
613 Template template;
614 GdkRectangle area;
615 ViewData *view = (ViewData *) colitem->view_data;
616 ViewCollection *view_collection = (ViewCollection *) user_data;
618 area.x = 0;
619 area.y = 0;
620 area.width = width;
621 area.height = height;
623 fill_template(&area, colitem, view_collection, &template);
625 return INSIDE(point_x, point_y, template.leafname) ||
626 INSIDE(point_x, point_y, template.icon) ||
627 (view->details && INSIDE(point_x, point_y, template.details));
630 /* 'box' renders a background box if the string is also selected */
631 static void draw_string(GtkWidget *widget,
632 PangoLayout *layout,
633 GdkRectangle *area, /* Area available on screen */
634 int width, /* Width of the full string */
635 GtkStateType selection_state,
636 gboolean box)
638 GdkGC *gc = selection_state == GTK_STATE_NORMAL
639 ? type_gc
640 : widget->style->fg_gc[selection_state];
642 if (selection_state != GTK_STATE_NORMAL && box)
643 gtk_paint_flat_box(widget->style, widget->window,
644 selection_state, GTK_SHADOW_NONE,
645 NULL, widget, "text",
646 area->x, area->y,
647 MIN(width, area->width),
648 area->height);
650 if (width > area->width)
652 gdk_gc_set_clip_origin(gc, 0, 0);
653 gdk_gc_set_clip_rectangle(gc, area);
656 gdk_draw_layout(widget->window, gc, area->x, area->y, layout);
658 if (width > area->width)
660 static GdkGC *red_gc = NULL;
662 if (!red_gc)
664 gboolean success;
665 GdkColor red = {0, 0xffff, 0, 0};
667 red_gc = gdk_gc_new(widget->window);
668 gdk_colormap_alloc_colors(
669 gtk_widget_get_colormap(widget),
670 &red, 1, FALSE, TRUE, &success);
671 gdk_gc_set_foreground(red_gc, &red);
673 gdk_draw_rectangle(widget->window, red_gc, TRUE,
674 area->x + area->width - 1, area->y,
675 1, area->height);
676 gdk_gc_set_clip_rectangle(gc, NULL);
680 /* Create the handers for the View interface */
681 static void view_collection_iface_init(gpointer giface, gpointer iface_data)
683 ViewIfaceClass *iface = giface;
685 g_assert(G_TYPE_FROM_INTERFACE(iface) == VIEW_TYPE_IFACE);
687 /* override stuff */
688 iface->sort = view_collection_sort;
689 iface->style_changed = view_collection_style_changed;
690 iface->add_items = view_collection_add_items;
691 iface->update_items = view_collection_update_items;
692 iface->delete_if = view_collection_delete_if;
693 iface->clear = view_collection_clear;
694 iface->select_all = view_collection_select_all;
695 iface->clear_selection = view_collection_clear_selection;
696 iface->count_items = view_collection_count_items;
697 iface->count_selected = view_collection_count_selected;
698 iface->show_cursor = view_collection_show_cursor;
699 iface->get_iter = view_collection_get_iter;
700 iface->get_iter_at_point = view_collection_get_iter_at_point;
701 iface->cursor_to_iter = view_collection_cursor_to_iter;
702 iface->set_selected = view_collection_set_selected;
703 iface->get_selected = view_collection_get_selected;
704 iface->set_frozen = view_collection_set_frozen;
705 iface->select_only = view_collection_select_only;
706 iface->wink_item = view_collection_wink_item;
707 iface->autosize = view_collection_autosize;
708 iface->cursor_visible = view_collection_cursor_visible;
709 iface->set_base = view_collection_set_base;
710 iface->start_lasso_box = view_collection_start_lasso_box;
711 iface->extend_tip = view_collection_extend_tip;
712 iface->auto_scroll_callback = view_collection_auto_scroll_callback;
715 static void view_collection_extend_tip(ViewIface *view, ViewIter *iter,
716 GString *tip)
718 ViewCollection*view_collection = (ViewCollection *) view;
719 Collection *collection = view_collection->collection;
720 FilerWindow *filer_window = view_collection->filer_window;
721 Template template;
722 int i = iter->i;
723 CollectionItem *colitem = &collection->items[i];
724 int col = i % collection->columns;
725 int row = i / collection->columns;
726 ViewData *view_data = (ViewData *) colitem->view_data;
727 GdkRectangle area;
729 g_return_if_fail(iter->view == (ViewIface *) view_collection);
730 g_return_if_fail(i >= 0 && i < collection->number_of_items);
732 /* TODO: What if the window is narrower than 1 column? */
733 if (filer_window->display_style == LARGE_ICONS ||
734 filer_window->display_style == HUGE_ICONS)
735 return; /* These wrap rather than truncate */
737 area.x = col * collection->item_width;
738 area.y = row * collection->item_height;
739 area.height = collection->item_height;
741 if (col == collection->columns - 1)
742 area.width = GTK_WIDGET(collection)->allocation.width - area.x;
743 else
744 area.width = collection->item_width;
746 fill_template(&area, colitem, view_collection, &template);
748 if (template.leafname.width < view_data->name_width)
750 DirItem *item = (DirItem *) collection->items[i].data;
752 g_string_append(tip, item->leafname);
753 g_string_append_c(tip, '\n');
757 static gint coll_motion_notify(GtkWidget *widget,
758 GdkEventMotion *event,
759 ViewCollection *view_collection)
761 return filer_motion_notify(view_collection->filer_window, event);
764 /* Viewport is to be resized, so calculate increments */
765 static void size_allocate(GtkWidget *w, GtkAllocation *a, gpointer data)
767 Collection *col = ((ViewCollection *) data)->collection;
769 col->vadj->step_increment = col->item_height;
770 col->vadj->page_increment = col->vadj->page_size;
773 static gint coll_button_release(GtkWidget *widget,
774 GdkEventButton *event,
775 ViewCollection *view_collection)
777 if (dnd_motion_release(event))
779 if (motion_buttons_pressed == 0 &&
780 view_collection->collection->lasso_box)
782 filer_set_autoscroll(view_collection->filer_window,
783 FALSE);
784 collection_end_lasso(view_collection->collection,
785 event->button == 1 ? GDK_SET : GDK_INVERT);
787 return FALSE;
790 filer_perform_action(view_collection->filer_window, event);
792 return FALSE;
795 static gint coll_button_press(GtkWidget *widget,
796 GdkEventButton *event,
797 ViewCollection *view_collection)
799 collection_set_cursor_item(view_collection->collection, -1, TRUE);
801 if (dnd_motion_press(widget, event))
802 filer_perform_action(view_collection->filer_window, event);
804 return FALSE;
807 /* Nothing is selected anymore - give up primary */
808 static void lost_selection(Collection *collection,
809 guint time,
810 gpointer user_data)
812 ViewCollection *view_collection = VIEW_COLLECTION(user_data);
814 filer_lost_selection(view_collection->filer_window, time);
817 static void selection_changed(Collection *collection,
818 gint time,
819 gpointer user_data)
821 ViewCollection *view_collection = VIEW_COLLECTION(user_data);
823 filer_selection_changed(view_collection->filer_window, time);
826 static void display_free_colitem(Collection *collection,
827 CollectionItem *colitem)
829 ViewData *view = (ViewData *) colitem->view_data;
831 if (!view)
832 return;
834 if (view->layout)
836 g_object_unref(G_OBJECT(view->layout));
837 view->layout = NULL;
839 if (view->details)
840 g_object_unref(G_OBJECT(view->details));
842 if (view->image)
843 g_object_unref(view->image);
845 g_free(view);
848 static void add_item(ViewCollection *view_collection, DirItem *item)
850 Collection *collection = view_collection->collection;
851 FilerWindow *filer_window = view_collection->filer_window;
852 int old_w = collection->item_width;
853 int old_h = collection->item_height;
854 int w, h, i;
856 i = collection_insert(collection, item,
857 display_create_viewdata(filer_window, item));
859 calc_size(filer_window, &collection->items[i], &w, &h);
861 if (w > old_w || h > old_h)
862 collection_set_item_size(collection,
863 MAX(old_w, w),
864 MAX(old_h, h));
867 static void style_set(Collection *collection,
868 GtkStyle *style,
869 ViewCollection *view_collection)
871 view_collection_style_changed(VIEW(view_collection),
872 VIEW_UPDATE_VIEWDATA | VIEW_UPDATE_NAME);
875 /* Return the size needed for this item */
876 static void calc_size(FilerWindow *filer_window, CollectionItem *colitem,
877 int *width, int *height)
879 int pix_width, pix_height;
880 int w;
881 DisplayStyle style = filer_window->display_style;
882 ViewData *view = (ViewData *) colitem->view_data;
884 if (filer_window->details_type == DETAILS_NONE)
886 if (style == HUGE_ICONS)
888 if (view->image)
890 if (!view->image->huge_pixbuf)
891 pixmap_make_huge(view->image);
892 pix_width = view->image->huge_width;
893 pix_height = view->image->huge_height;
895 else
897 pix_width = HUGE_WIDTH * 3 / 2;
898 pix_height = HUGE_HEIGHT * 3 / 2;
900 *width = MAX(pix_width, view->name_width) + 4;
901 *height = MAX(view->name_height + pix_height + 4,
902 HUGE_HEIGHT * 3 / 4);
904 else if (style == SMALL_ICONS)
906 w = MIN(view->name_width, o_small_width.int_value);
907 *width = SMALL_WIDTH + 12 + w;
908 *height = MAX(view->name_height, SMALL_HEIGHT) + 4;
910 else
912 if (view->image)
913 pix_width = view->image->width;
914 else
915 pix_width = ICON_WIDTH;
916 *width = MAX(pix_width, view->name_width) + 4;
917 *height = view->name_height + ICON_HEIGHT + 2;
920 else
922 w = view->details_width;
923 if (style == HUGE_ICONS)
925 *width = HUGE_WIDTH + 12 + MAX(w, view->name_width);
926 *height = HUGE_HEIGHT - 4;
928 else if (style == SMALL_ICONS)
930 int text_height;
932 *width = SMALL_WIDTH + view->name_width + 12 + w;
933 text_height = MAX(view->name_height,
934 view->details_height);
935 *height = MAX(text_height, SMALL_HEIGHT) + 4;
937 else
939 *width = ICON_WIDTH + 12 + MAX(w, view->name_width);
940 *height = ICON_HEIGHT;
945 static void update_item(ViewCollection *view_collection, int i)
947 Collection *collection = view_collection->collection;
948 int old_w = collection->item_width;
949 int old_h = collection->item_height;
950 int w, h;
951 CollectionItem *colitem;
952 FilerWindow *filer_window = view_collection->filer_window;
954 g_return_if_fail(i >= 0 && i < collection->number_of_items);
956 colitem = &collection->items[i];
958 display_update_view(filer_window,
959 (DirItem *) colitem->data,
960 (ViewData *) colitem->view_data,
961 FALSE);
963 calc_size(filer_window, colitem, &w, &h);
964 if (w > old_w || h > old_h)
965 collection_set_item_size(collection,
966 MAX(old_w, w),
967 MAX(old_h, h));
969 collection_draw_item(collection, i, TRUE);
972 /* Implementations of the View interface. See view_iface.c for comments. */
974 static void view_collection_style_changed(ViewIface *view, int flags)
976 ViewCollection *view_collection = VIEW_COLLECTION(view);
977 FilerWindow *filer_window = view_collection->filer_window;
978 int i;
979 Collection *col = view_collection->collection;
980 int width = MIN_ITEM_WIDTH;
981 int height = SMALL_HEIGHT;
982 int n = col->number_of_items;
984 if (n == 0 && filer_window->display_style != SMALL_ICONS)
985 height = ICON_HEIGHT;
987 /* Recalculate all the ViewData structs for this window
988 * (needed if the text or image has changed in any way) and
989 * get the size of each item.
991 for (i = 0; i < n; i++)
993 CollectionItem *ci = &col->items[i];
994 int w, h;
996 if (flags & (VIEW_UPDATE_VIEWDATA | VIEW_UPDATE_NAME))
997 display_update_view(filer_window,
998 (DirItem *) ci->data,
999 (ViewData *) ci->view_data,
1000 (flags & VIEW_UPDATE_NAME) != 0);
1002 calc_size(filer_window, ci, &w, &h);
1003 if (w > width)
1004 width = w;
1005 if (h > height)
1006 height = h;
1009 collection_set_item_size(col, width, height);
1011 gtk_widget_queue_draw(GTK_WIDGET(view_collection));
1014 typedef int (*SortFn)(gconstpointer a, gconstpointer b);
1016 static SortFn sort_fn(FilerWindow *fw)
1018 switch (fw->sort_type)
1020 case SORT_NAME: return sort_by_name;
1021 case SORT_TYPE: return sort_by_type;
1022 case SORT_DATE: return sort_by_date;
1023 case SORT_SIZE: return sort_by_size;
1024 case SORT_OWNER: return sort_by_owner;
1025 case SORT_GROUP: return sort_by_group;
1026 default:
1027 g_assert_not_reached();
1030 return NULL;
1033 static void view_collection_sort(ViewIface *view)
1035 ViewCollection *view_collection = VIEW_COLLECTION(view);
1036 FilerWindow *filer_window = view_collection->filer_window;
1038 collection_qsort(view_collection->collection, sort_fn(filer_window),
1039 filer_window->sort_order);
1042 static void view_collection_add_items(ViewIface *view, GPtrArray *items)
1044 ViewCollection *view_collection = VIEW_COLLECTION(view);
1045 Collection *collection = view_collection->collection;
1046 FilerWindow *filer_window = view_collection->filer_window;
1047 int old_num, i;
1049 old_num = collection->number_of_items;
1050 for (i = 0; i < items->len; i++)
1052 DirItem *item = (DirItem *) items->pdata[i];
1053 char *leafname = item->leafname;
1055 if (!filer_match_filter(filer_window, leafname))
1056 continue;
1058 add_item(view_collection, item);
1061 if (old_num != collection->number_of_items)
1062 view_collection_sort(view);
1065 static void view_collection_update_items(ViewIface *view, GPtrArray *items)
1067 ViewCollection *view_collection = VIEW_COLLECTION(view);
1068 Collection *collection = view_collection->collection;
1069 FilerWindow *filer_window = view_collection->filer_window;
1070 int i;
1072 g_return_if_fail(items->len > 0);
1074 /* The item data has already been modified, so this gives the
1075 * final sort order...
1077 collection_qsort(collection, sort_fn(filer_window),
1078 filer_window->sort_order);
1080 for (i = 0; i < items->len; i++)
1082 DirItem *item = (DirItem *) items->pdata[i];
1083 const gchar *leafname = item->leafname;
1084 int j;
1086 if (!filer_match_filter(filer_window, leafname))
1087 continue;
1089 j = collection_find_item(collection, item,
1090 sort_fn(filer_window),
1091 filer_window->sort_order);
1093 if (j < 0)
1094 g_warning("Failed to find '%s'\n", leafname);
1095 else
1096 update_item(view_collection, j);
1100 static void view_collection_delete_if(ViewIface *view,
1101 gboolean (*test)(gpointer item, gpointer data),
1102 gpointer data)
1104 ViewCollection *view_collection = VIEW_COLLECTION(view);
1105 Collection *collection = view_collection->collection;
1107 collection_delete_if(collection, test, data);
1110 static void view_collection_clear(ViewIface *view)
1112 ViewCollection *view_collection = VIEW_COLLECTION(view);
1113 Collection *collection = view_collection->collection;
1115 collection_clear(collection);
1118 static void view_collection_select_all(ViewIface *view)
1120 ViewCollection *view_collection = VIEW_COLLECTION(view);
1121 Collection *collection = view_collection->collection;
1123 collection_select_all(collection);
1126 static void view_collection_clear_selection(ViewIface *view)
1128 ViewCollection *view_collection = VIEW_COLLECTION(view);
1129 Collection *collection = view_collection->collection;
1131 collection_clear_selection(collection);
1134 static int view_collection_count_items(ViewIface *view)
1136 ViewCollection *view_collection = VIEW_COLLECTION(view);
1137 Collection *collection = view_collection->collection;
1139 return collection->number_of_items;
1142 static int view_collection_count_selected(ViewIface *view)
1144 ViewCollection *view_collection = VIEW_COLLECTION(view);
1145 Collection *collection = view_collection->collection;
1147 return collection->number_selected;
1150 static void view_collection_show_cursor(ViewIface *view)
1152 ViewCollection *view_collection = VIEW_COLLECTION(view);
1153 Collection *collection = view_collection->collection;
1155 collection_move_cursor(collection, 0, 0);
1158 /* The first time the next() method is used, this is called */
1159 static DirItem *iter_init(ViewIter *iter)
1161 ViewCollection *view_collection = (ViewCollection *) iter->view;
1162 Collection *collection = view_collection->collection;
1163 int i = -1;
1164 int n = collection->number_of_items;
1165 int flags = iter->flags;
1167 iter->peek = iter_peek;
1169 if (iter->n_remaining == 0)
1170 return NULL;
1172 if (flags & VIEW_ITER_FROM_CURSOR)
1174 i = collection->cursor_item;
1175 if (i == -1)
1176 return NULL; /* No cursor */
1178 else if (flags & VIEW_ITER_FROM_BASE)
1179 i = view_collection->cursor_base;
1181 if (i < 0 || i >= n)
1183 /* Either a normal iteration, or an iteration from an
1184 * invalid starting point.
1186 if (flags & VIEW_ITER_BACKWARDS)
1187 i = n - 1;
1188 else
1189 i = 0;
1192 if (i < 0 || i >= n)
1193 return NULL; /* No items at all! */
1195 iter->next = flags & VIEW_ITER_BACKWARDS ? iter_prev : iter_next;
1196 iter->n_remaining--;
1197 iter->i = i;
1199 if (flags & VIEW_ITER_SELECTED && !collection->items[i].selected)
1200 return iter->next(iter);
1201 return iter->peek(iter);
1203 /* Advance iter to point to the next item and return the new item
1204 * (this saves you calling peek after next each time).
1206 static DirItem *iter_next(ViewIter *iter)
1208 Collection *collection = ((ViewCollection *) iter->view)->collection;
1209 int n = collection->number_of_items;
1210 int i = iter->i;
1212 g_return_val_if_fail(iter->n_remaining >= 0, NULL);
1214 /* i is the last item returned (always valid) */
1216 g_return_val_if_fail(i >= 0 && i < n, NULL);
1218 while (iter->n_remaining)
1220 i++;
1221 iter->n_remaining--;
1223 if (i == n)
1224 i = 0;
1226 g_return_val_if_fail(i >= 0 && i < n, NULL);
1228 if (iter->flags & VIEW_ITER_SELECTED &&
1229 !collection->items[i].selected)
1230 continue;
1232 iter->i = i;
1233 return collection->items[i].data;
1236 iter->i = -1;
1237 return NULL;
1240 /* Like iter_next, but in the other direction */
1241 static DirItem *iter_prev(ViewIter *iter)
1243 Collection *collection = ((ViewCollection *) iter->view)->collection;
1244 int n = collection->number_of_items;
1245 int i = iter->i;
1247 g_return_val_if_fail(iter->n_remaining >= 0, NULL);
1249 /* i is the last item returned (always valid) */
1251 g_return_val_if_fail(i >= 0 && i < n, NULL);
1253 while (iter->n_remaining)
1255 i--;
1256 iter->n_remaining--;
1258 if (i == -1)
1259 i = collection->number_of_items - 1;
1261 g_return_val_if_fail(i >= 0 && i < n, NULL);
1263 if (iter->flags & VIEW_ITER_SELECTED &&
1264 !collection->items[i].selected)
1265 continue;
1267 iter->i = i;
1268 return collection->items[i].data;
1271 iter->i = -1;
1272 return NULL;
1275 static DirItem *iter_peek(ViewIter *iter)
1277 Collection *collection = ((ViewCollection *) iter->view)->collection;
1278 int i = iter->i;
1280 if (i == -1)
1281 return NULL;
1283 g_return_val_if_fail(i >= 0 && i < collection->number_of_items, NULL);
1285 return collection->items[i].data;
1288 static void make_iter(ViewCollection *view_collection, ViewIter *iter,
1289 IterFlags flags)
1291 Collection *collection = view_collection->collection;
1293 iter->view = (ViewIface *) view_collection;
1294 iter->next = iter_init;
1295 iter->peek = NULL;
1296 iter->i = -1;
1298 iter->flags = flags;
1300 if (flags & VIEW_ITER_ONE_ONLY)
1302 iter->n_remaining = 1;
1303 iter->next(iter);
1305 else
1306 iter->n_remaining = collection->number_of_items;
1309 /* Set the iterator to return 'i' on the next peek() */
1310 static void make_item_iter(ViewCollection *view_collection,
1311 ViewIter *iter, int i)
1313 Collection *collection = view_collection->collection;
1315 g_return_if_fail(i >= -1 && i < collection->number_of_items);
1317 make_iter(view_collection, iter, 0);
1319 iter->i = i;
1320 iter->next = iter_next;
1321 iter->peek = iter_peek;
1322 iter->n_remaining = 0;
1325 static void view_collection_get_iter(ViewIface *view,
1326 ViewIter *iter, IterFlags flags)
1328 ViewCollection *view_collection = VIEW_COLLECTION(view);
1330 make_iter(view_collection, iter, flags);
1333 static void view_collection_get_iter_at_point(ViewIface *view, ViewIter *iter,
1334 GdkWindow *src, int x, int y)
1336 ViewCollection *view_collection = VIEW_COLLECTION(view);
1337 Collection *collection = view_collection->collection;
1338 int i;
1340 if (src == ((GtkWidget *) view)->window)
1342 /* The event is on the Viewport, not the collection... */
1343 y += collection->vadj->value;
1346 i = collection_get_item(collection, x, y);
1347 make_item_iter(view_collection, iter, i);
1350 static void view_collection_cursor_to_iter(ViewIface *view, ViewIter *iter)
1352 ViewCollection *view_collection = VIEW_COLLECTION(view);
1353 Collection *collection = view_collection->collection;
1354 FilerWindow *filer_window = view_collection->filer_window;
1356 g_return_if_fail(iter == NULL ||
1357 iter->view == (ViewIface *) view_collection);
1359 collection_set_cursor_item(collection, iter ? iter->i : -1,
1360 filer_window->auto_scroll == -1);
1363 static void view_collection_set_selected(ViewIface *view,
1364 ViewIter *iter,
1365 gboolean selected)
1367 ViewCollection *view_collection = VIEW_COLLECTION(view);
1368 Collection *collection = view_collection->collection;
1370 g_return_if_fail(iter != NULL &&
1371 iter->view == (ViewIface *) view_collection);
1372 g_return_if_fail(iter->i >= 0 && iter->i < collection->number_of_items);
1374 if (selected)
1375 collection_select_item(collection, iter->i);
1376 else
1377 collection_unselect_item(collection, iter->i);
1380 static gboolean view_collection_get_selected(ViewIface *view, ViewIter *iter)
1382 ViewCollection *view_collection = VIEW_COLLECTION(view);
1383 Collection *collection = view_collection->collection;
1385 g_return_val_if_fail(iter != NULL &&
1386 iter->view == (ViewIface *) view_collection, FALSE);
1387 g_return_val_if_fail(iter->i >= 0 &&
1388 iter->i < collection->number_of_items, FALSE);
1390 return collection->items[iter->i].selected;
1393 static void view_collection_select_only(ViewIface *view, ViewIter *iter)
1395 ViewCollection *view_collection = VIEW_COLLECTION(view);
1396 Collection *collection = view_collection->collection;
1398 g_return_if_fail(iter != NULL &&
1399 iter->view == (ViewIface *) view_collection);
1400 g_return_if_fail(iter->i >= 0 && iter->i < collection->number_of_items);
1402 collection_clear_except(collection, iter->i);
1405 static void view_collection_set_frozen(ViewIface *view, gboolean frozen)
1407 ViewCollection *view_collection = VIEW_COLLECTION(view);
1408 Collection *collection = view_collection->collection;
1410 if (frozen)
1411 collection->block_selection_changed++;
1412 else
1413 collection_unblock_selection_changed(collection,
1414 gtk_get_current_event_time(), TRUE);
1417 static void view_collection_wink_item(ViewIface *view, ViewIter *iter)
1419 ViewCollection *view_collection = VIEW_COLLECTION(view);
1420 Collection *collection = view_collection->collection;
1422 if (!iter)
1424 collection_wink_item(collection, -1);
1425 return;
1428 g_return_if_fail(iter != NULL &&
1429 iter->view == (ViewIface *) view_collection);
1430 g_return_if_fail(iter->i >= 0 && iter->i < collection->number_of_items);
1432 collection_wink_item(collection, iter->i);
1435 static void view_collection_autosize(ViewIface *view)
1437 ViewCollection *view_collection = VIEW_COLLECTION(view);
1438 FilerWindow *filer_window = view_collection->filer_window;
1439 Collection *collection = view_collection->collection;
1440 int n;
1441 int w = collection->item_width;
1442 int h = collection->item_height;
1443 int x;
1444 int rows, cols;
1445 int max_x, max_rows;
1446 const float r = 2.5;
1447 int t = 0;
1448 int space = 0;
1450 /* Get the extra height required for the toolbar and minibuffer,
1451 * if visible.
1453 if (o_toolbar.int_value != TOOLBAR_NONE)
1454 t = filer_window->toolbar->allocation.height;
1455 if (filer_window->message)
1456 t += filer_window->message->allocation.height;
1457 if (GTK_WIDGET_VISIBLE(filer_window->minibuffer_area))
1459 GtkRequisition req;
1461 gtk_widget_size_request(filer_window->minibuffer_area, &req);
1462 space = req.height + 2;
1463 t += space;
1466 n = collection->number_of_items;
1467 if (n == 0)
1468 h = ICON_HEIGHT * 1.5;
1469 n = MAX(n, 2);
1471 max_x = (o_filer_size_limit.int_value * monitor_width) / 100;
1472 max_rows = (o_filer_size_limit.int_value * monitor_height) / (h * 100);
1474 /* Aim for a size where
1475 * x = r(y + t + h), (1)
1476 * unless that's too wide.
1478 * t = toolbar (and minibuffer) height
1479 * r = desired (width / height) ratio
1481 * Want to display all items:
1482 * (x/w)(y/h) = n
1483 * => xy = nwh
1484 * => x(x/r - t - h) = nwh (from 1)
1485 * => xx - x.rt - hr(1 - nw) = 0
1486 * => 2x = rt +/- sqrt(rt.rt + 4hr(nw - 1))
1487 * Now,
1488 * 4hr(nw - 1) > 0
1489 * so
1490 * sqrt(rt.rt + ...) > rt
1492 * So, the +/- must be +:
1494 * => x = (rt + sqrt(rt.rt + 4hr(nw - 1))) / 2
1496 * ( + w - 1 to round up)
1498 x = (r * t + sqrt(r*r*t*t + 4*h*r * (n*w - 1))) / 2 + w - 1;
1500 /* Limit x */
1501 if (x > max_x)
1502 x = max_x;
1504 cols = x / w;
1505 cols = MAX(cols, 1);
1507 /* Choose rows to display all items given our chosen x.
1508 * Don't make the window *too* big!
1510 rows = (n + cols - 1) / cols;
1511 if (rows > max_rows)
1512 rows = max_rows;
1514 /* Leave some room for extra icons, but only in Small Icons mode
1515 * otherwise it takes up too much space.
1516 * Also, don't add space if the minibuffer is open.
1518 if (space == 0)
1519 space = filer_window->display_style == SMALL_ICONS ? h : 2;
1521 filer_window_set_size(filer_window,
1522 w * MAX(cols, 1),
1523 h * MAX(rows, 1) + space);
1526 static gboolean view_collection_cursor_visible(ViewIface *view)
1528 ViewCollection *view_collection = VIEW_COLLECTION(view);
1529 Collection *collection = view_collection->collection;
1531 return collection->cursor_item != -1;
1534 static void view_collection_set_base(ViewIface *view, ViewIter *iter)
1536 ViewCollection *view_collection = VIEW_COLLECTION(view);
1538 view_collection->cursor_base = iter->i;
1541 static void view_collection_start_lasso_box(ViewIface *view,
1542 GdkEventButton *event)
1544 ViewCollection *view_collection = (ViewCollection *) view;
1545 Collection *collection = view_collection->collection;
1547 filer_set_autoscroll(view_collection->filer_window, TRUE);
1548 collection_lasso_box(collection, event->x, event->y);
1552 /* Change the adjustment by this amount. Bounded. */
1553 static void diff_vpos(Collection *collection, int diff)
1555 int old = collection->vadj->value;
1556 int value = old + diff;
1558 value = CLAMP(value, 0,
1559 collection->vadj->upper - collection->vadj->page_size);
1560 gtk_adjustment_set_value(collection->vadj, value);
1562 if (collection->vadj->value != old)
1563 dnd_spring_abort();
1566 static gboolean view_collection_auto_scroll_callback(ViewIface *view)
1568 ViewCollection *view_collection = (ViewCollection *) view;
1569 Collection *collection = view_collection->collection;
1570 GdkWindow *window = ((GtkWidget *) collection)->window;
1571 gint x, y, w, h;
1572 GdkModifierType mask;
1573 int diff = 0;
1575 gdk_window_get_pointer(window, &x, &y, &mask);
1576 gdk_drawable_get_size(window, &w, NULL);
1578 h = collection->vadj->page_size;
1579 y -= collection->vadj->value;
1581 if ((x < 0 || x > w || y < 0 || y > h) && !collection->lasso_box)
1582 return FALSE; /* Out of window - stop */
1584 if (y < AUTOSCROLL_STEP)
1585 diff = y - AUTOSCROLL_STEP;
1586 else if (y > h - AUTOSCROLL_STEP)
1587 diff = AUTOSCROLL_STEP + y - h;
1589 if (diff)
1590 diff_vpos(collection, diff);
1592 return TRUE;