r2525: Use stock icons in pinboard and panel menus.
[rox-filer.git] / ROX-Filer / src / view_collection.c
blobf708168e7e631614ca2240f091e592b610d51934
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 gboolean view_collection_autoselect(ViewIface *view, const gchar *leaf);
143 static void view_collection_add_items(ViewIface *view, GPtrArray *items);
144 static void view_collection_update_items(ViewIface *view, GPtrArray *items);
145 static void view_collection_delete_if(ViewIface *view,
146 gboolean (*test)(gpointer item, gpointer data),
147 gpointer data);
148 static void view_collection_clear(ViewIface *view);
149 static void view_collection_select_all(ViewIface *view);
150 static void view_collection_clear_selection(ViewIface *view);
151 static int view_collection_count_items(ViewIface *view);
152 static int view_collection_count_selected(ViewIface *view);
153 static void view_collection_show_cursor(ViewIface *view);
154 static void view_collection_get_iter(ViewIface *view,
155 ViewIter *iter, IterFlags flags);
156 static void view_collection_get_iter_at_point(ViewIface *view, ViewIter *iter,
157 GdkWindow *src, int x, int y);
158 static void view_collection_cursor_to_iter(ViewIface *view, ViewIter *iter);
159 static void view_collection_set_selected(ViewIface *view,
160 ViewIter *iter,
161 gboolean selected);
162 static gboolean view_collection_get_selected(ViewIface *view, ViewIter *iter);
163 static void view_collection_select_only(ViewIface *view, ViewIter *iter);
164 static void view_collection_set_frozen(ViewIface *view, gboolean frozen);
165 static void view_collection_wink_item(ViewIface *view, ViewIter *iter);
166 static void view_collection_autosize(ViewIface *view);
167 static gboolean view_collection_cursor_visible(ViewIface *view);
168 static void view_collection_set_base(ViewIface *view, ViewIter *iter);
169 static void view_collection_start_lasso_box(ViewIface *view,
170 GdkEventButton *event);
171 static void view_collection_extend_tip(ViewIface *view, ViewIter *iter,
172 GString *tip);
173 static gboolean view_collection_auto_scroll_callback(ViewIface *view);
175 static DirItem *iter_next(ViewIter *iter);
176 static DirItem *iter_prev(ViewIter *iter);
177 static DirItem *iter_peek(ViewIter *iter);
180 /****************************************************************
181 * EXTERNAL INTERFACE *
182 ****************************************************************/
184 GtkWidget *view_collection_new(FilerWindow *filer_window)
186 ViewCollection *view_collection;
188 view_collection = g_object_new(view_collection_get_type(), NULL);
189 view_collection->filer_window = filer_window;
191 gtk_range_set_adjustment(GTK_RANGE(filer_window->scrollbar),
192 view_collection->collection->vadj);
194 return GTK_WIDGET(view_collection);
197 GType view_collection_get_type(void)
199 static GType type = 0;
201 if (!type)
203 static const GTypeInfo info =
205 sizeof (ViewCollectionClass),
206 NULL, /* base_init */
207 NULL, /* base_finalise */
208 view_collection_class_init,
209 NULL, /* class_finalise */
210 NULL, /* class_data */
211 sizeof(ViewCollection),
212 0, /* n_preallocs */
213 view_collection_init
215 static const GInterfaceInfo iface_info =
217 view_collection_iface_init, NULL, NULL
220 type = g_type_register_static(gtk_viewport_get_type(),
221 "ViewCollection", &info, 0);
222 g_type_add_interface_static(type, VIEW_TYPE_IFACE, &iface_info);
225 return type;
228 /****************************************************************
229 * INTERNAL FUNCTIONS *
230 ****************************************************************/
232 static void view_collection_destroy(GtkObject *view_collection)
234 VIEW_COLLECTION(view_collection)->filer_window = NULL;
237 static void view_collection_finialize(GObject *object)
239 /* ViewCollection *view_collection = (ViewCollection *) object; */
241 G_OBJECT_CLASS(parent_class)->finalize(object);
244 static void view_collection_class_init(gpointer gclass, gpointer data)
246 GObjectClass *object = (GObjectClass *) gclass;
248 parent_class = g_type_class_peek_parent(gclass);
250 object->finalize = view_collection_finialize;
251 GTK_OBJECT_CLASS(object)->destroy = view_collection_destroy;
254 static void view_collection_init(GTypeInstance *object, gpointer gclass)
256 ViewCollection *view_collection = (ViewCollection *) object;
257 GtkViewport *viewport = (GtkViewport *) object;
258 GtkWidget *collection;
259 GtkAdjustment *adj;
261 collection = collection_new();
262 view_collection->collection = COLLECTION(collection);
264 adj = view_collection->collection->vadj;
265 gtk_viewport_set_vadjustment(viewport, adj);
266 gtk_viewport_set_hadjustment(viewport, NULL); /* Or Gtk will crash */
267 gtk_viewport_set_shadow_type(viewport, GTK_SHADOW_NONE);
268 gtk_container_add(GTK_CONTAINER(object), collection);
269 gtk_widget_show(collection);
270 gtk_widget_set_size_request(GTK_WIDGET(view_collection), 4, 4);
272 gtk_container_set_resize_mode(GTK_CONTAINER(viewport),
273 GTK_RESIZE_IMMEDIATE);
275 view_collection->collection->free_item = display_free_colitem;
276 view_collection->collection->draw_item = draw_item;
277 view_collection->collection->test_point = test_point;
278 view_collection->collection->cb_user_data = view_collection;
280 g_signal_connect(collection, "style_set",
281 G_CALLBACK(style_set),
282 view_collection);
284 g_signal_connect(collection, "lose_selection",
285 G_CALLBACK(lost_selection), view_collection);
286 g_signal_connect(collection, "selection_changed",
287 G_CALLBACK(selection_changed), view_collection);
289 g_signal_connect(collection, "button-release-event",
290 G_CALLBACK(coll_button_release), view_collection);
291 g_signal_connect(collection, "button-press-event",
292 G_CALLBACK(coll_button_press), view_collection);
293 g_signal_connect(collection, "motion-notify-event",
294 G_CALLBACK(coll_motion_notify), view_collection);
295 g_signal_connect(viewport, "size-allocate",
296 G_CALLBACK(size_allocate), view_collection);
298 gtk_widget_set_events(collection,
299 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
300 GDK_BUTTON3_MOTION_MASK | GDK_POINTER_MOTION_MASK |
301 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
305 static void draw_item(GtkWidget *widget,
306 CollectionItem *colitem,
307 GdkRectangle *area,
308 gpointer user_data)
310 DirItem *item = (DirItem *) colitem->data;
311 gboolean selected = colitem->selected;
312 Template template;
313 ViewData *view = (ViewData *) colitem->view_data;
314 ViewCollection *view_collection = (ViewCollection *) user_data;
315 FilerWindow *filer_window = view_collection->filer_window;
316 GtkStateType selection_state;
318 g_return_if_fail(view != NULL);
320 if (selected)
321 selection_state = filer_window->selection_state;
322 else
323 selection_state = GTK_STATE_NORMAL;
325 fill_template(area, colitem, view_collection, &template);
327 /* Set up GC for coloured file types */
328 if (!type_gc)
329 type_gc = gdk_gc_new(widget->window);
331 gdk_gc_set_foreground(type_gc, type_get_colour(item,
332 &widget->style->fg[GTK_STATE_NORMAL]));
334 if (template.icon.width <= SMALL_WIDTH &&
335 template.icon.height <= SMALL_HEIGHT)
337 draw_small_icon(widget->window, &template.icon,
338 item, view->image, selected);
340 else if (template.icon.width <= ICON_WIDTH &&
341 template.icon.height <= ICON_HEIGHT)
343 draw_large_icon(widget->window, &template.icon,
344 item, view->image, selected);
346 else
348 draw_huge_icon(widget->window, &template.icon,
349 item, view->image, selected);
352 draw_string(widget, view->layout,
353 &template.leafname,
354 view->name_width,
355 selection_state,
356 TRUE);
357 if (view->details)
358 draw_string(widget, view->details,
359 &template.details,
360 template.details.width,
361 selection_state,
362 TRUE);
365 /* A template contains the locations of the three rectangles (for the icon,
366 * name and extra details).
367 * Fill in the empty 'template' with the rectanges for this item.
369 static void fill_template(GdkRectangle *area, CollectionItem *colitem,
370 ViewCollection *view_collection, Template *template)
372 DisplayStyle style = view_collection->filer_window->display_style;
373 ViewData *view = (ViewData *) colitem->view_data;
375 if (view->details)
377 template->details.width = view->details_width;
378 template->details.height = view->details_height;
380 if (style == SMALL_ICONS)
381 small_full_template(area, colitem,
382 view_collection, template);
383 else if (style == LARGE_ICONS)
384 large_full_template(area, colitem,
385 view_collection, template);
386 else
387 huge_full_template(area, colitem,
388 view_collection, template);
390 else
392 if (style == HUGE_ICONS)
393 huge_template(area, colitem,
394 view_collection, template);
395 else if (style == LARGE_ICONS)
396 large_template(area, colitem,
397 view_collection, template);
398 else
399 small_template(area, colitem,
400 view_collection, template);
404 static void huge_template(GdkRectangle *area, CollectionItem *colitem,
405 ViewCollection *view_collection, Template *template)
407 int col_width = view_collection->collection->item_width;
408 int text_x, text_y;
409 ViewData *view = (ViewData *) colitem->view_data;
410 MaskedPixmap *image = view->image;
412 if (image)
414 if (!image->huge_pixbuf)
415 pixmap_make_huge(image);
416 template->icon.width = image->huge_width;
417 template->icon.height = image->huge_height;
419 else
421 template->icon.width = HUGE_WIDTH * 3 / 2;
422 template->icon.height = HUGE_HEIGHT;
425 template->leafname.width = view->name_width;
426 template->leafname.height = view->name_height;
428 text_x = area->x + ((col_width - template->leafname.width) >> 1);
429 text_y = area->y + area->height - template->leafname.height;
431 template->leafname.x = text_x;
432 template->leafname.y = text_y;
434 template->icon.x = area->x + ((col_width - template->icon.width) >> 1);
435 template->icon.y = template->leafname.y - template->icon.height - 2;
438 static void large_template(GdkRectangle *area, CollectionItem *colitem,
439 ViewCollection *view_collection, Template *template)
441 int col_width = view_collection->collection->item_width;
442 int iwidth, iheight;
443 int image_x;
444 int image_y;
445 ViewData *view = (ViewData *) colitem->view_data;
446 MaskedPixmap *image = view->image;
448 int text_x, text_y;
450 if (image)
452 iwidth = MIN(image->width, ICON_WIDTH);
453 iheight = MIN(image->height + 6, ICON_HEIGHT);
455 else
457 iwidth = ICON_WIDTH;
458 iheight = ICON_HEIGHT;
460 image_x = area->x + ((col_width - iwidth) >> 1);
462 template->leafname.width = view->name_width;
463 template->leafname.height = view->name_height;
465 text_x = area->x + ((col_width - template->leafname.width) >> 1);
466 text_y = area->y + ICON_HEIGHT + 2;
468 template->leafname.x = text_x;
469 template->leafname.y = text_y;
471 image_y = text_y - iheight;
472 image_y = MAX(area->y, image_y);
474 template->icon.x = image_x;
475 template->icon.y = image_y;
476 template->icon.width = iwidth;
477 template->icon.height = MIN(ICON_HEIGHT, iheight);
480 static void small_template(GdkRectangle *area, CollectionItem *colitem,
481 ViewCollection *view_collection, Template *template)
483 int text_x = area->x + SMALL_WIDTH + 4;
484 int low_text_y;
485 int max_text_width = area->width - SMALL_WIDTH - 4;
486 ViewData *view = (ViewData *) colitem->view_data;
488 low_text_y = area->y + area->height / 2 - view->name_height / 2;
490 template->leafname.x = text_x;
491 template->leafname.y = low_text_y;
492 template->leafname.width = MIN(max_text_width, view->name_width);
493 template->leafname.height = view->name_height;
495 template->icon.x = area->x;
496 template->icon.y = area->y + 1;
497 template->icon.width = SMALL_WIDTH;
498 template->icon.height = SMALL_HEIGHT;
501 static void huge_full_template(GdkRectangle *area, CollectionItem *colitem,
502 ViewCollection *view_collection, Template *template)
504 int max_text_width = area->width - HUGE_WIDTH - 4;
505 ViewData *view = (ViewData *) colitem->view_data;
506 MaskedPixmap *image = view->image;
508 if (image)
510 if (!image->huge_pixbuf)
511 pixmap_make_huge(image);
512 template->icon.width = image->huge_width;
513 template->icon.height = image->huge_height;
515 else
517 template->icon.width = HUGE_WIDTH * 3 / 2;
518 template->icon.height = HUGE_HEIGHT;
521 template->icon.x = area->x + (HUGE_WIDTH - template->icon.width) / 2;
522 template->icon.y = area->y + (area->height - template->icon.height) / 2;
524 template->leafname.x = area->x + HUGE_WIDTH + 4;
525 template->leafname.y = area->y + area->height / 2
526 - (view->name_height + 2 + view->details_height) / 2;
527 template->leafname.width = MIN(max_text_width, view->name_width);
528 template->leafname.height = view->name_height;
530 if (!image)
531 return; /* Not scanned yet */
533 template->details.x = template->leafname.x;
534 template->details.y = template->leafname.y + view->name_height + 2;
537 static void large_full_template(GdkRectangle *area, CollectionItem *colitem,
538 ViewCollection *view_collection, Template *template)
540 int max_text_width = area->width - ICON_WIDTH - 4;
541 ViewData *view = (ViewData *) colitem->view_data;
542 MaskedPixmap *image = view->image;
544 if (image)
546 template->icon.width = image->width;
547 template->icon.height = image->height;
549 else
551 template->icon.width = ICON_WIDTH;
552 template->icon.height = ICON_HEIGHT;
555 template->icon.x = area->x + (ICON_WIDTH - template->icon.width) / 2;
556 template->icon.y = area->y + (area->height - template->icon.height) / 2;
559 template->leafname.x = area->x + ICON_WIDTH + 4;
560 template->leafname.y = area->y + area->height / 2
561 - (view->name_height + 2 + view->details_height) / 2;
562 template->leafname.width = MIN(max_text_width, view->name_width);
563 template->leafname.height = view->name_height;
565 if (!image)
566 return; /* Not scanned yet */
568 template->details.x = template->leafname.x;
569 template->details.y = template->leafname.y + view->name_height + 2;
572 static void small_full_template(GdkRectangle *area, CollectionItem *colitem,
573 ViewCollection *view_collection, Template *template)
575 int col_width = view_collection->collection->item_width;
576 ViewData *view = (ViewData *) colitem->view_data;
578 small_template(area, colitem, view_collection, template);
580 if (!view->image)
581 return; /* Not scanned yet */
583 template->details.x = area->x + col_width - template->details.width;
584 template->details.y = area->y + area->height / 2 - \
585 view->details_height / 2;
588 #define INSIDE(px, py, area) \
589 (px >= area.x && py >= area.y && \
590 px <= area.x + area.width && py <= area.y + area.height)
592 static gboolean test_point(Collection *collection,
593 int point_x, int point_y,
594 CollectionItem *colitem,
595 int width, int height,
596 gpointer user_data)
598 Template template;
599 GdkRectangle area;
600 ViewData *view = (ViewData *) colitem->view_data;
601 ViewCollection *view_collection = (ViewCollection *) user_data;
603 area.x = 0;
604 area.y = 0;
605 area.width = width;
606 area.height = height;
608 fill_template(&area, colitem, view_collection, &template);
610 return INSIDE(point_x, point_y, template.leafname) ||
611 INSIDE(point_x, point_y, template.icon) ||
612 (view->details && INSIDE(point_x, point_y, template.details));
615 /* 'box' renders a background box if the string is also selected */
616 static void draw_string(GtkWidget *widget,
617 PangoLayout *layout,
618 GdkRectangle *area, /* Area available on screen */
619 int width, /* Width of the full string */
620 GtkStateType selection_state,
621 gboolean box)
623 GdkGC *gc = selection_state == GTK_STATE_NORMAL
624 ? type_gc
625 : widget->style->fg_gc[selection_state];
627 if (selection_state != GTK_STATE_NORMAL && box)
628 gtk_paint_flat_box(widget->style, widget->window,
629 selection_state, GTK_SHADOW_NONE,
630 NULL, widget, "text",
631 area->x, area->y,
632 MIN(width, area->width),
633 area->height);
635 if (width > area->width)
637 gdk_gc_set_clip_origin(gc, 0, 0);
638 gdk_gc_set_clip_rectangle(gc, area);
641 gdk_draw_layout(widget->window, gc, area->x, area->y, layout);
643 if (width > area->width)
645 static GdkGC *red_gc = NULL;
647 if (!red_gc)
649 gboolean success;
650 GdkColor red = {0, 0xffff, 0, 0};
652 red_gc = gdk_gc_new(widget->window);
653 gdk_colormap_alloc_colors(
654 gtk_widget_get_colormap(widget),
655 &red, 1, FALSE, TRUE, &success);
656 gdk_gc_set_foreground(red_gc, &red);
658 gdk_draw_rectangle(widget->window, red_gc, TRUE,
659 area->x + area->width - 1, area->y,
660 1, area->height);
661 gdk_gc_set_clip_rectangle(gc, NULL);
665 /* Create the handers for the View interface */
666 static void view_collection_iface_init(gpointer giface, gpointer iface_data)
668 ViewIfaceClass *iface = giface;
670 g_assert(G_TYPE_FROM_INTERFACE(iface) == VIEW_TYPE_IFACE);
672 /* override stuff */
673 iface->sort = view_collection_sort;
674 iface->style_changed = view_collection_style_changed;
675 iface->autoselect = view_collection_autoselect;
676 iface->add_items = view_collection_add_items;
677 iface->update_items = view_collection_update_items;
678 iface->delete_if = view_collection_delete_if;
679 iface->clear = view_collection_clear;
680 iface->select_all = view_collection_select_all;
681 iface->clear_selection = view_collection_clear_selection;
682 iface->count_items = view_collection_count_items;
683 iface->count_selected = view_collection_count_selected;
684 iface->show_cursor = view_collection_show_cursor;
685 iface->get_iter = view_collection_get_iter;
686 iface->get_iter_at_point = view_collection_get_iter_at_point;
687 iface->cursor_to_iter = view_collection_cursor_to_iter;
688 iface->set_selected = view_collection_set_selected;
689 iface->get_selected = view_collection_get_selected;
690 iface->set_frozen = view_collection_set_frozen;
691 iface->select_only = view_collection_select_only;
692 iface->wink_item = view_collection_wink_item;
693 iface->autosize = view_collection_autosize;
694 iface->cursor_visible = view_collection_cursor_visible;
695 iface->set_base = view_collection_set_base;
696 iface->start_lasso_box = view_collection_start_lasso_box;
697 iface->extend_tip = view_collection_extend_tip;
698 iface->auto_scroll_callback = view_collection_auto_scroll_callback;
701 static void view_collection_extend_tip(ViewIface *view, ViewIter *iter,
702 GString *tip)
704 ViewCollection*view_collection = (ViewCollection *) view;
705 Collection *collection = view_collection->collection;
706 FilerWindow *filer_window = view_collection->filer_window;
707 Template template;
708 int i = iter->i;
709 CollectionItem *colitem = &collection->items[i];
710 int col = i % collection->columns;
711 int row = i / collection->columns;
712 ViewData *view_data = (ViewData *) colitem->view_data;
713 GdkRectangle area;
715 g_return_if_fail(iter->view == (ViewIface *) view_collection);
716 g_return_if_fail(i >= 0 && i < collection->number_of_items);
718 /* TODO: What if the window is narrower than 1 column? */
719 if (filer_window->display_style == LARGE_ICONS ||
720 filer_window->display_style == HUGE_ICONS)
721 return; /* These wrap rather than truncate */
723 area.x = col * collection->item_width;
724 area.y = row * collection->item_height;
725 area.height = collection->item_height;
727 if (col == collection->columns - 1)
728 area.width = GTK_WIDGET(collection)->allocation.width - area.x;
729 else
730 area.width = collection->item_width;
732 fill_template(&area, colitem, view_collection, &template);
734 if (template.leafname.width < view_data->name_width)
736 DirItem *item = (DirItem *) collection->items[i].data;
738 g_string_append(tip, item->leafname);
739 g_string_append_c(tip, '\n');
743 static gint coll_motion_notify(GtkWidget *widget,
744 GdkEventMotion *event,
745 ViewCollection *view_collection)
747 return filer_motion_notify(view_collection->filer_window, event);
750 /* Viewport is to be resized, so calculate increments */
751 static void size_allocate(GtkWidget *w, GtkAllocation *a, gpointer data)
753 Collection *col = ((ViewCollection *) data)->collection;
755 col->vadj->step_increment = col->item_height;
756 col->vadj->page_increment = col->vadj->page_size;
759 static gint coll_button_release(GtkWidget *widget,
760 GdkEventButton *event,
761 ViewCollection *view_collection)
763 if (dnd_motion_release(event))
765 if (motion_buttons_pressed == 0 &&
766 view_collection->collection->lasso_box)
768 filer_set_autoscroll(view_collection->filer_window,
769 FALSE);
770 collection_end_lasso(view_collection->collection,
771 event->button == 1 ? GDK_SET : GDK_INVERT);
773 return FALSE;
776 filer_perform_action(view_collection->filer_window, event);
778 return FALSE;
781 static gint coll_button_press(GtkWidget *widget,
782 GdkEventButton *event,
783 ViewCollection *view_collection)
785 collection_set_cursor_item(view_collection->collection, -1, TRUE);
787 if (dnd_motion_press(widget, event))
788 filer_perform_action(view_collection->filer_window, event);
790 return FALSE;
793 /* Nothing is selected anymore - give up primary */
794 static void lost_selection(Collection *collection,
795 guint time,
796 gpointer user_data)
798 ViewCollection *view_collection = VIEW_COLLECTION(user_data);
800 filer_lost_selection(view_collection->filer_window, time);
803 static void selection_changed(Collection *collection,
804 gint time,
805 gpointer user_data)
807 ViewCollection *view_collection = VIEW_COLLECTION(user_data);
809 filer_selection_changed(view_collection->filer_window, time);
812 static void display_free_colitem(Collection *collection,
813 CollectionItem *colitem)
815 ViewData *view = (ViewData *) colitem->view_data;
817 if (!view)
818 return;
820 if (view->layout)
822 g_object_unref(G_OBJECT(view->layout));
823 view->layout = NULL;
825 if (view->details)
826 g_object_unref(G_OBJECT(view->details));
828 if (view->image)
829 g_object_unref(view->image);
831 g_free(view);
834 static void add_item(ViewCollection *view_collection, DirItem *item)
836 Collection *collection = view_collection->collection;
837 FilerWindow *filer_window = view_collection->filer_window;
838 int old_w = collection->item_width;
839 int old_h = collection->item_height;
840 int w, h, i;
842 i = collection_insert(collection, item,
843 display_create_viewdata(filer_window, item));
845 calc_size(filer_window, &collection->items[i], &w, &h);
847 if (w > old_w || h > old_h)
848 collection_set_item_size(collection,
849 MAX(old_w, w),
850 MAX(old_h, h));
853 static void style_set(Collection *collection,
854 GtkStyle *style,
855 ViewCollection *view_collection)
857 view_collection_style_changed(VIEW(view_collection),
858 VIEW_UPDATE_VIEWDATA | VIEW_UPDATE_NAME);
861 /* Return the size needed for this item */
862 static void calc_size(FilerWindow *filer_window, CollectionItem *colitem,
863 int *width, int *height)
865 int pix_width, pix_height;
866 int w;
867 DisplayStyle style = filer_window->display_style;
868 ViewData *view = (ViewData *) colitem->view_data;
870 if (filer_window->details_type == DETAILS_NONE)
872 if (style == HUGE_ICONS)
874 if (view->image)
876 if (!view->image->huge_pixbuf)
877 pixmap_make_huge(view->image);
878 pix_width = view->image->huge_width;
879 pix_height = view->image->huge_height;
881 else
883 pix_width = HUGE_WIDTH * 3 / 2;
884 pix_height = HUGE_HEIGHT * 3 / 2;
886 *width = MAX(pix_width, view->name_width) + 4;
887 *height = MAX(view->name_height + pix_height + 4,
888 HUGE_HEIGHT * 3 / 4);
890 else if (style == SMALL_ICONS)
892 w = MIN(view->name_width, o_small_width.int_value);
893 *width = SMALL_WIDTH + 12 + w;
894 *height = MAX(view->name_height, SMALL_HEIGHT) + 4;
896 else
898 if (view->image)
899 pix_width = view->image->width;
900 else
901 pix_width = ICON_WIDTH;
902 *width = MAX(pix_width, view->name_width) + 4;
903 *height = view->name_height + ICON_HEIGHT + 2;
906 else
908 w = view->details_width;
909 if (style == HUGE_ICONS)
911 *width = HUGE_WIDTH + 12 + MAX(w, view->name_width);
912 *height = HUGE_HEIGHT - 4;
914 else if (style == SMALL_ICONS)
916 int text_height;
918 *width = SMALL_WIDTH + view->name_width + 12 + w;
919 text_height = MAX(view->name_height,
920 view->details_height);
921 *height = MAX(text_height, SMALL_HEIGHT) + 4;
923 else
925 *width = ICON_WIDTH + 12 + MAX(w, view->name_width);
926 *height = ICON_HEIGHT;
931 static void update_item(ViewCollection *view_collection, int i)
933 Collection *collection = view_collection->collection;
934 int old_w = collection->item_width;
935 int old_h = collection->item_height;
936 int w, h;
937 CollectionItem *colitem;
938 FilerWindow *filer_window = view_collection->filer_window;
940 g_return_if_fail(i >= 0 && i < collection->number_of_items);
942 colitem = &collection->items[i];
944 display_update_view(filer_window,
945 (DirItem *) colitem->data,
946 (ViewData *) colitem->view_data,
947 FALSE);
949 calc_size(filer_window, colitem, &w, &h);
950 if (w > old_w || h > old_h)
951 collection_set_item_size(collection,
952 MAX(old_w, w),
953 MAX(old_h, h));
955 collection_draw_item(collection, i, TRUE);
958 /* Implementations of the View interface. See view_iface.c for comments. */
960 static void view_collection_style_changed(ViewIface *view, int flags)
962 ViewCollection *view_collection = VIEW_COLLECTION(view);
963 FilerWindow *filer_window = view_collection->filer_window;
964 int i;
965 Collection *col = view_collection->collection;
966 int width = MIN_ITEM_WIDTH;
967 int height = SMALL_HEIGHT;
968 int n = col->number_of_items;
970 if (n == 0 && filer_window->display_style != SMALL_ICONS)
971 height = ICON_HEIGHT;
973 /* Recalculate all the ViewData structs for this window
974 * (needed if the text or image has changed in any way) and
975 * get the size of each item.
977 for (i = 0; i < n; i++)
979 CollectionItem *ci = &col->items[i];
980 int w, h;
982 if (flags & (VIEW_UPDATE_VIEWDATA | VIEW_UPDATE_NAME))
983 display_update_view(filer_window,
984 (DirItem *) ci->data,
985 (ViewData *) ci->view_data,
986 (flags & VIEW_UPDATE_NAME) != 0);
988 calc_size(filer_window, ci, &w, &h);
989 if (w > width)
990 width = w;
991 if (h > height)
992 height = h;
995 collection_set_item_size(col, width, height);
997 gtk_widget_queue_draw(GTK_WIDGET(view_collection));
1000 typedef int (*SortFn)(gconstpointer a, gconstpointer b);
1002 static SortFn sort_fn(FilerWindow *fw)
1004 switch (fw->sort_type)
1006 case SORT_NAME: return sort_by_name;
1007 case SORT_TYPE: return sort_by_type;
1008 case SORT_DATE: return sort_by_date;
1009 case SORT_SIZE: return sort_by_size;
1010 case SORT_OWNER: return sort_by_owner;
1011 case SORT_GROUP: return sort_by_group;
1012 default:
1013 g_assert_not_reached();
1016 return NULL;
1019 static void view_collection_sort(ViewIface *view)
1021 ViewCollection *view_collection = VIEW_COLLECTION(view);
1022 FilerWindow *filer_window = view_collection->filer_window;
1024 collection_qsort(view_collection->collection, sort_fn(filer_window),
1025 filer_window->sort_order);
1028 static gboolean view_collection_autoselect(ViewIface *view, const gchar *leaf)
1030 ViewCollection *view_collection = VIEW_COLLECTION(view);
1031 Collection *col = view_collection->collection;
1032 int i;
1034 for (i = 0; i < col->number_of_items; i++)
1036 DirItem *item = (DirItem *) col->items[i].data;
1038 if (strcmp(item->leafname, leaf) == 0)
1040 if (col->cursor_item != -1)
1041 collection_set_cursor_item(col, i, TRUE);
1042 else
1043 collection_wink_item(col, i);
1044 return TRUE;
1048 return FALSE;
1051 static void view_collection_add_items(ViewIface *view, GPtrArray *items)
1053 ViewCollection *view_collection = VIEW_COLLECTION(view);
1054 Collection *collection = view_collection->collection;
1055 FilerWindow *filer_window = view_collection->filer_window;
1056 int old_num, i;
1058 old_num = collection->number_of_items;
1059 for (i = 0; i < items->len; i++)
1061 DirItem *item = (DirItem *) items->pdata[i];
1062 char *leafname = item->leafname;
1064 if (leafname[0] == '.' && !filer_window->show_hidden)
1065 continue;
1067 add_item(view_collection, item);
1070 if (old_num != collection->number_of_items)
1071 view_collection_sort(view);
1074 static void view_collection_update_items(ViewIface *view, GPtrArray *items)
1076 ViewCollection *view_collection = VIEW_COLLECTION(view);
1077 Collection *collection = view_collection->collection;
1078 FilerWindow *filer_window = view_collection->filer_window;
1079 int i;
1081 g_return_if_fail(items->len > 0);
1083 /* The item data has already been modified, so this gives the
1084 * final sort order...
1086 collection_qsort(collection, sort_fn(filer_window),
1087 filer_window->sort_order);
1089 for (i = 0; i < items->len; i++)
1091 DirItem *item = (DirItem *) items->pdata[i];
1092 const gchar *leafname = item->leafname;
1093 int j;
1095 if (leafname[0] == '.' && filer_window->show_hidden == FALSE)
1096 continue;
1098 j = collection_find_item(collection, item,
1099 sort_fn(filer_window),
1100 filer_window->sort_order);
1102 if (j < 0)
1103 g_warning("Failed to find '%s'\n", leafname);
1104 else
1105 update_item(view_collection, j);
1109 static void view_collection_delete_if(ViewIface *view,
1110 gboolean (*test)(gpointer item, gpointer data),
1111 gpointer data)
1113 ViewCollection *view_collection = VIEW_COLLECTION(view);
1114 Collection *collection = view_collection->collection;
1116 collection_delete_if(collection, test, data);
1119 static void view_collection_clear(ViewIface *view)
1121 ViewCollection *view_collection = VIEW_COLLECTION(view);
1122 Collection *collection = view_collection->collection;
1124 collection_clear(collection);
1127 static void view_collection_select_all(ViewIface *view)
1129 ViewCollection *view_collection = VIEW_COLLECTION(view);
1130 Collection *collection = view_collection->collection;
1132 collection_select_all(collection);
1135 static void view_collection_clear_selection(ViewIface *view)
1137 ViewCollection *view_collection = VIEW_COLLECTION(view);
1138 Collection *collection = view_collection->collection;
1140 collection_clear_selection(collection);
1143 static int view_collection_count_items(ViewIface *view)
1145 ViewCollection *view_collection = VIEW_COLLECTION(view);
1146 Collection *collection = view_collection->collection;
1148 return collection->number_of_items;
1151 static int view_collection_count_selected(ViewIface *view)
1153 ViewCollection *view_collection = VIEW_COLLECTION(view);
1154 Collection *collection = view_collection->collection;
1156 return collection->number_selected;
1159 static void view_collection_show_cursor(ViewIface *view)
1161 ViewCollection *view_collection = VIEW_COLLECTION(view);
1162 Collection *collection = view_collection->collection;
1164 collection_move_cursor(collection, 0, 0);
1167 /* The first time the next() method is used, this is called */
1168 static DirItem *iter_init(ViewIter *iter)
1170 ViewCollection *view_collection = (ViewCollection *) iter->view;
1171 Collection *collection = view_collection->collection;
1172 int i = -1;
1173 int n = collection->number_of_items;
1174 int flags = iter->flags;
1176 iter->peek = iter_peek;
1178 if (iter->n_remaining == 0)
1179 return NULL;
1181 if (flags & VIEW_ITER_FROM_CURSOR)
1183 i = collection->cursor_item;
1184 if (i == -1)
1185 return NULL; /* No cursor */
1187 else if (flags & VIEW_ITER_FROM_BASE)
1188 i = view_collection->cursor_base;
1190 if (i < 0 || i >= n)
1192 /* Either a normal iteration, or an iteration from an
1193 * invalid starting point.
1195 if (flags & VIEW_ITER_BACKWARDS)
1196 i = n - 1;
1197 else
1198 i = 0;
1201 if (i < 0 || i >= n)
1202 return NULL; /* No items at all! */
1204 iter->next = flags & VIEW_ITER_BACKWARDS ? iter_prev : iter_next;
1205 iter->n_remaining--;
1206 iter->i = i;
1208 if (flags & VIEW_ITER_SELECTED && !collection->items[i].selected)
1209 return iter->next(iter);
1210 return iter->peek(iter);
1212 /* Advance iter to point to the next item and return the new item
1213 * (this saves you calling peek after next each time).
1215 static DirItem *iter_next(ViewIter *iter)
1217 Collection *collection = ((ViewCollection *) iter->view)->collection;
1218 int n = collection->number_of_items;
1219 int i = iter->i;
1221 g_return_val_if_fail(iter->n_remaining >= 0, NULL);
1223 /* i is the last item returned (always valid) */
1225 g_return_val_if_fail(i >= 0 && i < n, NULL);
1227 while (iter->n_remaining)
1229 i++;
1230 iter->n_remaining--;
1232 if (i == n)
1233 i = 0;
1235 g_return_val_if_fail(i >= 0 && i < n, NULL);
1237 if (iter->flags & VIEW_ITER_SELECTED &&
1238 !collection->items[i].selected)
1239 continue;
1241 iter->i = i;
1242 return collection->items[i].data;
1245 iter->i = -1;
1246 return NULL;
1249 /* Like iter_next, but in the other direction */
1250 static DirItem *iter_prev(ViewIter *iter)
1252 Collection *collection = ((ViewCollection *) iter->view)->collection;
1253 int n = collection->number_of_items;
1254 int i = iter->i;
1256 g_return_val_if_fail(iter->n_remaining >= 0, NULL);
1258 /* i is the last item returned (always valid) */
1260 g_return_val_if_fail(i >= 0 && i < n, NULL);
1262 while (iter->n_remaining)
1264 i--;
1265 iter->n_remaining--;
1267 if (i == -1)
1268 i = collection->number_of_items - 1;
1270 g_return_val_if_fail(i >= 0 && i < n, NULL);
1272 if (iter->flags & VIEW_ITER_SELECTED &&
1273 !collection->items[i].selected)
1274 continue;
1276 iter->i = i;
1277 return collection->items[i].data;
1280 iter->i = -1;
1281 return NULL;
1284 static DirItem *iter_peek(ViewIter *iter)
1286 Collection *collection = ((ViewCollection *) iter->view)->collection;
1287 int i = iter->i;
1289 if (i == -1)
1290 return NULL;
1292 g_return_val_if_fail(i >= 0 && i < collection->number_of_items, NULL);
1294 return collection->items[i].data;
1297 static void make_iter(ViewCollection *view_collection, ViewIter *iter,
1298 IterFlags flags)
1300 Collection *collection = view_collection->collection;
1302 iter->view = (ViewIface *) view_collection;
1303 iter->next = iter_init;
1304 iter->peek = NULL;
1305 iter->i = -1;
1307 iter->flags = flags;
1309 if (flags & VIEW_ITER_ONE_ONLY)
1311 iter->n_remaining = 1;
1312 iter->next(iter);
1314 else
1315 iter->n_remaining = collection->number_of_items;
1318 /* Set the iterator to return 'i' on the next peek() */
1319 static void make_item_iter(ViewCollection *view_collection,
1320 ViewIter *iter, int i)
1322 Collection *collection = view_collection->collection;
1324 g_return_if_fail(i >= -1 && i < collection->number_of_items);
1326 make_iter(view_collection, iter, 0);
1328 iter->i = i;
1329 iter->next = iter_next;
1330 iter->peek = iter_peek;
1331 iter->n_remaining = 0;
1334 static void view_collection_get_iter(ViewIface *view,
1335 ViewIter *iter, IterFlags flags)
1337 ViewCollection *view_collection = VIEW_COLLECTION(view);
1339 make_iter(view_collection, iter, flags);
1342 static void view_collection_get_iter_at_point(ViewIface *view, ViewIter *iter,
1343 GdkWindow *src, int x, int y)
1345 ViewCollection *view_collection = VIEW_COLLECTION(view);
1346 Collection *collection = view_collection->collection;
1347 int i;
1349 if (src == ((GtkWidget *) view)->window)
1351 /* The event is on the Viewport, not the collection... */
1352 y += collection->vadj->value;
1355 i = collection_get_item(collection, x, y);
1356 make_item_iter(view_collection, iter, i);
1359 static void view_collection_cursor_to_iter(ViewIface *view, ViewIter *iter)
1361 ViewCollection *view_collection = VIEW_COLLECTION(view);
1362 Collection *collection = view_collection->collection;
1363 FilerWindow *filer_window = view_collection->filer_window;
1365 g_return_if_fail(iter == NULL ||
1366 iter->view == (ViewIface *) view_collection);
1368 collection_set_cursor_item(collection, iter ? iter->i : -1,
1369 filer_window->auto_scroll == -1);
1372 static void view_collection_set_selected(ViewIface *view,
1373 ViewIter *iter,
1374 gboolean selected)
1376 ViewCollection *view_collection = VIEW_COLLECTION(view);
1377 Collection *collection = view_collection->collection;
1379 g_return_if_fail(iter != NULL &&
1380 iter->view == (ViewIface *) view_collection);
1381 g_return_if_fail(iter->i >= 0 && iter->i < collection->number_of_items);
1383 if (selected)
1384 collection_select_item(collection, iter->i);
1385 else
1386 collection_unselect_item(collection, iter->i);
1389 static gboolean view_collection_get_selected(ViewIface *view, ViewIter *iter)
1391 ViewCollection *view_collection = VIEW_COLLECTION(view);
1392 Collection *collection = view_collection->collection;
1394 g_return_val_if_fail(iter != NULL &&
1395 iter->view == (ViewIface *) view_collection, FALSE);
1396 g_return_val_if_fail(iter->i >= 0 &&
1397 iter->i < collection->number_of_items, FALSE);
1399 return collection->items[iter->i].selected;
1402 static void view_collection_select_only(ViewIface *view, ViewIter *iter)
1404 ViewCollection *view_collection = VIEW_COLLECTION(view);
1405 Collection *collection = view_collection->collection;
1407 g_return_if_fail(iter != NULL &&
1408 iter->view == (ViewIface *) view_collection);
1409 g_return_if_fail(iter->i >= 0 && iter->i < collection->number_of_items);
1411 collection_clear_except(collection, iter->i);
1414 static void view_collection_set_frozen(ViewIface *view, gboolean frozen)
1416 ViewCollection *view_collection = VIEW_COLLECTION(view);
1417 Collection *collection = view_collection->collection;
1419 if (frozen)
1420 collection->block_selection_changed++;
1421 else
1422 collection_unblock_selection_changed(collection,
1423 gtk_get_current_event_time(), TRUE);
1426 static void view_collection_wink_item(ViewIface *view, ViewIter *iter)
1428 ViewCollection *view_collection = VIEW_COLLECTION(view);
1429 Collection *collection = view_collection->collection;
1431 if (!iter)
1433 collection_wink_item(collection, -1);
1434 return;
1437 g_return_if_fail(iter != NULL &&
1438 iter->view == (ViewIface *) view_collection);
1439 g_return_if_fail(iter->i >= 0 && iter->i < collection->number_of_items);
1441 collection_wink_item(collection, iter->i);
1444 static void view_collection_autosize(ViewIface *view)
1446 ViewCollection *view_collection = VIEW_COLLECTION(view);
1447 FilerWindow *filer_window = view_collection->filer_window;
1448 Collection *collection = view_collection->collection;
1449 int n;
1450 int w = collection->item_width;
1451 int h = collection->item_height;
1452 int x;
1453 int rows, cols;
1454 int max_x, max_rows;
1455 const float r = 2.5;
1456 int t = 0;
1457 int space = 0;
1459 /* Get the extra height required for the toolbar and minibuffer,
1460 * if visible.
1462 if (o_toolbar.int_value != TOOLBAR_NONE)
1463 t = filer_window->toolbar->allocation.height;
1464 if (filer_window->message)
1465 t += filer_window->message->allocation.height;
1466 if (GTK_WIDGET_VISIBLE(filer_window->minibuffer_area))
1468 GtkRequisition req;
1470 gtk_widget_size_request(filer_window->minibuffer_area, &req);
1471 space = req.height + 2;
1472 t += space;
1475 n = collection->number_of_items;
1476 if (n == 0)
1477 h = ICON_HEIGHT * 1.5;
1478 n = MAX(n, 2);
1480 max_x = (o_filer_size_limit.int_value * screen_width) / 100;
1481 max_rows = (o_filer_size_limit.int_value * screen_height) / (h * 100);
1483 /* Aim for a size where
1484 * x = r(y + t + h), (1)
1485 * unless that's too wide.
1487 * t = toolbar (and minibuffer) height
1488 * r = desired (width / height) ratio
1490 * Want to display all items:
1491 * (x/w)(y/h) = n
1492 * => xy = nwh
1493 * => x(x/r - t - h) = nwh (from 1)
1494 * => xx - x.rt - hr(1 - nw) = 0
1495 * => 2x = rt +/- sqrt(rt.rt + 4hr(nw - 1))
1496 * Now,
1497 * 4hr(nw - 1) > 0
1498 * so
1499 * sqrt(rt.rt + ...) > rt
1501 * So, the +/- must be +:
1503 * => x = (rt + sqrt(rt.rt + 4hr(nw - 1))) / 2
1505 * ( + w - 1 to round up)
1507 x = (r * t + sqrt(r*r*t*t + 4*h*r * (n*w - 1))) / 2 + w - 1;
1509 /* Limit x */
1510 if (x > max_x)
1511 x = max_x;
1513 cols = x / w;
1514 cols = MAX(cols, 1);
1516 /* Choose rows to display all items given our chosen x.
1517 * Don't make the window *too* big!
1519 rows = (n + cols - 1) / cols;
1520 if (rows > max_rows)
1521 rows = max_rows;
1523 /* Leave some room for extra icons, but only in Small Icons mode
1524 * otherwise it takes up too much space.
1525 * Also, don't add space if the minibuffer is open.
1527 if (space == 0)
1528 space = filer_window->display_style == SMALL_ICONS ? h : 2;
1530 filer_window_set_size(filer_window,
1531 w * MAX(cols, 1),
1532 h * MAX(rows, 1) + space);
1535 static gboolean view_collection_cursor_visible(ViewIface *view)
1537 ViewCollection *view_collection = VIEW_COLLECTION(view);
1538 Collection *collection = view_collection->collection;
1540 return collection->cursor_item != -1;
1543 static void view_collection_set_base(ViewIface *view, ViewIter *iter)
1545 ViewCollection *view_collection = VIEW_COLLECTION(view);
1547 view_collection->cursor_base = iter->i;
1550 static void view_collection_start_lasso_box(ViewIface *view,
1551 GdkEventButton *event)
1553 ViewCollection *view_collection = (ViewCollection *) view;
1554 Collection *collection = view_collection->collection;
1556 filer_set_autoscroll(view_collection->filer_window, TRUE);
1557 collection_lasso_box(collection, event->x, event->y);
1561 /* Change the adjustment by this amount. Bounded. */
1562 static void diff_vpos(Collection *collection, int diff)
1564 int old = collection->vadj->value;
1565 int value = old + diff;
1567 value = CLAMP(value, 0,
1568 collection->vadj->upper - collection->vadj->page_size);
1569 gtk_adjustment_set_value(collection->vadj, value);
1571 if (collection->vadj->value != old)
1572 dnd_spring_abort();
1575 static gboolean view_collection_auto_scroll_callback(ViewIface *view)
1577 ViewCollection *view_collection = (ViewCollection *) view;
1578 Collection *collection = view_collection->collection;
1579 GdkWindow *window = ((GtkWidget *) collection)->window;
1580 gint x, y, w, h;
1581 GdkModifierType mask;
1582 int diff = 0;
1584 gdk_window_get_pointer(window, &x, &y, &mask);
1585 gdk_drawable_get_size(window, &w, NULL);
1587 h = collection->vadj->page_size;
1588 y -= collection->vadj->value;
1590 if ((x < 0 || x > w || y < 0 || y > h) && !collection->lasso_box)
1591 return FALSE; /* Out of window - stop */
1593 if (y < AUTOSCROLL_STEP)
1594 diff = y - AUTOSCROLL_STEP;
1595 else if (y > h - AUTOSCROLL_STEP)
1596 diff = AUTOSCROLL_STEP + y - h;
1598 if (diff)
1599 diff_vpos(collection, diff);
1601 return TRUE;