1 /*
2  * ROX-Filer, filer for the ROX desktop project
3  * Copyright (C) 2006, Thomas Leonard and others (see changelog for details).
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the Free
7  * Software Foundation; either version 2 of the License, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along with
16  * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
17  * Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19 
20 /* view_collection.c - a subclass of Collection, used for displaying files */
21 
22 #include "config.h"
23 
24 #include <gtk/gtk.h>
25 #include <time.h>
26 #include <math.h>
27 #include <string.h>
28 
29 #include "global.h"
30 
31 #include "collection.h"
32 #include "view_iface.h"
33 #include "view_collection.h"
34 #include "type.h"
35 #include "pixmaps.h"
36 #include "dir.h"
37 #include "diritem.h"
38 #include "gui_support.h"
39 #include "support.h"
40 #include "dnd.h"
41 #include "bind.h"
42 #include "options.h"
43 
44 #include "toolbar.h"	/* for resizing */
45 #include "filer.h"
46 #include "display.h"
47 
48 #define MIN_ITEM_WIDTH 64
49 
50 static gpointer parent_class = NULL;
51 
52 struct _ViewCollectionClass {
53 	GtkViewportClass parent;
54 };
55 
56 struct _ViewCollection {
57 	GtkViewport viewport;
58 
59 	Collection *collection;
60 	FilerWindow *filer_window;	/* Used for styles, etc */
61 
62 	int	cursor_base;		/* Cursor when minibuffer opened */
63 };
64 
65 typedef struct _Template Template;
66 
67 struct _Template {
68 	GdkRectangle	icon;
69 	GdkRectangle	leafname;
70 	GdkRectangle	details;
71 };
72 
73 /* GC for drawing colour filenames */
74 static GdkGC	*type_gc = NULL;
75 
76 /* Static prototypes */
77 static void view_collection_finialize(GObject *object);
78 static void view_collection_class_init(gpointer gclass, gpointer data);
79 static void view_collection_init(GTypeInstance *object, gpointer gclass);
80 
81 static void draw_item(GtkWidget *widget,
82 			CollectionItem *item,
83 			GdkRectangle *area,
84 			gpointer user_data);
85 static void fill_template(GdkRectangle *area, CollectionItem *item,
86 			ViewCollection *view_collection, Template *template);
87 static void huge_template(GdkRectangle *area, CollectionItem *colitem,
88 			   ViewCollection *view_collection, Template *template);
89 static void large_template(GdkRectangle *area, CollectionItem *colitem,
90 			   ViewCollection *view_collection, Template *template);
91 static void small_template(GdkRectangle *area, CollectionItem *colitem,
92 			   ViewCollection *view_collection, Template *template);
93 static void huge_full_template(GdkRectangle *area, CollectionItem *colitem,
94 			   ViewCollection *view_collection, Template *template);
95 static void large_full_template(GdkRectangle *area, CollectionItem *colitem,
96 			   ViewCollection *view_collection, Template *template);
97 static void small_full_template(GdkRectangle *area, CollectionItem *colitem,
98 			   ViewCollection *view_collection, Template *template);
99 static gboolean test_point(Collection *collection,
100 				int point_x, int point_y,
101 				CollectionItem *item,
102 				int width, int height,
103 				gpointer user_data);
104 static void draw_string(GtkWidget *widget,
105 		PangoLayout *layout,
106 		GdkRectangle *area,	/* Area available on screen */
107 		int 	width,		/* Width of the full string */
108 		GtkStateType selection_state,
109 		gboolean box);
110 static void view_collection_iface_init(gpointer giface, gpointer iface_data);
111 static gint coll_motion_notify(GtkWidget *widget,
112 			       GdkEventMotion *event,
113 			       ViewCollection *view_collection);
114 static gint coll_button_release(GtkWidget *widget,
115 			        GdkEventButton *event,
116 			        ViewCollection *view_collection);
117 static gint coll_button_press(GtkWidget *widget,
118 			      GdkEventButton *event,
119 			      ViewCollection *view_collection);
120 static void size_allocate(GtkWidget *w, GtkAllocation *a, gpointer data);
121 static void style_set(Collection 	*collection,
122 		      GtkStyle		*style,
123 		      ViewCollection	*view_collection);
124 static void display_free_colitem(Collection *collection,
125 				 CollectionItem *colitem);
126 static void lost_selection(Collection  *collection,
127 			   guint        time,
128 			   gpointer     user_data);
129 static void selection_changed(Collection *collection,
130 			      gint time,
131 			      gpointer user_data);
132 static void calc_size(FilerWindow *filer_window, CollectionItem *colitem,
133 		int *width, int *height);
134 static void make_iter(ViewCollection *view_collection, ViewIter *iter,
135 		      IterFlags flags);
136 static void make_item_iter(ViewCollection *vc, ViewIter *iter, int i);
137 
138 static void view_collection_sort(ViewIface *view);
139 static void view_collection_style_changed(ViewIface *view, int flags);
140 static void view_collection_add_items(ViewIface *view, GPtrArray *items);
141 static void view_collection_update_items(ViewIface *view, GPtrArray *items);
142 static void view_collection_delete_if(ViewIface *view,
143 			  gboolean (*test)(gpointer item, gpointer data),
144 			  gpointer data);
145 static void view_collection_clear(ViewIface *view);
146 static void view_collection_select_all(ViewIface *view);
147 static void view_collection_clear_selection(ViewIface *view);
148 static int view_collection_count_items(ViewIface *view);
149 static int view_collection_count_selected(ViewIface *view);
150 static void view_collection_show_cursor(ViewIface *view);
151 static void view_collection_get_iter(ViewIface *view,
152 				     ViewIter *iter, IterFlags flags);
153 static void view_collection_get_iter_at_point(ViewIface *view, ViewIter *iter,
154 					      GdkWindow *src, int x, int y);
155 static void view_collection_cursor_to_iter(ViewIface *view, ViewIter *iter);
156 static void view_collection_set_selected(ViewIface *view,
157 					 ViewIter *iter,
158 					 gboolean selected);
159 static gboolean view_collection_get_selected(ViewIface *view, ViewIter *iter);
160 static void view_collection_select_only(ViewIface *view, ViewIter *iter);
161 static void view_collection_set_frozen(ViewIface *view, gboolean frozen);
162 static void view_collection_wink_item(ViewIface *view, ViewIter *iter);
163 static void view_collection_autosize(ViewIface *view);
164 static gboolean view_collection_cursor_visible(ViewIface *view);
165 static void view_collection_set_base(ViewIface *view, ViewIter *iter);
166 static void view_collection_start_lasso_box(ViewIface *view,
167 					     GdkEventButton *event);
168 static void view_collection_extend_tip(ViewIface *view, ViewIter *iter,
169 					GString *tip);
170 static gboolean view_collection_auto_scroll_callback(ViewIface *view);
171 
172 static DirItem *iter_next(ViewIter *iter);
173 static DirItem *iter_prev(ViewIter *iter);
174 static DirItem *iter_peek(ViewIter *iter);
175 
176 
177 /****************************************************************
178  *			EXTERNAL INTERFACE			*
179  ****************************************************************/
180 
view_collection_new(FilerWindow * filer_window)181 GtkWidget *view_collection_new(FilerWindow *filer_window)
182 {
183 	ViewCollection *view_collection;
184 
185 	view_collection = g_object_new(view_collection_get_type(), NULL);
186 	view_collection->filer_window = filer_window;
187 
188 	/* Starting with GTK+-2.2.2, the vadjustment is reset after init
189 	 * (even though it's already set during init) to a new adjustment.
190 	 * Change it back:
191 	 */
192 	gtk_viewport_set_vadjustment(GTK_VIEWPORT(view_collection),
193 				 view_collection->collection->vadj);
194 
195 	gtk_range_set_adjustment(GTK_RANGE(filer_window->scrollbar),
196 				 view_collection->collection->vadj);
197 
198 	return GTK_WIDGET(view_collection);
199 }
200 
view_collection_get_type(void)201 GType view_collection_get_type(void)
202 {
203 	static GType type = 0;
204 
205 	if (!type)
206 	{
207 		static const GTypeInfo info =
208 		{
209 			sizeof (ViewCollectionClass),
210 			NULL,			/* base_init */
211 			NULL,			/* base_finalise */
212 			view_collection_class_init,
213 			NULL,			/* class_finalise */
214 			NULL,			/* class_data */
215 			sizeof(ViewCollection),
216 			0,			/* n_preallocs */
217 			view_collection_init
218 		};
219 		static const GInterfaceInfo iface_info =
220 		{
221 			view_collection_iface_init, NULL, NULL
222 		};
223 
224 		type = g_type_register_static(gtk_viewport_get_type(),
225 						"ViewCollection", &info, 0);
226 		g_type_add_interface_static(type, VIEW_TYPE_IFACE, &iface_info);
227 	}
228 
229 	return type;
230 }
231 
232 /****************************************************************
233  *			INTERNAL FUNCTIONS			*
234  ****************************************************************/
235 
view_collection_destroy(GtkObject * view_collection)236 static void view_collection_destroy(GtkObject *view_collection)
237 {
238 	VIEW_COLLECTION(view_collection)->filer_window = NULL;
239 }
240 
view_collection_finialize(GObject * object)241 static void view_collection_finialize(GObject *object)
242 {
243 	/* ViewCollection *view_collection = (ViewCollection *) object; */
244 
245 	G_OBJECT_CLASS(parent_class)->finalize(object);
246 }
247 
view_collection_grab_focus(GtkWidget * focus_widget)248 static void view_collection_grab_focus(GtkWidget *focus_widget)
249 {
250 	ViewCollection *view_collection = VIEW_COLLECTION(focus_widget);
251 	gtk_widget_grab_focus(GTK_WIDGET(view_collection->collection));
252 }
253 
view_collection_class_init(gpointer gclass,gpointer data)254 static void view_collection_class_init(gpointer gclass, gpointer data)
255 {
256 	GObjectClass *object = (GObjectClass *) gclass;
257 	GtkWidgetClass *widget = (GtkWidgetClass *) gclass;
258 
259 	parent_class = g_type_class_peek_parent(gclass);
260 
261 	widget->grab_focus = view_collection_grab_focus;
262 
263 	object->finalize = view_collection_finialize;
264 	GTK_OBJECT_CLASS(object)->destroy = view_collection_destroy;
265 }
266 
view_collection_init(GTypeInstance * object,gpointer gclass)267 static void view_collection_init(GTypeInstance *object, gpointer gclass)
268 {
269 	ViewCollection *view_collection = (ViewCollection *) object;
270 	GtkViewport *viewport = (GtkViewport *) object;
271 	GtkWidget *collection;
272 	GtkAdjustment *adj;
273 
274 	collection = collection_new();
275 	view_collection->collection = COLLECTION(collection);
276 
277 	adj = view_collection->collection->vadj;
278 	gtk_viewport_set_vadjustment(viewport, adj);
279 	gtk_viewport_set_hadjustment(viewport, NULL); /* Or Gtk will crash */
280 	gtk_viewport_set_shadow_type(viewport, GTK_SHADOW_NONE);
281 	gtk_container_add(GTK_CONTAINER(object), collection);
282 	gtk_widget_show(collection);
283 	gtk_widget_set_size_request(GTK_WIDGET(view_collection), 4, 4);
284 
285 	gtk_container_set_resize_mode(GTK_CONTAINER(viewport),
286 			GTK_RESIZE_IMMEDIATE);
287 
288 	view_collection->collection->free_item = display_free_colitem;
289 	view_collection->collection->draw_item = draw_item;
290 	view_collection->collection->test_point = test_point;
291 	view_collection->collection->cb_user_data = view_collection;
292 
293 	g_signal_connect(collection, "style_set",
294 			G_CALLBACK(style_set),
295 			view_collection);
296 
297 	g_signal_connect(collection, "lose_selection",
298 			G_CALLBACK(lost_selection), view_collection);
299 	g_signal_connect(collection, "selection_changed",
300 			G_CALLBACK(selection_changed), view_collection);
301 
302 	g_signal_connect(collection, "button-release-event",
303 			G_CALLBACK(coll_button_release), view_collection);
304 	g_signal_connect(collection, "button-press-event",
305 			G_CALLBACK(coll_button_press), view_collection);
306 	g_signal_connect(collection, "motion-notify-event",
307 			G_CALLBACK(coll_motion_notify), view_collection);
308 	g_signal_connect(viewport, "size-allocate",
309 			G_CALLBACK(size_allocate), view_collection);
310 
311 	gtk_widget_set_events(collection,
312 			GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
313 			GDK_BUTTON3_MOTION_MASK | GDK_POINTER_MOTION_MASK |
314 			GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
315 }
316 
317 
draw_item(GtkWidget * widget,CollectionItem * colitem,GdkRectangle * area,gpointer user_data)318 static void draw_item(GtkWidget *widget,
319 			CollectionItem *colitem,
320 			GdkRectangle *area,
321 			gpointer user_data)
322 {
323 	DirItem		*item = (DirItem *) colitem->data;
324 	gboolean	selected = colitem->selected;
325 	Template	template;
326 	ViewData *view = (ViewData *) colitem->view_data;
327 	ViewCollection	*view_collection = (ViewCollection *) user_data;
328 	FilerWindow	*filer_window = view_collection->filer_window;
329 	GtkStateType	selection_state;
330 	GdkColor	*color;
331 
332 	g_return_if_fail(view != NULL);
333 
334 	if (selected)
335 		selection_state = filer_window->selection_state;
336 	else
337 		selection_state = GTK_STATE_NORMAL;
338 
339 	color = &widget->style->base[selection_state];
340 
341 	fill_template(area, colitem, view_collection, &template);
342 
343 	/* Set up GC for coloured file types */
344 	if (!type_gc)
345 		type_gc = gdk_gc_new(widget->window);
346 
347 	gdk_gc_set_foreground(type_gc, type_get_colour(item,
348 					&widget->style->text[GTK_STATE_NORMAL]));
349 
350 	if (template.icon.width <= SMALL_WIDTH &&
351 			template.icon.height <= SMALL_HEIGHT)
352 	{
353 		draw_small_icon(widget->window, widget->style, &template.icon,
354 				item, view->image, selected, color);
355 	}
356 	else if (template.icon.width <= ICON_WIDTH &&
357 			template.icon.height <= ICON_HEIGHT)
358 	{
359 		draw_large_icon(widget->window, widget->style, &template.icon,
360 				item, view->image, selected, color);
361 	}
362 	else
363 	{
364 		draw_huge_icon(widget->window, widget->style, &template.icon,
365 				item, view->image, selected, color);
366 	}
367 
368 	draw_string(widget, view->layout,
369 			&template.leafname,
370 			view->name_width,
371 			selection_state,
372 			TRUE);
373 	if (view->details)
374 		draw_string(widget, view->details,
375 				&template.details,
376 				template.details.width,
377 				selection_state,
378 				TRUE);
379 }
380 
381 /* A template contains the locations of the three rectangles (for the icon,
382  * name and extra details).
383  * Fill in the empty 'template' with the rectanges for this item.
384  */
fill_template(GdkRectangle * area,CollectionItem * colitem,ViewCollection * view_collection,Template * template)385 static void fill_template(GdkRectangle *area, CollectionItem *colitem,
386 			ViewCollection *view_collection, Template *template)
387 {
388 	DisplayStyle	style = view_collection->filer_window->display_style;
389 	ViewData 	*view = (ViewData *) colitem->view_data;
390 
391 	if (view->details)
392 	{
393 		template->details.width = view->details_width;
394 		template->details.height = view->details_height;
395 
396 		if (style == SMALL_ICONS)
397 			small_full_template(area, colitem,
398 						view_collection, template);
399 		else if (style == LARGE_ICONS)
400 			large_full_template(area, colitem,
401 						view_collection, template);
402 		else
403 			huge_full_template(area, colitem,
404 						view_collection, template);
405 	}
406 	else
407 	{
408 		if (style == HUGE_ICONS)
409 			huge_template(area, colitem,
410 					view_collection, template);
411 		else if (style == LARGE_ICONS)
412 			large_template(area, colitem,
413 					view_collection, template);
414 		else
415 			small_template(area, colitem,
416 					view_collection, template);
417 	}
418 }
419 
huge_template(GdkRectangle * area,CollectionItem * colitem,ViewCollection * view_collection,Template * template)420 static void huge_template(GdkRectangle *area, CollectionItem *colitem,
421 			   ViewCollection *view_collection, Template *template)
422 {
423 	int	col_width = view_collection->collection->item_width;
424 	int		text_x, text_y;
425 	ViewData	*view = (ViewData *) colitem->view_data;
426 	MaskedPixmap	*image = view->image;
427 
428 	if (image)
429 	{
430 		if (!image->huge_pixbuf)
431 			pixmap_make_huge(image);
432 		template->icon.width = image->huge_width;
433 		template->icon.height = image->huge_height;
434 	}
435 	else
436 	{
437 		template->icon.width = HUGE_WIDTH * 3 / 2;
438 		template->icon.height = HUGE_HEIGHT;
439 	}
440 
441 	template->leafname.width = view->name_width;
442 	template->leafname.height = view->name_height;
443 
444 	text_x = area->x + ((col_width - template->leafname.width) >> 1);
445 	text_y = area->y + area->height - template->leafname.height;
446 
447 	template->leafname.x = text_x;
448 	template->leafname.y = text_y;
449 
450 	template->icon.x = area->x + ((col_width - template->icon.width) >> 1);
451 	template->icon.y = template->leafname.y - template->icon.height - 2;
452 }
453 
large_template(GdkRectangle * area,CollectionItem * colitem,ViewCollection * view_collection,Template * template)454 static void large_template(GdkRectangle *area, CollectionItem *colitem,
455 			   ViewCollection *view_collection, Template *template)
456 {
457 	int	col_width = view_collection->collection->item_width;
458 	int		iwidth, iheight;
459 	int		image_x;
460 	int		image_y;
461 	ViewData	*view = (ViewData *) colitem->view_data;
462 	MaskedPixmap	*image = view->image;
463 
464 	int		text_x, text_y;
465 
466 	if (image)
467 	{
468 		iwidth = MIN(image->width, ICON_WIDTH);
469 		iheight = MIN(image->height + 6, ICON_HEIGHT);
470 	}
471 	else
472 	{
473 		iwidth = ICON_WIDTH;
474 		iheight = ICON_HEIGHT;
475 	}
476 	image_x = area->x + ((col_width - iwidth) >> 1);
477 
478 	template->leafname.width = view->name_width;
479 	template->leafname.height = view->name_height;
480 
481 	text_x = area->x + ((col_width - template->leafname.width) >> 1);
482 	text_y = area->y + ICON_HEIGHT + 2;
483 
484 	template->leafname.x = text_x;
485 	template->leafname.y = text_y;
486 
487 	image_y = text_y - iheight;
488 	image_y = MAX(area->y, image_y);
489 
490 	template->icon.x = image_x;
491 	template->icon.y = image_y;
492 	template->icon.width = iwidth;
493 	template->icon.height = MIN(ICON_HEIGHT, iheight);
494 }
495 
small_template(GdkRectangle * area,CollectionItem * colitem,ViewCollection * view_collection,Template * template)496 static void small_template(GdkRectangle *area, CollectionItem *colitem,
497 			   ViewCollection *view_collection, Template *template)
498 {
499 	int	text_x = area->x + SMALL_WIDTH + 4;
500 	int	low_text_y;
501 	int	max_text_width = area->width - SMALL_WIDTH - 4;
502 	ViewData *view = (ViewData *) colitem->view_data;
503 
504 	low_text_y = area->y + area->height / 2 - view->name_height / 2;
505 
506 	template->leafname.x = text_x;
507 	template->leafname.y = low_text_y;
508 	template->leafname.width = MIN(max_text_width, view->name_width);
509 	template->leafname.height = view->name_height;
510 
511 	template->icon.x = area->x;
512 	template->icon.y = area->y + 1;
513 	template->icon.width = SMALL_WIDTH;
514 	template->icon.height = SMALL_HEIGHT;
515 }
516 
huge_full_template(GdkRectangle * area,CollectionItem * colitem,ViewCollection * view_collection,Template * template)517 static void huge_full_template(GdkRectangle *area, CollectionItem *colitem,
518 			   ViewCollection *view_collection, Template *template)
519 {
520 	int	max_text_width = area->width - HUGE_WIDTH - 4;
521 	ViewData *view = (ViewData *) colitem->view_data;
522 	MaskedPixmap	*image = view->image;
523 
524 	if (image)
525 	{
526 		if (!image->huge_pixbuf)
527 			pixmap_make_huge(image);
528 		template->icon.width = image->huge_width;
529 		template->icon.height = image->huge_height;
530 	}
531 	else
532 	{
533 		template->icon.width = HUGE_WIDTH * 3 / 2;
534 		template->icon.height = HUGE_HEIGHT;
535 	}
536 
537 	template->icon.x = area->x + (HUGE_WIDTH - template->icon.width) / 2;
538 	template->icon.y = area->y + (area->height - template->icon.height) / 2;
539 
540 	template->leafname.x = area->x + HUGE_WIDTH + 4;
541 	template->leafname.y = area->y + area->height / 2
542 			- (view->name_height + 2 + view->details_height) / 2;
543 	template->leafname.width = MIN(max_text_width, view->name_width);
544 	template->leafname.height = view->name_height;
545 
546 	if (!image)
547 		return;		/* Not scanned yet */
548 
549 	template->details.x = template->leafname.x;
550 	template->details.y = template->leafname.y + view->name_height + 2;
551 }
552 
large_full_template(GdkRectangle * area,CollectionItem * colitem,ViewCollection * view_collection,Template * template)553 static void large_full_template(GdkRectangle *area, CollectionItem *colitem,
554 			   ViewCollection *view_collection, Template *template)
555 {
556 	int	max_text_width = area->width - ICON_WIDTH - 4;
557 	ViewData *view = (ViewData *) colitem->view_data;
558 	MaskedPixmap *image = view->image;
559 
560 	if (image)
561 	{
562 		template->icon.width = image->width;
563 		template->icon.height = image->height;
564 	}
565 	else
566 	{
567 		template->icon.width = ICON_WIDTH;
568 		template->icon.height = ICON_HEIGHT;
569 	}
570 
571 	template->icon.x = area->x + (ICON_WIDTH - template->icon.width) / 2;
572 	template->icon.y = area->y + (area->height - template->icon.height) / 2;
573 
574 
575 	template->leafname.x = area->x + ICON_WIDTH + 4;
576 	template->leafname.y = area->y + area->height / 2
577 			- (view->name_height + 2 + view->details_height) / 2;
578 	template->leafname.width = MIN(max_text_width, view->name_width);
579 	template->leafname.height = view->name_height;
580 
581 	if (!image)
582 		return;		/* Not scanned yet */
583 
584 	template->details.x = template->leafname.x;
585 	template->details.y = template->leafname.y + view->name_height + 2;
586 }
587 
small_full_template(GdkRectangle * area,CollectionItem * colitem,ViewCollection * view_collection,Template * template)588 static void small_full_template(GdkRectangle *area, CollectionItem *colitem,
589 			   ViewCollection *view_collection, Template *template)
590 {
591 	int	col_width = view_collection->collection->item_width;
592 	ViewData *view = (ViewData *) colitem->view_data;
593 
594 	small_template(area, colitem, view_collection, template);
595 
596 	if (!view->image)
597 		return;		/* Not scanned yet */
598 
599 	template->details.x = area->x + col_width - template->details.width;
600 	template->details.y = area->y + area->height / 2 - \
601 				view->details_height / 2;
602 }
603 
604 #define INSIDE(px, py, area)	\
605 	(px >= area.x && py >= area.y && \
606 	 px <= area.x + area.width && py <= area.y + area.height)
607 
test_point(Collection * collection,int point_x,int point_y,CollectionItem * colitem,int width,int height,gpointer user_data)608 static gboolean test_point(Collection *collection,
609 				int point_x, int point_y,
610 				CollectionItem *colitem,
611 				int width, int height,
612 				gpointer user_data)
613 {
614 	Template	template;
615 	GdkRectangle	area;
616 	ViewData	*view = (ViewData *) colitem->view_data;
617 	ViewCollection	*view_collection = (ViewCollection *) user_data;
618 
619 	area.x = 0;
620 	area.y = 0;
621 	area.width = width;
622 	area.height = height;
623 
624 	fill_template(&area, colitem, view_collection, &template);
625 
626 	return INSIDE(point_x, point_y, template.leafname) ||
627 	       INSIDE(point_x, point_y, template.icon) ||
628 	       (view->details && INSIDE(point_x, point_y, template.details));
629 }
630 
631 /* 'box' renders a background box if the string is also selected */
draw_string(GtkWidget * widget,PangoLayout * layout,GdkRectangle * area,int width,GtkStateType selection_state,gboolean box)632 static void draw_string(GtkWidget *widget,
633 		PangoLayout *layout,
634 		GdkRectangle *area,	/* Area available on screen */
635 		int 	width,		/* Width of the full string */
636 		GtkStateType selection_state,
637 		gboolean box)
638 {
639 	GdkGC	*gc = selection_state == GTK_STATE_NORMAL
640 			? type_gc
641 			: widget->style->text_gc[selection_state];
642 
643 	if (selection_state != GTK_STATE_NORMAL && box)
644 		gtk_paint_flat_box(widget->style, widget->window,
645 				selection_state, GTK_SHADOW_NONE,
646 				NULL, widget, "text",
647 				area->x, area->y,
648 				MIN(width, area->width),
649 				area->height);
650 
651 	if (width > area->width)
652 	{
653 		gdk_gc_set_clip_origin(gc, 0, 0);
654 		gdk_gc_set_clip_rectangle(gc, area);
655 	}
656 
657 	gdk_draw_layout(widget->window, gc, area->x, area->y, layout);
658 
659 	if (width > area->width)
660 	{
661 		static GdkGC *red_gc = NULL;
662 
663 		if (!red_gc)
664 		{
665 			gboolean success;
666 			GdkColor red = {0, 0xffff, 0, 0};
667 
668 			red_gc = gdk_gc_new(widget->window);
669 			gdk_colormap_alloc_colors(
670 					gtk_widget_get_colormap(widget),
671 					&red, 1, FALSE, TRUE, &success);
672 			gdk_gc_set_foreground(red_gc, &red);
673 		}
674 		gdk_draw_rectangle(widget->window, red_gc, TRUE,
675 				area->x + area->width - 1, area->y,
676 				1, area->height);
677 		gdk_gc_set_clip_rectangle(gc, NULL);
678 	}
679 }
680 
681 /* Create the handers for the View interface */
view_collection_iface_init(gpointer giface,gpointer iface_data)682 static void view_collection_iface_init(gpointer giface, gpointer iface_data)
683 {
684 	ViewIfaceClass *iface = giface;
685 
686 	g_assert(G_TYPE_FROM_INTERFACE(iface) == VIEW_TYPE_IFACE);
687 
688 	/* override stuff */
689 	iface->sort = view_collection_sort;
690 	iface->style_changed = view_collection_style_changed;
691 	iface->add_items = view_collection_add_items;
692 	iface->update_items = view_collection_update_items;
693 	iface->delete_if = view_collection_delete_if;
694 	iface->clear = view_collection_clear;
695 	iface->select_all = view_collection_select_all;
696 	iface->clear_selection = view_collection_clear_selection;
697 	iface->count_items = view_collection_count_items;
698 	iface->count_selected = view_collection_count_selected;
699 	iface->show_cursor = view_collection_show_cursor;
700 	iface->get_iter = view_collection_get_iter;
701 	iface->get_iter_at_point = view_collection_get_iter_at_point;
702 	iface->cursor_to_iter = view_collection_cursor_to_iter;
703 	iface->set_selected = view_collection_set_selected;
704 	iface->get_selected = view_collection_get_selected;
705 	iface->set_frozen = view_collection_set_frozen;
706 	iface->select_only = view_collection_select_only;
707 	iface->wink_item = view_collection_wink_item;
708 	iface->autosize = view_collection_autosize;
709 	iface->cursor_visible = view_collection_cursor_visible;
710 	iface->set_base = view_collection_set_base;
711 	iface->start_lasso_box = view_collection_start_lasso_box;
712 	iface->extend_tip = view_collection_extend_tip;
713 	iface->auto_scroll_callback = view_collection_auto_scroll_callback;
714 }
715 
view_collection_extend_tip(ViewIface * view,ViewIter * iter,GString * tip)716 static void view_collection_extend_tip(ViewIface *view, ViewIter *iter,
717 					GString *tip)
718 {
719 	ViewCollection*view_collection = (ViewCollection *) view;
720 	Collection *collection = view_collection->collection;
721 	FilerWindow *filer_window = view_collection->filer_window;
722 	Template template;
723 	int i = iter->i;
724 	CollectionItem	*colitem = &collection->items[i];
725 	ViewData *view_data = (ViewData *) colitem->view_data;
726 	GdkRectangle area;
727 	int row,col;
728 
729 	collection_item_to_rowcol(collection, i, &row, &col);
730 
731 	g_return_if_fail(iter->view == (ViewIface *) view_collection);
732 	g_return_if_fail(i >= 0 && i < collection->number_of_items);
733 
734 	/* TODO: What if the window is narrower than 1 column? */
735 	if (filer_window->display_style == LARGE_ICONS ||
736 	    filer_window->display_style == HUGE_ICONS)
737 		return;		/* These wrap rather than truncate */
738 
739 	area.x = col * collection->item_width;
740 	area.y = row * collection->item_height;
741 	area.height = collection->item_height;
742 
743 	if (col == collection->columns - 1)
744 		area.width = GTK_WIDGET(collection)->allocation.width - area.x;
745 	else
746 		area.width = collection->item_width;
747 
748 	fill_template(&area, colitem, view_collection, &template);
749 
750 	if (template.leafname.width < view_data->name_width)
751 	{
752 		DirItem *item = (DirItem *) collection->items[i].data;
753 
754 		g_string_append(tip, item->leafname);
755 		g_string_append_c(tip, '\n');
756 	}
757 }
758 
coll_motion_notify(GtkWidget * widget,GdkEventMotion * event,ViewCollection * view_collection)759 static gint coll_motion_notify(GtkWidget *widget,
760 			       GdkEventMotion *event,
761 			       ViewCollection *view_collection)
762 {
763 	return filer_motion_notify(view_collection->filer_window, event);
764 }
765 
766 /* Viewport is to be resized, so calculate increments */
size_allocate(GtkWidget * w,GtkAllocation * a,gpointer data)767 static void size_allocate(GtkWidget *w, GtkAllocation *a, gpointer data)
768 {
769 	Collection *col = ((ViewCollection *) data)->collection;
770 
771 	col->vadj->step_increment = col->item_height;
772 	col->vadj->page_increment = col->vadj->page_size;
773 }
774 
coll_button_release(GtkWidget * widget,GdkEventButton * event,ViewCollection * view_collection)775 static gint coll_button_release(GtkWidget *widget,
776 			        GdkEventButton *event,
777 				ViewCollection *view_collection)
778 {
779 	if (dnd_motion_release(event))
780 	{
781 		if (motion_buttons_pressed == 0 &&
782 					view_collection->collection->lasso_box)
783 		{
784 			filer_set_autoscroll(view_collection->filer_window,
785 					     FALSE);
786 			collection_end_lasso(view_collection->collection,
787 				event->button == 1 ? GDK_SET : GDK_INVERT);
788 		}
789 		return FALSE;
790 	}
791 
792 	filer_perform_action(view_collection->filer_window, event);
793 
794 	return FALSE;
795 }
796 
coll_button_press(GtkWidget * widget,GdkEventButton * event,ViewCollection * view_collection)797 static gint coll_button_press(GtkWidget *widget,
798 			      GdkEventButton *event,
799 			      ViewCollection *view_collection)
800 {
801 	collection_set_cursor_item(view_collection->collection, -1, TRUE);
802 
803 	if (dnd_motion_press(widget, event))
804 		filer_perform_action(view_collection->filer_window, event);
805 
806 	return FALSE;
807 }
808 
809 /* Nothing is selected anymore - give up primary */
lost_selection(Collection * collection,guint time,gpointer user_data)810 static void lost_selection(Collection  *collection,
811 			   guint        time,
812 			   gpointer     user_data)
813 {
814 	ViewCollection *view_collection = VIEW_COLLECTION(user_data);
815 
816 	filer_lost_selection(view_collection->filer_window, time);
817 }
818 
selection_changed(Collection * collection,gint time,gpointer user_data)819 static void selection_changed(Collection *collection,
820 			      gint time,
821 			      gpointer user_data)
822 {
823 	ViewCollection *view_collection = VIEW_COLLECTION(user_data);
824 
825 	filer_selection_changed(view_collection->filer_window, time);
826 }
827 
display_free_colitem(Collection * collection,CollectionItem * colitem)828 static void display_free_colitem(Collection *collection,
829 				 CollectionItem *colitem)
830 {
831 	ViewData	*view = (ViewData *) colitem->view_data;
832 
833 	if (!view)
834 		return;
835 
836 	if (view->layout)
837 	{
838 		g_object_unref(G_OBJECT(view->layout));
839 		view->layout = NULL;
840 	}
841 	if (view->details)
842 		g_object_unref(G_OBJECT(view->details));
843 
844 	if (view->image)
845 		g_object_unref(view->image);
846 
847 	g_free(view);
848 }
849 
add_item(ViewCollection * view_collection,DirItem * item)850 static void add_item(ViewCollection *view_collection, DirItem *item)
851 {
852 	Collection *collection = view_collection->collection;
853 	FilerWindow	*filer_window = view_collection->filer_window;
854 	int		old_w = collection->item_width;
855 	int		old_h = collection->item_height;
856 	int		w, h, i;
857 
858 	i = collection_insert(collection, item,
859 				display_create_viewdata(filer_window, item));
860 
861 	calc_size(filer_window, &collection->items[i], &w, &h);
862 
863 	if (w > old_w || h > old_h)
864 		collection_set_item_size(collection,
865 					 MAX(old_w, w),
866 					 MAX(old_h, h));
867 }
868 
style_set(Collection * collection,GtkStyle * style,ViewCollection * view_collection)869 static void style_set(Collection 	*collection,
870 		      GtkStyle		*style,
871 		      ViewCollection	*view_collection)
872 {
873 	view_collection_style_changed(VIEW(view_collection),
874 			VIEW_UPDATE_VIEWDATA | VIEW_UPDATE_NAME);
875 }
876 
877 /* Return the size needed for this item */
calc_size(FilerWindow * filer_window,CollectionItem * colitem,int * width,int * height)878 static void calc_size(FilerWindow *filer_window, CollectionItem *colitem,
879 		int *width, int *height)
880 {
881 	int		pix_width, pix_height;
882 	int		w;
883 	DisplayStyle	style = filer_window->display_style;
884 	ViewData	*view = (ViewData *) colitem->view_data;
885 
886 	if (filer_window->details_type == DETAILS_NONE)
887 	{
888                 if (style == HUGE_ICONS)
889 		{
890 			if (view->image)
891 			{
892 				if (!view->image->huge_pixbuf)
893 					pixmap_make_huge(view->image);
894 				pix_width = view->image->huge_width;
895 				pix_height = view->image->huge_height;
896 			}
897 			else
898 			{
899 				pix_width = HUGE_WIDTH * 3 / 2;
900 				pix_height = HUGE_HEIGHT * 3 / 2;
901 			}
902 			*width = MAX(pix_width, view->name_width) + 4;
903 			*height = MAX(view->name_height + pix_height + 4,
904 					HUGE_HEIGHT * 3 / 4);
905 		}
906 		else if (style == SMALL_ICONS)
907 		{
908 			w = MIN(view->name_width, o_small_width.int_value);
909 			*width = SMALL_WIDTH + 12 + w;
910 			*height = MAX(view->name_height, SMALL_HEIGHT) + 4;
911 		}
912 		else
913 		{
914 			if (view->image)
915 				pix_width = view->image->width;
916 			else
917 				pix_width = ICON_WIDTH;
918 			*width = MAX(pix_width, view->name_width) + 4;
919 			*height = view->name_height + ICON_HEIGHT + 2;
920 		}
921 	}
922 	else
923 	{
924 		w = view->details_width;
925 		if (style == HUGE_ICONS)
926 		{
927 			*width = HUGE_WIDTH + 12 + MAX(w, view->name_width);
928 			*height = HUGE_HEIGHT - 4;
929 		}
930 		else if (style == SMALL_ICONS)
931 		{
932 			int	text_height;
933 
934 			*width = SMALL_WIDTH + view->name_width + 12 + w;
935 			text_height = MAX(view->name_height,
936 					  view->details_height);
937 			*height = MAX(text_height, SMALL_HEIGHT) + 4;
938 		}
939 		else
940 		{
941                         *width = ICON_WIDTH + 12 + MAX(w, view->name_width);
942 			*height = ICON_HEIGHT;
943 		}
944         }
945 }
946 
update_item(ViewCollection * view_collection,int i)947 static void update_item(ViewCollection *view_collection, int i)
948 {
949 	Collection *collection = view_collection->collection;
950 	int	old_w = collection->item_width;
951 	int	old_h = collection->item_height;
952 	int	w, h;
953 	CollectionItem *colitem;
954 	FilerWindow *filer_window = view_collection->filer_window;
955 
956 	g_return_if_fail(i >= 0 && i < collection->number_of_items);
957 
958 	colitem = &collection->items[i];
959 
960 	display_update_view(filer_window,
961 			(DirItem *) colitem->data,
962 			(ViewData *) colitem->view_data,
963 			FALSE);
964 
965 	calc_size(filer_window, colitem, &w, &h);
966 	if (w > old_w || h > old_h)
967 		collection_set_item_size(collection,
968 					 MAX(old_w, w),
969 					 MAX(old_h, h));
970 
971 	collection_draw_item(collection, i, TRUE);
972 }
973 
974 /* Implementations of the View interface. See view_iface.c for comments. */
975 
view_collection_style_changed(ViewIface * view,int flags)976 static void view_collection_style_changed(ViewIface *view, int flags)
977 {
978 	ViewCollection *view_collection = VIEW_COLLECTION(view);
979 	FilerWindow	*filer_window = view_collection->filer_window;
980 	int		i;
981 	Collection	*col = view_collection->collection;
982 	int		width = MIN_ITEM_WIDTH;
983 	int		height = SMALL_HEIGHT;
984 	int		n = col->number_of_items;
985 
986 	if (n == 0 && filer_window->display_style != SMALL_ICONS)
987 		height = ICON_HEIGHT;
988 
989 	view_collection->collection->vertical_order = FALSE;
990 	if (filer_window->display_style == SMALL_ICONS &&
991 	    o_vertical_order_small.int_value)
992 	  view_collection->collection->vertical_order = TRUE;
993 	if (filer_window->display_style != SMALL_ICONS &&
994 	    o_vertical_order_large.int_value)
995 	  view_collection->collection->vertical_order = TRUE;
996 
997 
998 
999 
1000 	/* Recalculate all the ViewData structs for this window
1001 	 * (needed if the text or image has changed in any way) and
1002 	 * get the size of each item.
1003 	 */
1004 	for (i = 0; i < n; i++)
1005 	{
1006 		CollectionItem *ci = &col->items[i];
1007 		int	w, h;
1008 
1009 		if (flags & (VIEW_UPDATE_VIEWDATA | VIEW_UPDATE_NAME))
1010 			display_update_view(filer_window,
1011 					(DirItem *) ci->data,
1012 					(ViewData *) ci->view_data,
1013 					(flags & VIEW_UPDATE_NAME) != 0);
1014 
1015 		calc_size(filer_window, ci, &w, &h);
1016 		if (w > width)
1017 			width = w;
1018 		if (h > height)
1019 			height = h;
1020 	}
1021 
1022 	collection_set_item_size(col, width, height);
1023 
1024 	gtk_widget_queue_draw(GTK_WIDGET(view_collection));
1025 }
1026 
1027 typedef int (*SortFn)(gconstpointer a, gconstpointer b);
1028 
sort_fn(FilerWindow * fw)1029 static SortFn sort_fn(FilerWindow *fw)
1030 {
1031 	switch (fw->sort_type)
1032 	{
1033 		case SORT_NAME: return sort_by_name;
1034 		case SORT_TYPE: return sort_by_type;
1035 		case SORT_DATE: return sort_by_date;
1036 		case SORT_SIZE: return sort_by_size;
1037 		case SORT_OWNER: return sort_by_owner;
1038 		case SORT_GROUP: return sort_by_group;
1039 		default:
1040 			g_assert_not_reached();
1041 	}
1042 
1043 	return NULL;
1044 }
1045 
view_collection_sort(ViewIface * view)1046 static void view_collection_sort(ViewIface *view)
1047 {
1048 	ViewCollection	*view_collection = VIEW_COLLECTION(view);
1049 	FilerWindow	*filer_window = view_collection->filer_window;
1050 
1051 	collection_qsort(view_collection->collection, sort_fn(filer_window),
1052 			filer_window->sort_order);
1053 }
1054 
view_collection_add_items(ViewIface * view,GPtrArray * items)1055 static void view_collection_add_items(ViewIface *view, GPtrArray *items)
1056 {
1057 	ViewCollection	*view_collection = VIEW_COLLECTION(view);
1058 	Collection	*collection = view_collection->collection;
1059 	FilerWindow	*filer_window = view_collection->filer_window;
1060 	int old_num, i;
1061 
1062 	old_num = collection->number_of_items;
1063 	for (i = 0; i < items->len; i++)
1064 	{
1065 		DirItem *item = (DirItem *) items->pdata[i];
1066 
1067 		if (!filer_match_filter(filer_window, item))
1068 			continue;
1069 
1070 		add_item(view_collection, item);
1071 	}
1072 
1073 	if (old_num != collection->number_of_items)
1074 		view_collection_sort(view);
1075 }
1076 
view_collection_update_items(ViewIface * view,GPtrArray * items)1077 static void view_collection_update_items(ViewIface *view, GPtrArray *items)
1078 {
1079 	ViewCollection	*view_collection = VIEW_COLLECTION(view);
1080 	Collection	*collection = view_collection->collection;
1081 	FilerWindow	*filer_window = view_collection->filer_window;
1082 	int		i;
1083 
1084 	g_return_if_fail(items->len > 0);
1085 
1086 	/* The item data has already been modified, so this gives the
1087 	 * final sort order...
1088 	 */
1089 	collection_qsort(collection, sort_fn(filer_window),
1090 			 filer_window->sort_order);
1091 
1092 	for (i = 0; i < items->len; i++)
1093 	{
1094 		DirItem *item = (DirItem *) items->pdata[i];
1095 		const gchar *leafname = item->leafname;
1096 		int j;
1097 
1098 		if (!filer_match_filter(filer_window, item))
1099 			continue;
1100 
1101 		j = collection_find_item(collection, item,
1102 					 sort_fn(filer_window),
1103 					 filer_window->sort_order);
1104 
1105 		if (j < 0)
1106 			g_warning("Failed to find '%s'\n", leafname);
1107 		else
1108 			update_item(view_collection, j);
1109 	}
1110 }
1111 
view_collection_delete_if(ViewIface * view,gboolean (* test)(gpointer item,gpointer data),gpointer data)1112 static void view_collection_delete_if(ViewIface *view,
1113 			  gboolean (*test)(gpointer item, gpointer data),
1114 			  gpointer data)
1115 {
1116 	ViewCollection	*view_collection = VIEW_COLLECTION(view);
1117 	Collection	*collection = view_collection->collection;
1118 
1119 	collection_delete_if(collection, test, data);
1120 }
1121 
view_collection_clear(ViewIface * view)1122 static void view_collection_clear(ViewIface *view)
1123 {
1124 	ViewCollection	*view_collection = VIEW_COLLECTION(view);
1125 	Collection	*collection = view_collection->collection;
1126 
1127 	collection_clear(collection);
1128 }
1129 
view_collection_select_all(ViewIface * view)1130 static void view_collection_select_all(ViewIface *view)
1131 {
1132 	ViewCollection	*view_collection = VIEW_COLLECTION(view);
1133 	Collection	*collection = view_collection->collection;
1134 
1135 	collection_select_all(collection);
1136 }
1137 
view_collection_clear_selection(ViewIface * view)1138 static void view_collection_clear_selection(ViewIface *view)
1139 {
1140 	ViewCollection	*view_collection = VIEW_COLLECTION(view);
1141 	Collection	*collection = view_collection->collection;
1142 
1143 	collection_clear_selection(collection);
1144 }
1145 
view_collection_count_items(ViewIface * view)1146 static int view_collection_count_items(ViewIface *view)
1147 {
1148 	ViewCollection	*view_collection = VIEW_COLLECTION(view);
1149 	Collection	*collection = view_collection->collection;
1150 
1151 	return collection->number_of_items;
1152 }
1153 
view_collection_count_selected(ViewIface * view)1154 static int view_collection_count_selected(ViewIface *view)
1155 {
1156 	ViewCollection	*view_collection = VIEW_COLLECTION(view);
1157 	Collection	*collection = view_collection->collection;
1158 
1159 	return collection->number_selected;
1160 }
1161 
view_collection_show_cursor(ViewIface * view)1162 static void view_collection_show_cursor(ViewIface *view)
1163 {
1164 	ViewCollection	*view_collection = VIEW_COLLECTION(view);
1165 	Collection	*collection = view_collection->collection;
1166 
1167 	collection_move_cursor(collection, 0, 0);
1168 }
1169 
1170 /* The first time the next() method is used, this is called */
iter_init(ViewIter * iter)1171 static DirItem *iter_init(ViewIter *iter)
1172 {
1173 	ViewCollection *view_collection = (ViewCollection *) iter->view;
1174 	Collection *collection = view_collection->collection;
1175 	int i = -1;
1176 	int n = collection->number_of_items;
1177 	int flags = iter->flags;
1178 
1179 	iter->peek = iter_peek;
1180 
1181 	if (iter->n_remaining == 0)
1182 		return NULL;
1183 
1184 	if (flags & VIEW_ITER_FROM_CURSOR)
1185 	{
1186 		i = collection->cursor_item;
1187 		if (i == -1)
1188 			return NULL;	/* No cursor */
1189 	}
1190 	else if (flags & VIEW_ITER_FROM_BASE)
1191 		i = view_collection->cursor_base;
1192 
1193 	if (i < 0 || i >= n)
1194 	{
1195 		/* Either a normal iteration, or an iteration from an
1196 		 * invalid starting point.
1197 		 */
1198 		if (flags & VIEW_ITER_BACKWARDS)
1199 			i = n - 1;
1200 		else
1201 			i = 0;
1202 	}
1203 
1204 	if (i < 0 || i >= n)
1205 		return NULL;	/* No items at all! */
1206 
1207 	iter->next = flags & VIEW_ITER_BACKWARDS ? iter_prev : iter_next;
1208 	iter->n_remaining--;
1209 	iter->i = i;
1210 
1211 	if (flags & VIEW_ITER_SELECTED && !collection->items[i].selected)
1212 		return iter->next(iter);
1213 	return iter->peek(iter);
1214 }
1215 /* Advance iter to point to the next item and return the new item
1216  * (this saves you calling peek after next each time).
1217  */
iter_next(ViewIter * iter)1218 static DirItem *iter_next(ViewIter *iter)
1219 {
1220 	Collection *collection = ((ViewCollection *) iter->view)->collection;
1221 	int n = collection->number_of_items;
1222 	int i = iter->i;
1223 
1224 	g_return_val_if_fail(iter->n_remaining >= 0, NULL);
1225 
1226 	/* i is the last item returned (always valid) */
1227 
1228 	g_return_val_if_fail(i >= 0 && i < n, NULL);
1229 
1230 	while (iter->n_remaining)
1231 	{
1232 		i++;
1233 		iter->n_remaining--;
1234 
1235 		if (i == n)
1236 			i = 0;
1237 
1238 		g_return_val_if_fail(i >= 0 && i < n, NULL);
1239 
1240 		if (iter->flags & VIEW_ITER_SELECTED &&
1241 		    !collection->items[i].selected)
1242 			continue;
1243 
1244 		iter->i = i;
1245 		return collection->items[i].data;
1246 	}
1247 
1248 	iter->i = -1;
1249 	return NULL;
1250 }
1251 
1252 /* Like iter_next, but in the other direction */
iter_prev(ViewIter * iter)1253 static DirItem *iter_prev(ViewIter *iter)
1254 {
1255 	Collection *collection = ((ViewCollection *) iter->view)->collection;
1256 	int n = collection->number_of_items;
1257 	int i = iter->i;
1258 
1259 	g_return_val_if_fail(iter->n_remaining >= 0, NULL);
1260 
1261 	/* i is the last item returned (always valid) */
1262 
1263 	g_return_val_if_fail(i >= 0 && i < n, NULL);
1264 
1265 	while (iter->n_remaining)
1266 	{
1267 		i--;
1268 		iter->n_remaining--;
1269 
1270 		if (i == -1)
1271 			i = collection->number_of_items - 1;
1272 
1273 		g_return_val_if_fail(i >= 0 && i < n, NULL);
1274 
1275 		if (iter->flags & VIEW_ITER_SELECTED &&
1276 		    !collection->items[i].selected)
1277 			continue;
1278 
1279 		iter->i = i;
1280 		return collection->items[i].data;
1281 	}
1282 
1283 	iter->i = -1;
1284 	return NULL;
1285 }
1286 
iter_peek(ViewIter * iter)1287 static DirItem *iter_peek(ViewIter *iter)
1288 {
1289 	Collection *collection = ((ViewCollection *) iter->view)->collection;
1290 	int i = iter->i;
1291 
1292 	if (i == -1)
1293 		return NULL;
1294 
1295 	g_return_val_if_fail(i >= 0 && i < collection->number_of_items, NULL);
1296 
1297 	return collection->items[i].data;
1298 }
1299 
make_iter(ViewCollection * view_collection,ViewIter * iter,IterFlags flags)1300 static void make_iter(ViewCollection *view_collection, ViewIter *iter,
1301 		      IterFlags flags)
1302 {
1303 	Collection *collection = view_collection->collection;
1304 
1305 	iter->view = (ViewIface *) view_collection;
1306 	iter->next = iter_init;
1307 	iter->peek = NULL;
1308 	iter->i = -1;
1309 
1310 	iter->flags = flags;
1311 
1312 	if (flags & VIEW_ITER_ONE_ONLY)
1313 	{
1314 		iter->n_remaining = 1;
1315 		iter->next(iter);
1316 	}
1317 	else
1318 		iter->n_remaining = collection->number_of_items;
1319 }
1320 
1321 /* Set the iterator to return 'i' on the next peek() */
make_item_iter(ViewCollection * view_collection,ViewIter * iter,int i)1322 static void make_item_iter(ViewCollection *view_collection,
1323 			   ViewIter *iter, int i)
1324 {
1325 	Collection *collection = view_collection->collection;
1326 
1327 	g_return_if_fail(i >= -1 && i < collection->number_of_items);
1328 
1329 	make_iter(view_collection, iter, 0);
1330 
1331 	iter->i = i;
1332 	iter->next = iter_next;
1333 	iter->peek = iter_peek;
1334 	iter->n_remaining = 0;
1335 }
1336 
view_collection_get_iter(ViewIface * view,ViewIter * iter,IterFlags flags)1337 static void view_collection_get_iter(ViewIface *view,
1338 				     ViewIter *iter, IterFlags flags)
1339 {
1340 	ViewCollection	*view_collection = VIEW_COLLECTION(view);
1341 
1342 	make_iter(view_collection, iter, flags);
1343 }
1344 
view_collection_get_iter_at_point(ViewIface * view,ViewIter * iter,GdkWindow * src,int x,int y)1345 static void view_collection_get_iter_at_point(ViewIface *view, ViewIter *iter,
1346 					      GdkWindow *src, int x, int y)
1347 {
1348 	ViewCollection *view_collection = VIEW_COLLECTION(view);
1349 	Collection *collection = view_collection->collection;
1350 	int i;
1351 
1352 	if (src == ((GtkWidget *) view)->window)
1353 	{
1354 		/* The event is on the Viewport, not the collection... */
1355 		y += collection->vadj->value;
1356 	}
1357 
1358 	i = collection_get_item(collection, x, y);
1359 	make_item_iter(view_collection, iter, i);
1360 }
1361 
view_collection_cursor_to_iter(ViewIface * view,ViewIter * iter)1362 static void view_collection_cursor_to_iter(ViewIface *view, ViewIter *iter)
1363 {
1364 	ViewCollection	*view_collection = VIEW_COLLECTION(view);
1365 	Collection	*collection = view_collection->collection;
1366 	FilerWindow	*filer_window = view_collection->filer_window;
1367 
1368 	g_return_if_fail(iter == NULL ||
1369 			 iter->view == (ViewIface *) view_collection);
1370 
1371 	collection_set_cursor_item(collection, iter ? iter->i : -1,
1372 			filer_window->auto_scroll == -1);
1373 }
1374 
view_collection_set_selected(ViewIface * view,ViewIter * iter,gboolean selected)1375 static void view_collection_set_selected(ViewIface *view,
1376 					 ViewIter *iter,
1377 					 gboolean selected)
1378 {
1379 	ViewCollection	*view_collection = VIEW_COLLECTION(view);
1380 	Collection	*collection = view_collection->collection;
1381 
1382 	g_return_if_fail(iter != NULL &&
1383 			 iter->view == (ViewIface *) view_collection);
1384 	g_return_if_fail(iter->i >= 0 && iter->i < collection->number_of_items);
1385 
1386 	if (selected)
1387 		collection_select_item(collection, iter->i);
1388 	else
1389 		collection_unselect_item(collection, iter->i);
1390 }
1391 
view_collection_get_selected(ViewIface * view,ViewIter * iter)1392 static gboolean view_collection_get_selected(ViewIface *view, ViewIter *iter)
1393 {
1394 	ViewCollection	*view_collection = VIEW_COLLECTION(view);
1395 	Collection	*collection = view_collection->collection;
1396 
1397 	g_return_val_if_fail(iter != NULL &&
1398 			iter->view == (ViewIface *) view_collection, FALSE);
1399 	g_return_val_if_fail(iter->i >= 0 &&
1400 				iter->i < collection->number_of_items, FALSE);
1401 
1402 	return collection->items[iter->i].selected;
1403 }
1404 
view_collection_select_only(ViewIface * view,ViewIter * iter)1405 static void view_collection_select_only(ViewIface *view, ViewIter *iter)
1406 {
1407 	ViewCollection	*view_collection = VIEW_COLLECTION(view);
1408 	Collection	*collection = view_collection->collection;
1409 
1410 	g_return_if_fail(iter != NULL &&
1411 			 iter->view == (ViewIface *) view_collection);
1412 	g_return_if_fail(iter->i >= 0 && iter->i < collection->number_of_items);
1413 
1414 	collection_clear_except(collection, iter->i);
1415 }
1416 
view_collection_set_frozen(ViewIface * view,gboolean frozen)1417 static void view_collection_set_frozen(ViewIface *view, gboolean frozen)
1418 {
1419 	ViewCollection	*view_collection = VIEW_COLLECTION(view);
1420 	Collection	*collection = view_collection->collection;
1421 
1422 	if (frozen)
1423 		collection->block_selection_changed++;
1424 	else
1425 		collection_unblock_selection_changed(collection,
1426 				gtk_get_current_event_time(), TRUE);
1427 }
1428 
view_collection_wink_item(ViewIface * view,ViewIter * iter)1429 static void view_collection_wink_item(ViewIface *view, ViewIter *iter)
1430 {
1431 	ViewCollection	*view_collection = VIEW_COLLECTION(view);
1432 	Collection	*collection = view_collection->collection;
1433 
1434 	if (!iter)
1435 	{
1436 		collection_wink_item(collection, -1);
1437 		return;
1438 	}
1439 
1440 	g_return_if_fail(iter != NULL &&
1441 			 iter->view == (ViewIface *) view_collection);
1442 	g_return_if_fail(iter->i >= 0 && iter->i < collection->number_of_items);
1443 
1444 	collection_wink_item(collection, iter->i);
1445 }
1446 
view_collection_autosize(ViewIface * view)1447 static void view_collection_autosize(ViewIface *view)
1448 {
1449 	ViewCollection	*view_collection = VIEW_COLLECTION(view);
1450 	FilerWindow	*filer_window = view_collection->filer_window;
1451 	Collection	*collection = view_collection->collection;
1452 	int 		n;
1453 	int		w = collection->item_width;
1454 	int		h = collection->item_height;
1455 	int 		x;
1456 	int		rows, cols;
1457 	int 		max_x, max_rows;
1458 	const float	r = 2.5;
1459 	int		t = 0;
1460 	int		space = 0;
1461 
1462 	/* Get the extra height required for the toolbar and minibuffer,
1463 	 * if visible.
1464 	 */
1465 	if (o_toolbar.int_value != TOOLBAR_NONE)
1466 		t = filer_window->toolbar->allocation.height;
1467 	if (filer_window->message)
1468 		t += filer_window->message->allocation.height;
1469 	if (GTK_WIDGET_VISIBLE(filer_window->minibuffer_area))
1470 	{
1471 		GtkRequisition req;
1472 
1473 		gtk_widget_size_request(filer_window->minibuffer_area, &req);
1474 		space = req.height + 2;
1475 		t += space;
1476 	}
1477 
1478 	n = collection->number_of_items;
1479 	if (n == 0)
1480 		h = ICON_HEIGHT * 1.5;
1481 	n = MAX(n, 2);
1482 
1483 	max_x = (o_filer_size_limit.int_value * monitor_width) / 100;
1484 	max_rows = (o_filer_size_limit.int_value * monitor_height) / (h * 100);
1485 
1486 	/* Aim for a size where
1487 	 * 	   x = r(y + t + h),		(1)
1488 	 * unless that's too wide.
1489 	 *
1490 	 * t = toolbar (and minibuffer) height
1491 	 * r = desired (width / height) ratio
1492 	 *
1493 	 * Want to display all items:
1494 	 * 	   (x/w)(y/h) = n
1495 	 * 	=> xy = nwh
1496 	 *	=> x(x/r - t - h) = nwh		(from 1)
1497 	 *	=> xx - x.rt - hr(1 - nw) = 0
1498 	 *	=> 2x = rt +/- sqrt(rt.rt + 4hr(nw - 1))
1499 	 * Now,
1500 	 * 	   4hr(nw - 1) > 0
1501 	 * so
1502 	 * 	   sqrt(rt.rt + ...) > rt
1503 	 *
1504 	 * So, the +/- must be +:
1505 	 *
1506 	 *	=> x = (rt + sqrt(rt.rt + 4hr(nw - 1))) / 2
1507 	 *
1508 	 * ( + w - 1 to round up)
1509 	 */
1510 	x = (r * t + sqrt(r*r*t*t + 4*h*r * (n*w - 1))) / 2 + w - 1;
1511 
1512 	/* Limit x */
1513 	if (x > max_x)
1514 		x = max_x;
1515 
1516 	cols = x / w;
1517 	cols = MAX(cols, 1);
1518 
1519 	/* Choose rows to display all items given our chosen x.
1520 	 * Don't make the window *too* big!
1521 	 */
1522 	rows = (n + cols - 1) / cols;
1523 	if (rows > max_rows)
1524 		rows = max_rows;
1525 
1526 	/* Leave some room for extra icons, but only in Small Icons mode
1527 	 * otherwise it takes up too much space.
1528 	 * Also, don't add space if the minibuffer is open.
1529 	 */
1530 	if (space == 0)
1531 		space = filer_window->display_style == SMALL_ICONS ? h : 2;
1532 
1533 	filer_window_set_size(filer_window,
1534 			w * MAX(cols, 1),
1535 			h * MAX(rows, 1) + space);
1536 }
1537 
view_collection_cursor_visible(ViewIface * view)1538 static gboolean view_collection_cursor_visible(ViewIface *view)
1539 {
1540 	ViewCollection	*view_collection = VIEW_COLLECTION(view);
1541 	Collection	*collection = view_collection->collection;
1542 
1543 	return collection->cursor_item != -1;
1544 }
1545 
view_collection_set_base(ViewIface * view,ViewIter * iter)1546 static void view_collection_set_base(ViewIface *view, ViewIter *iter)
1547 {
1548 	ViewCollection	*view_collection = VIEW_COLLECTION(view);
1549 
1550 	view_collection->cursor_base = iter->i;
1551 }
1552 
view_collection_start_lasso_box(ViewIface * view,GdkEventButton * event)1553 static void view_collection_start_lasso_box(ViewIface *view,
1554 					    GdkEventButton *event)
1555 {
1556 	ViewCollection	*view_collection = (ViewCollection *) view;
1557 	Collection	*collection = view_collection->collection;
1558 
1559 	filer_set_autoscroll(view_collection->filer_window, TRUE);
1560 	collection_lasso_box(collection, event->x, event->y);
1561 }
1562 
1563 
1564 /* Change the adjustment by this amount. Bounded. */
diff_vpos(Collection * collection,int diff)1565 static void diff_vpos(Collection *collection, int diff)
1566 {
1567 	int	old = collection->vadj->value;
1568 	int	value = old + diff;
1569 
1570 	value = CLAMP(value, 0,
1571 			collection->vadj->upper - collection->vadj->page_size);
1572 	gtk_adjustment_set_value(collection->vadj, value);
1573 
1574 	if (collection->vadj->value != old)
1575 		dnd_spring_abort();
1576 }
1577 
view_collection_auto_scroll_callback(ViewIface * view)1578 static gboolean view_collection_auto_scroll_callback(ViewIface *view)
1579 {
1580 	ViewCollection	*view_collection = (ViewCollection *) view;
1581 	Collection	*collection = view_collection->collection;
1582 	GdkWindow	*window = ((GtkWidget *) collection)->window;
1583 	gint		x, y, w, h;
1584 	GdkModifierType	mask;
1585 	int		diff = 0;
1586 
1587 	gdk_window_get_pointer(window, &x, &y, &mask);
1588 	gdk_drawable_get_size(window, &w, NULL);
1589 
1590 	h = collection->vadj->page_size;
1591 	y -= collection->vadj->value;
1592 
1593 	if ((x < 0 || x > w || y < 0 || y > h) && !collection->lasso_box)
1594 		return FALSE;		/* Out of window - stop */
1595 
1596 	if (y < AUTOSCROLL_STEP)
1597 		diff = y - AUTOSCROLL_STEP;
1598 	else if (y > h - AUTOSCROLL_STEP)
1599 		diff = AUTOSCROLL_STEP + y - h;
1600 
1601 	if (diff)
1602 		diff_vpos(collection, diff);
1603 
1604 	return TRUE;
1605 }
1606