r2723: Fixed focussing problems in Icons View (reported by Andrzej Radecki).
[rox-filer.git] / ROX-Filer / src / view_collection.c
blob18c50698f60fdbf74cd4b63e2543133c5d6d5a41
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 "display.h"
48 #include "filer.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 gtk_range_set_adjustment(GTK_RANGE(filer_window->scrollbar),
191 view_collection->collection->vadj);
193 return GTK_WIDGET(view_collection);
196 GType view_collection_get_type(void)
198 static GType type = 0;
200 if (!type)
202 static const GTypeInfo info =
204 sizeof (ViewCollectionClass),
205 NULL, /* base_init */
206 NULL, /* base_finalise */
207 view_collection_class_init,
208 NULL, /* class_finalise */
209 NULL, /* class_data */
210 sizeof(ViewCollection),
211 0, /* n_preallocs */
212 view_collection_init
214 static const GInterfaceInfo iface_info =
216 view_collection_iface_init, NULL, NULL
219 type = g_type_register_static(gtk_viewport_get_type(),
220 "ViewCollection", &info, 0);
221 g_type_add_interface_static(type, VIEW_TYPE_IFACE, &iface_info);
224 return type;
227 /****************************************************************
228 * INTERNAL FUNCTIONS *
229 ****************************************************************/
231 static void view_collection_destroy(GtkObject *view_collection)
233 VIEW_COLLECTION(view_collection)->filer_window = NULL;
236 static void view_collection_finialize(GObject *object)
238 /* ViewCollection *view_collection = (ViewCollection *) object; */
240 G_OBJECT_CLASS(parent_class)->finalize(object);
243 static void view_collection_grab_focus(GtkWidget *focus_widget)
245 ViewCollection *view_collection = VIEW_COLLECTION(focus_widget);
246 gtk_widget_grab_focus(GTK_WIDGET(view_collection->collection));
249 static void view_collection_class_init(gpointer gclass, gpointer data)
251 GObjectClass *object = (GObjectClass *) gclass;
252 GtkWidgetClass *widget = (GtkWidgetClass *) gclass;
254 parent_class = g_type_class_peek_parent(gclass);
256 widget->grab_focus = view_collection_grab_focus;
258 object->finalize = view_collection_finialize;
259 GTK_OBJECT_CLASS(object)->destroy = view_collection_destroy;
262 static void view_collection_init(GTypeInstance *object, gpointer gclass)
264 ViewCollection *view_collection = (ViewCollection *) object;
265 GtkViewport *viewport = (GtkViewport *) object;
266 GtkWidget *collection;
267 GtkAdjustment *adj;
269 collection = collection_new();
270 view_collection->collection = COLLECTION(collection);
272 adj = view_collection->collection->vadj;
273 gtk_viewport_set_vadjustment(viewport, adj);
274 gtk_viewport_set_hadjustment(viewport, NULL); /* Or Gtk will crash */
275 gtk_viewport_set_shadow_type(viewport, GTK_SHADOW_NONE);
276 gtk_container_add(GTK_CONTAINER(object), collection);
277 gtk_widget_show(collection);
278 gtk_widget_set_size_request(GTK_WIDGET(view_collection), 4, 4);
280 gtk_container_set_resize_mode(GTK_CONTAINER(viewport),
281 GTK_RESIZE_IMMEDIATE);
283 view_collection->collection->free_item = display_free_colitem;
284 view_collection->collection->draw_item = draw_item;
285 view_collection->collection->test_point = test_point;
286 view_collection->collection->cb_user_data = view_collection;
288 g_signal_connect(collection, "style_set",
289 G_CALLBACK(style_set),
290 view_collection);
292 g_signal_connect(collection, "lose_selection",
293 G_CALLBACK(lost_selection), view_collection);
294 g_signal_connect(collection, "selection_changed",
295 G_CALLBACK(selection_changed), view_collection);
297 g_signal_connect(collection, "button-release-event",
298 G_CALLBACK(coll_button_release), view_collection);
299 g_signal_connect(collection, "button-press-event",
300 G_CALLBACK(coll_button_press), view_collection);
301 g_signal_connect(collection, "motion-notify-event",
302 G_CALLBACK(coll_motion_notify), view_collection);
303 g_signal_connect(viewport, "size-allocate",
304 G_CALLBACK(size_allocate), view_collection);
306 gtk_widget_set_events(collection,
307 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
308 GDK_BUTTON3_MOTION_MASK | GDK_POINTER_MOTION_MASK |
309 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
313 static void draw_item(GtkWidget *widget,
314 CollectionItem *colitem,
315 GdkRectangle *area,
316 gpointer user_data)
318 DirItem *item = (DirItem *) colitem->data;
319 gboolean selected = colitem->selected;
320 Template template;
321 ViewData *view = (ViewData *) colitem->view_data;
322 ViewCollection *view_collection = (ViewCollection *) user_data;
323 FilerWindow *filer_window = view_collection->filer_window;
324 GtkStateType selection_state;
326 g_return_if_fail(view != NULL);
328 if (selected)
329 selection_state = filer_window->selection_state;
330 else
331 selection_state = GTK_STATE_NORMAL;
333 fill_template(area, colitem, view_collection, &template);
335 /* Set up GC for coloured file types */
336 if (!type_gc)
337 type_gc = gdk_gc_new(widget->window);
339 gdk_gc_set_foreground(type_gc, type_get_colour(item,
340 &widget->style->fg[GTK_STATE_NORMAL]));
342 if (template.icon.width <= SMALL_WIDTH &&
343 template.icon.height <= SMALL_HEIGHT)
345 draw_small_icon(widget->window, &template.icon,
346 item, view->image, selected);
348 else if (template.icon.width <= ICON_WIDTH &&
349 template.icon.height <= ICON_HEIGHT)
351 draw_large_icon(widget->window, &template.icon,
352 item, view->image, selected);
354 else
356 draw_huge_icon(widget->window, &template.icon,
357 item, view->image, selected);
360 draw_string(widget, view->layout,
361 &template.leafname,
362 view->name_width,
363 selection_state,
364 TRUE);
365 if (view->details)
366 draw_string(widget, view->details,
367 &template.details,
368 template.details.width,
369 selection_state,
370 TRUE);
373 /* A template contains the locations of the three rectangles (for the icon,
374 * name and extra details).
375 * Fill in the empty 'template' with the rectanges for this item.
377 static void fill_template(GdkRectangle *area, CollectionItem *colitem,
378 ViewCollection *view_collection, Template *template)
380 DisplayStyle style = view_collection->filer_window->display_style;
381 ViewData *view = (ViewData *) colitem->view_data;
383 if (view->details)
385 template->details.width = view->details_width;
386 template->details.height = view->details_height;
388 if (style == SMALL_ICONS)
389 small_full_template(area, colitem,
390 view_collection, template);
391 else if (style == LARGE_ICONS)
392 large_full_template(area, colitem,
393 view_collection, template);
394 else
395 huge_full_template(area, colitem,
396 view_collection, template);
398 else
400 if (style == HUGE_ICONS)
401 huge_template(area, colitem,
402 view_collection, template);
403 else if (style == LARGE_ICONS)
404 large_template(area, colitem,
405 view_collection, template);
406 else
407 small_template(area, colitem,
408 view_collection, template);
412 static void huge_template(GdkRectangle *area, CollectionItem *colitem,
413 ViewCollection *view_collection, Template *template)
415 int col_width = view_collection->collection->item_width;
416 int text_x, text_y;
417 ViewData *view = (ViewData *) colitem->view_data;
418 MaskedPixmap *image = view->image;
420 if (image)
422 if (!image->huge_pixbuf)
423 pixmap_make_huge(image);
424 template->icon.width = image->huge_width;
425 template->icon.height = image->huge_height;
427 else
429 template->icon.width = HUGE_WIDTH * 3 / 2;
430 template->icon.height = HUGE_HEIGHT;
433 template->leafname.width = view->name_width;
434 template->leafname.height = view->name_height;
436 text_x = area->x + ((col_width - template->leafname.width) >> 1);
437 text_y = area->y + area->height - template->leafname.height;
439 template->leafname.x = text_x;
440 template->leafname.y = text_y;
442 template->icon.x = area->x + ((col_width - template->icon.width) >> 1);
443 template->icon.y = template->leafname.y - template->icon.height - 2;
446 static void large_template(GdkRectangle *area, CollectionItem *colitem,
447 ViewCollection *view_collection, Template *template)
449 int col_width = view_collection->collection->item_width;
450 int iwidth, iheight;
451 int image_x;
452 int image_y;
453 ViewData *view = (ViewData *) colitem->view_data;
454 MaskedPixmap *image = view->image;
456 int text_x, text_y;
458 if (image)
460 iwidth = MIN(image->width, ICON_WIDTH);
461 iheight = MIN(image->height + 6, ICON_HEIGHT);
463 else
465 iwidth = ICON_WIDTH;
466 iheight = ICON_HEIGHT;
468 image_x = area->x + ((col_width - iwidth) >> 1);
470 template->leafname.width = view->name_width;
471 template->leafname.height = view->name_height;
473 text_x = area->x + ((col_width - template->leafname.width) >> 1);
474 text_y = area->y + ICON_HEIGHT + 2;
476 template->leafname.x = text_x;
477 template->leafname.y = text_y;
479 image_y = text_y - iheight;
480 image_y = MAX(area->y, image_y);
482 template->icon.x = image_x;
483 template->icon.y = image_y;
484 template->icon.width = iwidth;
485 template->icon.height = MIN(ICON_HEIGHT, iheight);
488 static void small_template(GdkRectangle *area, CollectionItem *colitem,
489 ViewCollection *view_collection, Template *template)
491 int text_x = area->x + SMALL_WIDTH + 4;
492 int low_text_y;
493 int max_text_width = area->width - SMALL_WIDTH - 4;
494 ViewData *view = (ViewData *) colitem->view_data;
496 low_text_y = area->y + area->height / 2 - view->name_height / 2;
498 template->leafname.x = text_x;
499 template->leafname.y = low_text_y;
500 template->leafname.width = MIN(max_text_width, view->name_width);
501 template->leafname.height = view->name_height;
503 template->icon.x = area->x;
504 template->icon.y = area->y + 1;
505 template->icon.width = SMALL_WIDTH;
506 template->icon.height = SMALL_HEIGHT;
509 static void huge_full_template(GdkRectangle *area, CollectionItem *colitem,
510 ViewCollection *view_collection, Template *template)
512 int max_text_width = area->width - HUGE_WIDTH - 4;
513 ViewData *view = (ViewData *) colitem->view_data;
514 MaskedPixmap *image = view->image;
516 if (image)
518 if (!image->huge_pixbuf)
519 pixmap_make_huge(image);
520 template->icon.width = image->huge_width;
521 template->icon.height = image->huge_height;
523 else
525 template->icon.width = HUGE_WIDTH * 3 / 2;
526 template->icon.height = HUGE_HEIGHT;
529 template->icon.x = area->x + (HUGE_WIDTH - template->icon.width) / 2;
530 template->icon.y = area->y + (area->height - template->icon.height) / 2;
532 template->leafname.x = area->x + HUGE_WIDTH + 4;
533 template->leafname.y = area->y + area->height / 2
534 - (view->name_height + 2 + view->details_height) / 2;
535 template->leafname.width = MIN(max_text_width, view->name_width);
536 template->leafname.height = view->name_height;
538 if (!image)
539 return; /* Not scanned yet */
541 template->details.x = template->leafname.x;
542 template->details.y = template->leafname.y + view->name_height + 2;
545 static void large_full_template(GdkRectangle *area, CollectionItem *colitem,
546 ViewCollection *view_collection, Template *template)
548 int max_text_width = area->width - ICON_WIDTH - 4;
549 ViewData *view = (ViewData *) colitem->view_data;
550 MaskedPixmap *image = view->image;
552 if (image)
554 template->icon.width = image->width;
555 template->icon.height = image->height;
557 else
559 template->icon.width = ICON_WIDTH;
560 template->icon.height = ICON_HEIGHT;
563 template->icon.x = area->x + (ICON_WIDTH - template->icon.width) / 2;
564 template->icon.y = area->y + (area->height - template->icon.height) / 2;
567 template->leafname.x = area->x + ICON_WIDTH + 4;
568 template->leafname.y = area->y + area->height / 2
569 - (view->name_height + 2 + view->details_height) / 2;
570 template->leafname.width = MIN(max_text_width, view->name_width);
571 template->leafname.height = view->name_height;
573 if (!image)
574 return; /* Not scanned yet */
576 template->details.x = template->leafname.x;
577 template->details.y = template->leafname.y + view->name_height + 2;
580 static void small_full_template(GdkRectangle *area, CollectionItem *colitem,
581 ViewCollection *view_collection, Template *template)
583 int col_width = view_collection->collection->item_width;
584 ViewData *view = (ViewData *) colitem->view_data;
586 small_template(area, colitem, view_collection, template);
588 if (!view->image)
589 return; /* Not scanned yet */
591 template->details.x = area->x + col_width - template->details.width;
592 template->details.y = area->y + area->height / 2 - \
593 view->details_height / 2;
596 #define INSIDE(px, py, area) \
597 (px >= area.x && py >= area.y && \
598 px <= area.x + area.width && py <= area.y + area.height)
600 static gboolean test_point(Collection *collection,
601 int point_x, int point_y,
602 CollectionItem *colitem,
603 int width, int height,
604 gpointer user_data)
606 Template template;
607 GdkRectangle area;
608 ViewData *view = (ViewData *) colitem->view_data;
609 ViewCollection *view_collection = (ViewCollection *) user_data;
611 area.x = 0;
612 area.y = 0;
613 area.width = width;
614 area.height = height;
616 fill_template(&area, colitem, view_collection, &template);
618 return INSIDE(point_x, point_y, template.leafname) ||
619 INSIDE(point_x, point_y, template.icon) ||
620 (view->details && INSIDE(point_x, point_y, template.details));
623 /* 'box' renders a background box if the string is also selected */
624 static void draw_string(GtkWidget *widget,
625 PangoLayout *layout,
626 GdkRectangle *area, /* Area available on screen */
627 int width, /* Width of the full string */
628 GtkStateType selection_state,
629 gboolean box)
631 GdkGC *gc = selection_state == GTK_STATE_NORMAL
632 ? type_gc
633 : widget->style->fg_gc[selection_state];
635 if (selection_state != GTK_STATE_NORMAL && box)
636 gtk_paint_flat_box(widget->style, widget->window,
637 selection_state, GTK_SHADOW_NONE,
638 NULL, widget, "text",
639 area->x, area->y,
640 MIN(width, area->width),
641 area->height);
643 if (width > area->width)
645 gdk_gc_set_clip_origin(gc, 0, 0);
646 gdk_gc_set_clip_rectangle(gc, area);
649 gdk_draw_layout(widget->window, gc, area->x, area->y, layout);
651 if (width > area->width)
653 static GdkGC *red_gc = NULL;
655 if (!red_gc)
657 gboolean success;
658 GdkColor red = {0, 0xffff, 0, 0};
660 red_gc = gdk_gc_new(widget->window);
661 gdk_colormap_alloc_colors(
662 gtk_widget_get_colormap(widget),
663 &red, 1, FALSE, TRUE, &success);
664 gdk_gc_set_foreground(red_gc, &red);
666 gdk_draw_rectangle(widget->window, red_gc, TRUE,
667 area->x + area->width - 1, area->y,
668 1, area->height);
669 gdk_gc_set_clip_rectangle(gc, NULL);
673 /* Create the handers for the View interface */
674 static void view_collection_iface_init(gpointer giface, gpointer iface_data)
676 ViewIfaceClass *iface = giface;
678 g_assert(G_TYPE_FROM_INTERFACE(iface) == VIEW_TYPE_IFACE);
680 /* override stuff */
681 iface->sort = view_collection_sort;
682 iface->style_changed = view_collection_style_changed;
683 iface->add_items = view_collection_add_items;
684 iface->update_items = view_collection_update_items;
685 iface->delete_if = view_collection_delete_if;
686 iface->clear = view_collection_clear;
687 iface->select_all = view_collection_select_all;
688 iface->clear_selection = view_collection_clear_selection;
689 iface->count_items = view_collection_count_items;
690 iface->count_selected = view_collection_count_selected;
691 iface->show_cursor = view_collection_show_cursor;
692 iface->get_iter = view_collection_get_iter;
693 iface->get_iter_at_point = view_collection_get_iter_at_point;
694 iface->cursor_to_iter = view_collection_cursor_to_iter;
695 iface->set_selected = view_collection_set_selected;
696 iface->get_selected = view_collection_get_selected;
697 iface->set_frozen = view_collection_set_frozen;
698 iface->select_only = view_collection_select_only;
699 iface->wink_item = view_collection_wink_item;
700 iface->autosize = view_collection_autosize;
701 iface->cursor_visible = view_collection_cursor_visible;
702 iface->set_base = view_collection_set_base;
703 iface->start_lasso_box = view_collection_start_lasso_box;
704 iface->extend_tip = view_collection_extend_tip;
705 iface->auto_scroll_callback = view_collection_auto_scroll_callback;
708 static void view_collection_extend_tip(ViewIface *view, ViewIter *iter,
709 GString *tip)
711 ViewCollection*view_collection = (ViewCollection *) view;
712 Collection *collection = view_collection->collection;
713 FilerWindow *filer_window = view_collection->filer_window;
714 Template template;
715 int i = iter->i;
716 CollectionItem *colitem = &collection->items[i];
717 int col = i % collection->columns;
718 int row = i / collection->columns;
719 ViewData *view_data = (ViewData *) colitem->view_data;
720 GdkRectangle area;
722 g_return_if_fail(iter->view == (ViewIface *) view_collection);
723 g_return_if_fail(i >= 0 && i < collection->number_of_items);
725 /* TODO: What if the window is narrower than 1 column? */
726 if (filer_window->display_style == LARGE_ICONS ||
727 filer_window->display_style == HUGE_ICONS)
728 return; /* These wrap rather than truncate */
730 area.x = col * collection->item_width;
731 area.y = row * collection->item_height;
732 area.height = collection->item_height;
734 if (col == collection->columns - 1)
735 area.width = GTK_WIDGET(collection)->allocation.width - area.x;
736 else
737 area.width = collection->item_width;
739 fill_template(&area, colitem, view_collection, &template);
741 if (template.leafname.width < view_data->name_width)
743 DirItem *item = (DirItem *) collection->items[i].data;
745 g_string_append(tip, item->leafname);
746 g_string_append_c(tip, '\n');
750 static gint coll_motion_notify(GtkWidget *widget,
751 GdkEventMotion *event,
752 ViewCollection *view_collection)
754 return filer_motion_notify(view_collection->filer_window, event);
757 /* Viewport is to be resized, so calculate increments */
758 static void size_allocate(GtkWidget *w, GtkAllocation *a, gpointer data)
760 Collection *col = ((ViewCollection *) data)->collection;
762 col->vadj->step_increment = col->item_height;
763 col->vadj->page_increment = col->vadj->page_size;
766 static gint coll_button_release(GtkWidget *widget,
767 GdkEventButton *event,
768 ViewCollection *view_collection)
770 if (dnd_motion_release(event))
772 if (motion_buttons_pressed == 0 &&
773 view_collection->collection->lasso_box)
775 filer_set_autoscroll(view_collection->filer_window,
776 FALSE);
777 collection_end_lasso(view_collection->collection,
778 event->button == 1 ? GDK_SET : GDK_INVERT);
780 return FALSE;
783 filer_perform_action(view_collection->filer_window, event);
785 return FALSE;
788 static gint coll_button_press(GtkWidget *widget,
789 GdkEventButton *event,
790 ViewCollection *view_collection)
792 collection_set_cursor_item(view_collection->collection, -1, TRUE);
794 if (dnd_motion_press(widget, event))
795 filer_perform_action(view_collection->filer_window, event);
797 return FALSE;
800 /* Nothing is selected anymore - give up primary */
801 static void lost_selection(Collection *collection,
802 guint time,
803 gpointer user_data)
805 ViewCollection *view_collection = VIEW_COLLECTION(user_data);
807 filer_lost_selection(view_collection->filer_window, time);
810 static void selection_changed(Collection *collection,
811 gint time,
812 gpointer user_data)
814 ViewCollection *view_collection = VIEW_COLLECTION(user_data);
816 filer_selection_changed(view_collection->filer_window, time);
819 static void display_free_colitem(Collection *collection,
820 CollectionItem *colitem)
822 ViewData *view = (ViewData *) colitem->view_data;
824 if (!view)
825 return;
827 if (view->layout)
829 g_object_unref(G_OBJECT(view->layout));
830 view->layout = NULL;
832 if (view->details)
833 g_object_unref(G_OBJECT(view->details));
835 if (view->image)
836 g_object_unref(view->image);
838 g_free(view);
841 static void add_item(ViewCollection *view_collection, DirItem *item)
843 Collection *collection = view_collection->collection;
844 FilerWindow *filer_window = view_collection->filer_window;
845 int old_w = collection->item_width;
846 int old_h = collection->item_height;
847 int w, h, i;
849 i = collection_insert(collection, item,
850 display_create_viewdata(filer_window, item));
852 calc_size(filer_window, &collection->items[i], &w, &h);
854 if (w > old_w || h > old_h)
855 collection_set_item_size(collection,
856 MAX(old_w, w),
857 MAX(old_h, h));
860 static void style_set(Collection *collection,
861 GtkStyle *style,
862 ViewCollection *view_collection)
864 view_collection_style_changed(VIEW(view_collection),
865 VIEW_UPDATE_VIEWDATA | VIEW_UPDATE_NAME);
868 /* Return the size needed for this item */
869 static void calc_size(FilerWindow *filer_window, CollectionItem *colitem,
870 int *width, int *height)
872 int pix_width, pix_height;
873 int w;
874 DisplayStyle style = filer_window->display_style;
875 ViewData *view = (ViewData *) colitem->view_data;
877 if (filer_window->details_type == DETAILS_NONE)
879 if (style == HUGE_ICONS)
881 if (view->image)
883 if (!view->image->huge_pixbuf)
884 pixmap_make_huge(view->image);
885 pix_width = view->image->huge_width;
886 pix_height = view->image->huge_height;
888 else
890 pix_width = HUGE_WIDTH * 3 / 2;
891 pix_height = HUGE_HEIGHT * 3 / 2;
893 *width = MAX(pix_width, view->name_width) + 4;
894 *height = MAX(view->name_height + pix_height + 4,
895 HUGE_HEIGHT * 3 / 4);
897 else if (style == SMALL_ICONS)
899 w = MIN(view->name_width, o_small_width.int_value);
900 *width = SMALL_WIDTH + 12 + w;
901 *height = MAX(view->name_height, SMALL_HEIGHT) + 4;
903 else
905 if (view->image)
906 pix_width = view->image->width;
907 else
908 pix_width = ICON_WIDTH;
909 *width = MAX(pix_width, view->name_width) + 4;
910 *height = view->name_height + ICON_HEIGHT + 2;
913 else
915 w = view->details_width;
916 if (style == HUGE_ICONS)
918 *width = HUGE_WIDTH + 12 + MAX(w, view->name_width);
919 *height = HUGE_HEIGHT - 4;
921 else if (style == SMALL_ICONS)
923 int text_height;
925 *width = SMALL_WIDTH + view->name_width + 12 + w;
926 text_height = MAX(view->name_height,
927 view->details_height);
928 *height = MAX(text_height, SMALL_HEIGHT) + 4;
930 else
932 *width = ICON_WIDTH + 12 + MAX(w, view->name_width);
933 *height = ICON_HEIGHT;
938 static void update_item(ViewCollection *view_collection, int i)
940 Collection *collection = view_collection->collection;
941 int old_w = collection->item_width;
942 int old_h = collection->item_height;
943 int w, h;
944 CollectionItem *colitem;
945 FilerWindow *filer_window = view_collection->filer_window;
947 g_return_if_fail(i >= 0 && i < collection->number_of_items);
949 colitem = &collection->items[i];
951 display_update_view(filer_window,
952 (DirItem *) colitem->data,
953 (ViewData *) colitem->view_data,
954 FALSE);
956 calc_size(filer_window, colitem, &w, &h);
957 if (w > old_w || h > old_h)
958 collection_set_item_size(collection,
959 MAX(old_w, w),
960 MAX(old_h, h));
962 collection_draw_item(collection, i, TRUE);
965 /* Implementations of the View interface. See view_iface.c for comments. */
967 static void view_collection_style_changed(ViewIface *view, int flags)
969 ViewCollection *view_collection = VIEW_COLLECTION(view);
970 FilerWindow *filer_window = view_collection->filer_window;
971 int i;
972 Collection *col = view_collection->collection;
973 int width = MIN_ITEM_WIDTH;
974 int height = SMALL_HEIGHT;
975 int n = col->number_of_items;
977 if (n == 0 && filer_window->display_style != SMALL_ICONS)
978 height = ICON_HEIGHT;
980 /* Recalculate all the ViewData structs for this window
981 * (needed if the text or image has changed in any way) and
982 * get the size of each item.
984 for (i = 0; i < n; i++)
986 CollectionItem *ci = &col->items[i];
987 int w, h;
989 if (flags & (VIEW_UPDATE_VIEWDATA | VIEW_UPDATE_NAME))
990 display_update_view(filer_window,
991 (DirItem *) ci->data,
992 (ViewData *) ci->view_data,
993 (flags & VIEW_UPDATE_NAME) != 0);
995 calc_size(filer_window, ci, &w, &h);
996 if (w > width)
997 width = w;
998 if (h > height)
999 height = h;
1002 collection_set_item_size(col, width, height);
1004 gtk_widget_queue_draw(GTK_WIDGET(view_collection));
1007 typedef int (*SortFn)(gconstpointer a, gconstpointer b);
1009 static SortFn sort_fn(FilerWindow *fw)
1011 switch (fw->sort_type)
1013 case SORT_NAME: return sort_by_name;
1014 case SORT_TYPE: return sort_by_type;
1015 case SORT_DATE: return sort_by_date;
1016 case SORT_SIZE: return sort_by_size;
1017 case SORT_OWNER: return sort_by_owner;
1018 case SORT_GROUP: return sort_by_group;
1019 default:
1020 g_assert_not_reached();
1023 return NULL;
1026 static void view_collection_sort(ViewIface *view)
1028 ViewCollection *view_collection = VIEW_COLLECTION(view);
1029 FilerWindow *filer_window = view_collection->filer_window;
1031 collection_qsort(view_collection->collection, sort_fn(filer_window),
1032 filer_window->sort_order);
1035 static void view_collection_add_items(ViewIface *view, GPtrArray *items)
1037 ViewCollection *view_collection = VIEW_COLLECTION(view);
1038 Collection *collection = view_collection->collection;
1039 FilerWindow *filer_window = view_collection->filer_window;
1040 int old_num, i;
1042 old_num = collection->number_of_items;
1043 for (i = 0; i < items->len; i++)
1045 DirItem *item = (DirItem *) items->pdata[i];
1046 char *leafname = item->leafname;
1048 if (leafname[0] == '.' && !filer_window->show_hidden)
1049 continue;
1051 add_item(view_collection, item);
1054 if (old_num != collection->number_of_items)
1055 view_collection_sort(view);
1058 static void view_collection_update_items(ViewIface *view, GPtrArray *items)
1060 ViewCollection *view_collection = VIEW_COLLECTION(view);
1061 Collection *collection = view_collection->collection;
1062 FilerWindow *filer_window = view_collection->filer_window;
1063 int i;
1065 g_return_if_fail(items->len > 0);
1067 /* The item data has already been modified, so this gives the
1068 * final sort order...
1070 collection_qsort(collection, sort_fn(filer_window),
1071 filer_window->sort_order);
1073 for (i = 0; i < items->len; i++)
1075 DirItem *item = (DirItem *) items->pdata[i];
1076 const gchar *leafname = item->leafname;
1077 int j;
1079 if (leafname[0] == '.' && filer_window->show_hidden == FALSE)
1080 continue;
1082 j = collection_find_item(collection, item,
1083 sort_fn(filer_window),
1084 filer_window->sort_order);
1086 if (j < 0)
1087 g_warning("Failed to find '%s'\n", leafname);
1088 else
1089 update_item(view_collection, j);
1093 static void view_collection_delete_if(ViewIface *view,
1094 gboolean (*test)(gpointer item, gpointer data),
1095 gpointer data)
1097 ViewCollection *view_collection = VIEW_COLLECTION(view);
1098 Collection *collection = view_collection->collection;
1100 collection_delete_if(collection, test, data);
1103 static void view_collection_clear(ViewIface *view)
1105 ViewCollection *view_collection = VIEW_COLLECTION(view);
1106 Collection *collection = view_collection->collection;
1108 collection_clear(collection);
1111 static void view_collection_select_all(ViewIface *view)
1113 ViewCollection *view_collection = VIEW_COLLECTION(view);
1114 Collection *collection = view_collection->collection;
1116 collection_select_all(collection);
1119 static void view_collection_clear_selection(ViewIface *view)
1121 ViewCollection *view_collection = VIEW_COLLECTION(view);
1122 Collection *collection = view_collection->collection;
1124 collection_clear_selection(collection);
1127 static int view_collection_count_items(ViewIface *view)
1129 ViewCollection *view_collection = VIEW_COLLECTION(view);
1130 Collection *collection = view_collection->collection;
1132 return collection->number_of_items;
1135 static int view_collection_count_selected(ViewIface *view)
1137 ViewCollection *view_collection = VIEW_COLLECTION(view);
1138 Collection *collection = view_collection->collection;
1140 return collection->number_selected;
1143 static void view_collection_show_cursor(ViewIface *view)
1145 ViewCollection *view_collection = VIEW_COLLECTION(view);
1146 Collection *collection = view_collection->collection;
1148 collection_move_cursor(collection, 0, 0);
1151 /* The first time the next() method is used, this is called */
1152 static DirItem *iter_init(ViewIter *iter)
1154 ViewCollection *view_collection = (ViewCollection *) iter->view;
1155 Collection *collection = view_collection->collection;
1156 int i = -1;
1157 int n = collection->number_of_items;
1158 int flags = iter->flags;
1160 iter->peek = iter_peek;
1162 if (iter->n_remaining == 0)
1163 return NULL;
1165 if (flags & VIEW_ITER_FROM_CURSOR)
1167 i = collection->cursor_item;
1168 if (i == -1)
1169 return NULL; /* No cursor */
1171 else if (flags & VIEW_ITER_FROM_BASE)
1172 i = view_collection->cursor_base;
1174 if (i < 0 || i >= n)
1176 /* Either a normal iteration, or an iteration from an
1177 * invalid starting point.
1179 if (flags & VIEW_ITER_BACKWARDS)
1180 i = n - 1;
1181 else
1182 i = 0;
1185 if (i < 0 || i >= n)
1186 return NULL; /* No items at all! */
1188 iter->next = flags & VIEW_ITER_BACKWARDS ? iter_prev : iter_next;
1189 iter->n_remaining--;
1190 iter->i = i;
1192 if (flags & VIEW_ITER_SELECTED && !collection->items[i].selected)
1193 return iter->next(iter);
1194 return iter->peek(iter);
1196 /* Advance iter to point to the next item and return the new item
1197 * (this saves you calling peek after next each time).
1199 static DirItem *iter_next(ViewIter *iter)
1201 Collection *collection = ((ViewCollection *) iter->view)->collection;
1202 int n = collection->number_of_items;
1203 int i = iter->i;
1205 g_return_val_if_fail(iter->n_remaining >= 0, NULL);
1207 /* i is the last item returned (always valid) */
1209 g_return_val_if_fail(i >= 0 && i < n, NULL);
1211 while (iter->n_remaining)
1213 i++;
1214 iter->n_remaining--;
1216 if (i == n)
1217 i = 0;
1219 g_return_val_if_fail(i >= 0 && i < n, NULL);
1221 if (iter->flags & VIEW_ITER_SELECTED &&
1222 !collection->items[i].selected)
1223 continue;
1225 iter->i = i;
1226 return collection->items[i].data;
1229 iter->i = -1;
1230 return NULL;
1233 /* Like iter_next, but in the other direction */
1234 static DirItem *iter_prev(ViewIter *iter)
1236 Collection *collection = ((ViewCollection *) iter->view)->collection;
1237 int n = collection->number_of_items;
1238 int i = iter->i;
1240 g_return_val_if_fail(iter->n_remaining >= 0, NULL);
1242 /* i is the last item returned (always valid) */
1244 g_return_val_if_fail(i >= 0 && i < n, NULL);
1246 while (iter->n_remaining)
1248 i--;
1249 iter->n_remaining--;
1251 if (i == -1)
1252 i = collection->number_of_items - 1;
1254 g_return_val_if_fail(i >= 0 && i < n, NULL);
1256 if (iter->flags & VIEW_ITER_SELECTED &&
1257 !collection->items[i].selected)
1258 continue;
1260 iter->i = i;
1261 return collection->items[i].data;
1264 iter->i = -1;
1265 return NULL;
1268 static DirItem *iter_peek(ViewIter *iter)
1270 Collection *collection = ((ViewCollection *) iter->view)->collection;
1271 int i = iter->i;
1273 if (i == -1)
1274 return NULL;
1276 g_return_val_if_fail(i >= 0 && i < collection->number_of_items, NULL);
1278 return collection->items[i].data;
1281 static void make_iter(ViewCollection *view_collection, ViewIter *iter,
1282 IterFlags flags)
1284 Collection *collection = view_collection->collection;
1286 iter->view = (ViewIface *) view_collection;
1287 iter->next = iter_init;
1288 iter->peek = NULL;
1289 iter->i = -1;
1291 iter->flags = flags;
1293 if (flags & VIEW_ITER_ONE_ONLY)
1295 iter->n_remaining = 1;
1296 iter->next(iter);
1298 else
1299 iter->n_remaining = collection->number_of_items;
1302 /* Set the iterator to return 'i' on the next peek() */
1303 static void make_item_iter(ViewCollection *view_collection,
1304 ViewIter *iter, int i)
1306 Collection *collection = view_collection->collection;
1308 g_return_if_fail(i >= -1 && i < collection->number_of_items);
1310 make_iter(view_collection, iter, 0);
1312 iter->i = i;
1313 iter->next = iter_next;
1314 iter->peek = iter_peek;
1315 iter->n_remaining = 0;
1318 static void view_collection_get_iter(ViewIface *view,
1319 ViewIter *iter, IterFlags flags)
1321 ViewCollection *view_collection = VIEW_COLLECTION(view);
1323 make_iter(view_collection, iter, flags);
1326 static void view_collection_get_iter_at_point(ViewIface *view, ViewIter *iter,
1327 GdkWindow *src, int x, int y)
1329 ViewCollection *view_collection = VIEW_COLLECTION(view);
1330 Collection *collection = view_collection->collection;
1331 int i;
1333 if (src == ((GtkWidget *) view)->window)
1335 /* The event is on the Viewport, not the collection... */
1336 y += collection->vadj->value;
1339 i = collection_get_item(collection, x, y);
1340 make_item_iter(view_collection, iter, i);
1343 static void view_collection_cursor_to_iter(ViewIface *view, ViewIter *iter)
1345 ViewCollection *view_collection = VIEW_COLLECTION(view);
1346 Collection *collection = view_collection->collection;
1347 FilerWindow *filer_window = view_collection->filer_window;
1349 g_return_if_fail(iter == NULL ||
1350 iter->view == (ViewIface *) view_collection);
1352 collection_set_cursor_item(collection, iter ? iter->i : -1,
1353 filer_window->auto_scroll == -1);
1356 static void view_collection_set_selected(ViewIface *view,
1357 ViewIter *iter,
1358 gboolean selected)
1360 ViewCollection *view_collection = VIEW_COLLECTION(view);
1361 Collection *collection = view_collection->collection;
1363 g_return_if_fail(iter != NULL &&
1364 iter->view == (ViewIface *) view_collection);
1365 g_return_if_fail(iter->i >= 0 && iter->i < collection->number_of_items);
1367 if (selected)
1368 collection_select_item(collection, iter->i);
1369 else
1370 collection_unselect_item(collection, iter->i);
1373 static gboolean view_collection_get_selected(ViewIface *view, ViewIter *iter)
1375 ViewCollection *view_collection = VIEW_COLLECTION(view);
1376 Collection *collection = view_collection->collection;
1378 g_return_val_if_fail(iter != NULL &&
1379 iter->view == (ViewIface *) view_collection, FALSE);
1380 g_return_val_if_fail(iter->i >= 0 &&
1381 iter->i < collection->number_of_items, FALSE);
1383 return collection->items[iter->i].selected;
1386 static void view_collection_select_only(ViewIface *view, ViewIter *iter)
1388 ViewCollection *view_collection = VIEW_COLLECTION(view);
1389 Collection *collection = view_collection->collection;
1391 g_return_if_fail(iter != NULL &&
1392 iter->view == (ViewIface *) view_collection);
1393 g_return_if_fail(iter->i >= 0 && iter->i < collection->number_of_items);
1395 collection_clear_except(collection, iter->i);
1398 static void view_collection_set_frozen(ViewIface *view, gboolean frozen)
1400 ViewCollection *view_collection = VIEW_COLLECTION(view);
1401 Collection *collection = view_collection->collection;
1403 if (frozen)
1404 collection->block_selection_changed++;
1405 else
1406 collection_unblock_selection_changed(collection,
1407 gtk_get_current_event_time(), TRUE);
1410 static void view_collection_wink_item(ViewIface *view, ViewIter *iter)
1412 ViewCollection *view_collection = VIEW_COLLECTION(view);
1413 Collection *collection = view_collection->collection;
1415 if (!iter)
1417 collection_wink_item(collection, -1);
1418 return;
1421 g_return_if_fail(iter != NULL &&
1422 iter->view == (ViewIface *) view_collection);
1423 g_return_if_fail(iter->i >= 0 && iter->i < collection->number_of_items);
1425 collection_wink_item(collection, iter->i);
1428 static void view_collection_autosize(ViewIface *view)
1430 ViewCollection *view_collection = VIEW_COLLECTION(view);
1431 FilerWindow *filer_window = view_collection->filer_window;
1432 Collection *collection = view_collection->collection;
1433 int n;
1434 int w = collection->item_width;
1435 int h = collection->item_height;
1436 int x;
1437 int rows, cols;
1438 int max_x, max_rows;
1439 const float r = 2.5;
1440 int t = 0;
1441 int space = 0;
1443 /* Get the extra height required for the toolbar and minibuffer,
1444 * if visible.
1446 if (o_toolbar.int_value != TOOLBAR_NONE)
1447 t = filer_window->toolbar->allocation.height;
1448 if (filer_window->message)
1449 t += filer_window->message->allocation.height;
1450 if (GTK_WIDGET_VISIBLE(filer_window->minibuffer_area))
1452 GtkRequisition req;
1454 gtk_widget_size_request(filer_window->minibuffer_area, &req);
1455 space = req.height + 2;
1456 t += space;
1459 n = collection->number_of_items;
1460 if (n == 0)
1461 h = ICON_HEIGHT * 1.5;
1462 n = MAX(n, 2);
1464 max_x = (o_filer_size_limit.int_value * screen_width) / 100;
1465 max_rows = (o_filer_size_limit.int_value * screen_height) / (h * 100);
1467 /* Aim for a size where
1468 * x = r(y + t + h), (1)
1469 * unless that's too wide.
1471 * t = toolbar (and minibuffer) height
1472 * r = desired (width / height) ratio
1474 * Want to display all items:
1475 * (x/w)(y/h) = n
1476 * => xy = nwh
1477 * => x(x/r - t - h) = nwh (from 1)
1478 * => xx - x.rt - hr(1 - nw) = 0
1479 * => 2x = rt +/- sqrt(rt.rt + 4hr(nw - 1))
1480 * Now,
1481 * 4hr(nw - 1) > 0
1482 * so
1483 * sqrt(rt.rt + ...) > rt
1485 * So, the +/- must be +:
1487 * => x = (rt + sqrt(rt.rt + 4hr(nw - 1))) / 2
1489 * ( + w - 1 to round up)
1491 x = (r * t + sqrt(r*r*t*t + 4*h*r * (n*w - 1))) / 2 + w - 1;
1493 /* Limit x */
1494 if (x > max_x)
1495 x = max_x;
1497 cols = x / w;
1498 cols = MAX(cols, 1);
1500 /* Choose rows to display all items given our chosen x.
1501 * Don't make the window *too* big!
1503 rows = (n + cols - 1) / cols;
1504 if (rows > max_rows)
1505 rows = max_rows;
1507 /* Leave some room for extra icons, but only in Small Icons mode
1508 * otherwise it takes up too much space.
1509 * Also, don't add space if the minibuffer is open.
1511 if (space == 0)
1512 space = filer_window->display_style == SMALL_ICONS ? h : 2;
1514 filer_window_set_size(filer_window,
1515 w * MAX(cols, 1),
1516 h * MAX(rows, 1) + space);
1519 static gboolean view_collection_cursor_visible(ViewIface *view)
1521 ViewCollection *view_collection = VIEW_COLLECTION(view);
1522 Collection *collection = view_collection->collection;
1524 return collection->cursor_item != -1;
1527 static void view_collection_set_base(ViewIface *view, ViewIter *iter)
1529 ViewCollection *view_collection = VIEW_COLLECTION(view);
1531 view_collection->cursor_base = iter->i;
1534 static void view_collection_start_lasso_box(ViewIface *view,
1535 GdkEventButton *event)
1537 ViewCollection *view_collection = (ViewCollection *) view;
1538 Collection *collection = view_collection->collection;
1540 filer_set_autoscroll(view_collection->filer_window, TRUE);
1541 collection_lasso_box(collection, event->x, event->y);
1545 /* Change the adjustment by this amount. Bounded. */
1546 static void diff_vpos(Collection *collection, int diff)
1548 int old = collection->vadj->value;
1549 int value = old + diff;
1551 value = CLAMP(value, 0,
1552 collection->vadj->upper - collection->vadj->page_size);
1553 gtk_adjustment_set_value(collection->vadj, value);
1555 if (collection->vadj->value != old)
1556 dnd_spring_abort();
1559 static gboolean view_collection_auto_scroll_callback(ViewIface *view)
1561 ViewCollection *view_collection = (ViewCollection *) view;
1562 Collection *collection = view_collection->collection;
1563 GdkWindow *window = ((GtkWidget *) collection)->window;
1564 gint x, y, w, h;
1565 GdkModifierType mask;
1566 int diff = 0;
1568 gdk_window_get_pointer(window, &x, &y, &mask);
1569 gdk_drawable_get_size(window, &w, NULL);
1571 h = collection->vadj->page_size;
1572 y -= collection->vadj->value;
1574 if ((x < 0 || x > w || y < 0 || y > h) && !collection->lasso_box)
1575 return FALSE; /* Out of window - stop */
1577 if (y < AUTOSCROLL_STEP)
1578 diff = y - AUTOSCROLL_STEP;
1579 else if (y > h - AUTOSCROLL_STEP)
1580 diff = AUTOSCROLL_STEP + y - h;
1582 if (diff)
1583 diff_vpos(collection, diff);
1585 return TRUE;