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_details.c - display a list of files in a TreeView */
21 
22 #include "config.h"
23 
24 #include <gtk/gtk.h>
25 #include <gdk/gdkkeysyms.h>
26 
27 #include "global.h"
28 
29 #include "view_iface.h"
30 #include "view_details.h"
31 #include "dir.h"
32 #include "diritem.h"
33 #include "support.h"
34 #include "type.h"
35 #include "filer.h"
36 #include "display.h"
37 #include "pixmaps.h"
38 #include "dnd.h"
39 #include "bind.h"
40 #include "gui_support.h"
41 #include "menu.h"
42 #include "options.h"
43 #include "cell_icon.h"
44 
45 /* These are the column numbers in the ListStore */
46 #define COL_LEAF 0
47 #define COL_TYPE 1
48 #define COL_PERM 2
49 #define COL_OWNER 3
50 #define COL_GROUP 4
51 #define COL_SIZE 5
52 #define COL_MTIME 6
53 #define COL_ITEM 7
54 #define COL_COLOUR 8
55 #define COL_WEIGHT 9
56 #define COL_VIEW_ITEM 10
57 #define N_COLUMNS 11
58 
59 static gpointer parent_class = NULL;
60 
61 struct _ViewDetailsClass {
62 	GtkTreeViewClass parent;
63 };
64 
65 /* Static prototypes */
66 static void view_details_finialize(GObject *object);
67 static void view_details_class_init(gpointer gclass, gpointer data);
68 static void view_details_init(GTypeInstance *object, gpointer gclass);
69 
70 static void view_details_iface_init(gpointer giface, gpointer iface_data);
71 
72 static void view_details_sort(ViewIface *view);
73 static void view_details_style_changed(ViewIface *view, int flags);
74 static void view_details_add_items(ViewIface *view, GPtrArray *items);
75 static void view_details_update_items(ViewIface *view, GPtrArray *items);
76 static void view_details_delete_if(ViewIface *view,
77 			  gboolean (*test)(gpointer item, gpointer data),
78 			  gpointer data);
79 static void view_details_clear(ViewIface *view);
80 static void view_details_select_all(ViewIface *view);
81 static void view_details_clear_selection(ViewIface *view);
82 static int view_details_count_items(ViewIface *view);
83 static int view_details_count_selected(ViewIface *view);
84 static void view_details_show_cursor(ViewIface *view);
85 static void view_details_get_iter(ViewIface *view,
86 				     ViewIter *iter, IterFlags flags);
87 static void view_details_get_iter_at_point(ViewIface *view, ViewIter *iter,
88 					   GdkWindow *src, int x, int y);
89 static void view_details_cursor_to_iter(ViewIface *view, ViewIter *iter);
90 static void view_details_set_selected(ViewIface *view,
91 					 ViewIter *iter,
92 					 gboolean selected);
93 static gboolean view_details_get_selected(ViewIface *view, ViewIter *iter);
94 static void view_details_select_only(ViewIface *view, ViewIter *iter);
95 static void view_details_set_frozen(ViewIface *view, gboolean frozen);
96 static void view_details_wink_item(ViewIface *view, ViewIter *iter);
97 static void view_details_autosize(ViewIface *view);
98 static gboolean view_details_cursor_visible(ViewIface *view);
99 static void view_details_set_base(ViewIface *view, ViewIter *iter);
100 static void view_details_start_lasso_box(ViewIface *view,
101 				     	 GdkEventButton *event);
102 static void view_details_extend_tip(ViewIface *view,
103 				    ViewIter *iter, GString *tip);
104 static gboolean view_details_auto_scroll_callback(ViewIface *view);
105 
106 static DirItem *iter_peek(ViewIter *iter);
107 static DirItem *iter_prev(ViewIter *iter);
108 static DirItem *iter_next(ViewIter *iter);
109 static void make_iter(ViewDetails *view_details, ViewIter *iter,
110 		      IterFlags flags);
111 static void make_item_iter(ViewDetails *view_details, ViewIter *iter, int i);
112 static void view_details_tree_model_init(GtkTreeModelIface *iface);
113 static gboolean details_get_sort_column_id(GtkTreeSortable *sortable,
114 					   gint            *sort_column_id,
115 					   GtkSortType     *order);
116 static void details_set_sort_column_id(GtkTreeSortable     *sortable,
117 				       gint                sort_column_id,
118 				       GtkSortType         order);
119 static void details_set_sort_func(GtkTreeSortable          *sortable,
120 			          gint                    sort_column_id,
121 			          GtkTreeIterCompareFunc  func,
122 			          gpointer                data,
123 			          GtkDestroyNotify        destroy);
124 static void details_set_default_sort_func(GtkTreeSortable        *sortable,
125 				          GtkTreeIterCompareFunc  func,
126 				          gpointer                data,
127 				          GtkDestroyNotify        destroy);
128 static gboolean details_has_default_sort_func(GtkTreeSortable *sortable);
129 static void view_details_sortable_init(GtkTreeSortableIface *iface);
130 static void set_selected(ViewDetails *view_details, int i, gboolean selected);
131 static gboolean get_selected(ViewDetails *view_details, int i);
132 static void free_view_item(ViewItem *view_item);
133 static void details_update_header_visibility(ViewDetails *view_details);
134 static void set_lasso(ViewDetails *view_details, int x, int y);
135 static void cancel_wink(ViewDetails *view_details);
136 
137 
138 /****************************************************************
139  *			EXTERNAL INTERFACE			*
140  ****************************************************************/
141 
view_details_new(FilerWindow * filer_window)142 GtkWidget *view_details_new(FilerWindow *filer_window)
143 {
144 	ViewDetails *view_details;
145 
146 	view_details = g_object_new(view_details_get_type(), NULL);
147 	view_details->filer_window = filer_window;
148 
149 	gtk_range_set_adjustment(GTK_RANGE(filer_window->scrollbar),
150 		gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(view_details)));
151 
152 	if (filer_window->sort_type != -1)
153 		view_details_sort((ViewIface *) view_details);
154 
155 	details_update_header_visibility(view_details);
156 
157 	return GTK_WIDGET(view_details);
158 }
159 
view_details_get_type(void)160 GType view_details_get_type(void)
161 {
162 	static GType type = 0;
163 
164 	if (!type)
165 	{
166 		static const GTypeInfo info =
167 		{
168 			sizeof (ViewDetailsClass),
169 			NULL,			/* base_init */
170 			NULL,			/* base_finalise */
171 			view_details_class_init,
172 			NULL,			/* class_finalise */
173 			NULL,			/* class_data */
174 			sizeof(ViewDetails),
175 			0,			/* n_preallocs */
176 			view_details_init
177 		};
178 		static const GInterfaceInfo view_iface_info = {
179 			view_details_iface_init,
180 			NULL, NULL
181 		};
182 		static const GInterfaceInfo tree_model_info = {
183 			(GInterfaceInitFunc) view_details_tree_model_init,
184 			NULL, NULL
185 		};
186 		static const GInterfaceInfo sortable_info = {
187 			(GInterfaceInitFunc) view_details_sortable_init,
188 			NULL, NULL
189 		};
190 
191 
192 		type = g_type_register_static(gtk_tree_view_get_type(),
193 						"ViewDetails", &info, 0);
194 
195 		g_type_add_interface_static(type, VIEW_TYPE_IFACE,
196 				&view_iface_info);
197 		g_type_add_interface_static(type, GTK_TYPE_TREE_MODEL,
198 				&tree_model_info);
199 		g_type_add_interface_static(type, GTK_TYPE_TREE_SORTABLE,
200 				&sortable_info);
201 	}
202 
203 	return type;
204 }
205 
206 /****************************************************************
207  *			INTERNAL FUNCTIONS			*
208  ****************************************************************/
209 
210 /* Update the visibility of the list headers */
details_update_header_visibility(ViewDetails * view_details)211 static void details_update_header_visibility(ViewDetails *view_details)
212 {
213 	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view_details),
214 					  o_display_show_headers.int_value);
215 }
216 
217 /* Fulfill the GtkTreeModel requirements */
details_get_flags(GtkTreeModel * tree_model)218 static guint details_get_flags(GtkTreeModel *tree_model)
219 {
220 	return GTK_TREE_MODEL_LIST_ONLY;
221 }
222 
details_get_n_columns(GtkTreeModel * tree_model)223 static gint details_get_n_columns(GtkTreeModel *tree_model)
224 {
225 	return N_COLUMNS;
226 }
227 
details_get_column_type(GtkTreeModel * tree_model,gint index)228 static GType details_get_column_type(GtkTreeModel *tree_model, gint index)
229 {
230 	g_return_val_if_fail(index < N_COLUMNS && index >= 0, G_TYPE_INVALID);
231 
232 	if (index == COL_COLOUR)
233 		return GDK_TYPE_COLOR;
234 	else if (index == COL_ITEM || index == COL_VIEW_ITEM)
235 		return G_TYPE_POINTER;
236 	else if (index == COL_WEIGHT)
237 		return G_TYPE_INT;
238 	return G_TYPE_STRING;
239 }
240 
details_get_iter(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreePath * path)241 static gboolean details_get_iter(GtkTreeModel *tree_model,
242 				 GtkTreeIter  *iter,
243 				 GtkTreePath  *path)
244 {
245 	ViewDetails *view_details = (ViewDetails *) tree_model;
246 	gint i;
247 
248 	g_return_val_if_fail(gtk_tree_path_get_depth (path) > 0, FALSE);
249 
250 	i = gtk_tree_path_get_indices(path)[0];
251 
252 	if (i >= view_details->items->len)
253 		return FALSE;
254 
255 	iter->user_data = GINT_TO_POINTER(i);
256 
257 	return TRUE;
258 }
259 
details_get_path(GtkTreeModel * tree_model,GtkTreeIter * iter)260 static GtkTreePath *details_get_path(GtkTreeModel *tree_model,
261 				     GtkTreeIter  *iter)
262 {
263 	GtkTreePath *retval;
264 
265 	retval = gtk_tree_path_new();
266 	gtk_tree_path_append_index(retval, GPOINTER_TO_INT(iter->user_data));
267 
268 	return retval;
269 }
270 
details_get_value(GtkTreeModel * tree_model,GtkTreeIter * iter,gint column,GValue * value)271 static void details_get_value(GtkTreeModel *tree_model,
272 			      GtkTreeIter  *iter,
273 			      gint         column,
274 			      GValue       *value)
275 {
276 	ViewDetails *view_details = (ViewDetails *) tree_model;
277 	gint i;
278 	GPtrArray *items = view_details->items;
279 	ViewItem *view_item;
280 	DirItem *item;
281 	mode_t m;
282 
283 	g_return_if_fail(column >= 0 && column < N_COLUMNS);
284 
285 	i = GPOINTER_TO_INT(iter->user_data);
286 	g_return_if_fail(i >= 0 && i < items->len);
287 	view_item = (ViewItem *) items->pdata[i];
288 	item = view_item->item;
289 
290 	if (column == COL_LEAF)
291 	{
292 		g_value_init(value, G_TYPE_STRING);
293 		g_value_set_string(value,
294 			view_item->utf8_name ? view_item->utf8_name
295 					     : item->leafname);
296 		return;
297 	}
298 	else if (column == COL_VIEW_ITEM)
299 	{
300 		g_value_init(value, G_TYPE_POINTER);
301 		g_value_set_pointer(value, view_item);
302 		return;
303 	}
304 	else if (column == COL_ITEM)
305 	{
306 		g_value_init(value, G_TYPE_POINTER);
307 		g_value_set_pointer(value, item);
308 		return;
309 	}
310 
311 	if (item->base_type == TYPE_UNKNOWN)
312 	{
313 		GType type;
314 		type = details_get_column_type(tree_model, column);
315 		g_value_init(value, type);
316 		if (type == G_TYPE_STRING)
317 			g_value_set_string(value, "");
318 		else if (type == GDK_TYPE_COLOR)
319 			g_value_set_boxed(value, NULL);
320 		else if (type == G_TYPE_INT)
321 			g_value_set_int(value, PANGO_WEIGHT_NORMAL);
322 		else
323 			g_value_set_object(value, NULL);
324 
325 		return;
326 	}
327 	m = item->mode;
328 
329 	switch (column)
330 	{
331 		case COL_LEAF:
332 			g_value_init(value, G_TYPE_STRING);
333 			g_value_set_string(value, item->leafname);
334 			break;
335 		case COL_COLOUR:
336 			g_value_init(value, GDK_TYPE_COLOR);
337 			if (view_item->utf8_name)
338 			{
339 				GdkColor red;
340 				red.red = 0xffff;
341 				red.green = 0;
342 				red.blue = 0;
343 				g_value_set_boxed(value, &red);
344 			}
345 			else
346 				g_value_set_boxed(value,
347 						  type_get_colour(item, NULL));
348 			break;
349 		case COL_OWNER:
350 			g_value_init(value, G_TYPE_STRING);
351 			g_value_set_string(value, user_name(item->uid));
352 			break;
353 		case COL_GROUP:
354 			g_value_init(value, G_TYPE_STRING);
355 			g_value_set_string(value, group_name(item->gid));
356 			break;
357 		case COL_MTIME:
358 		{
359 			gchar *time;
360 			time = pretty_time(&item->mtime);
361 			g_value_init(value, G_TYPE_STRING);
362 			g_value_set_string(value, time);
363 			g_free(time);
364 			break;
365 		}
366 		case COL_PERM:
367 			g_value_init(value, G_TYPE_STRING);
368 			g_value_set_string(value, pretty_permissions(m));
369 			break;
370 		case COL_SIZE:
371 			g_value_init(value, G_TYPE_STRING);
372 			if (item->base_type != TYPE_DIRECTORY)
373 				g_value_set_string(value,
374 						   format_size(item->size));
375 			break;
376 		case COL_TYPE:
377 			g_value_init(value, G_TYPE_STRING);
378 			if(o_display_show_full_type.int_value)
379 				g_value_set_string(value,
380 						   item->flags & ITEM_FLAG_APPDIR? "Application" :
381 						   mime_type_comment(item->mime_type));
382 			else
383 				g_value_set_string(value,
384 						   item->flags & ITEM_FLAG_APPDIR? "App" :
385 						   S_ISDIR(m) ? "Dir" :
386 						   S_ISCHR(m) ? "Char" :
387 						   S_ISBLK(m) ? "Blck" :
388 						   S_ISLNK(m) ? "Link" :
389 						   S_ISSOCK(m) ? "Sock" :
390 						   S_ISFIFO(m) ? "Pipe" :
391 						   S_ISDOOR(m) ? "Door" :
392 						   "File");
393 
394 			break;
395 		case COL_WEIGHT:
396 			g_value_init(value, G_TYPE_INT);
397 			if (item->flags & ITEM_FLAG_RECENT)
398 				g_value_set_int(value, PANGO_WEIGHT_BOLD);
399 			else
400 				g_value_set_int(value, PANGO_WEIGHT_NORMAL);
401 			break;
402 		default:
403 			g_value_init(value, G_TYPE_STRING);
404 			g_value_set_string(value, "Hello");
405 			break;
406 	}
407 }
408 
details_iter_next(GtkTreeModel * tree_model,GtkTreeIter * iter)409 static gboolean details_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter)
410 {
411 	ViewDetails *view_details = (ViewDetails *) tree_model;
412 	int i;
413 
414 	i = GPOINTER_TO_INT(iter->user_data) + 1;
415 	iter->user_data = GINT_TO_POINTER(i);
416 
417 	return i < view_details->items->len;
418 }
419 
details_iter_children(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreeIter * parent)420 static gboolean details_iter_children(GtkTreeModel *tree_model,
421 				      GtkTreeIter  *iter,
422 				      GtkTreeIter  *parent)
423 {
424 	ViewDetails *view_details = (ViewDetails *) tree_model;
425 
426 	/* this is a list, nodes have no children */
427 	if (parent)
428 		return FALSE;
429 
430 	/* but if parent == NULL we return the list itself as children of the
431 	 * "root"
432 	 */
433 
434 	if (view_details->items->len)
435 	{
436 		iter->user_data = GINT_TO_POINTER(0);
437 		return TRUE;
438 	}
439 	else
440 		return FALSE;
441 }
442 
details_iter_has_child(GtkTreeModel * tree_model,GtkTreeIter * iter)443 static gboolean details_iter_has_child(GtkTreeModel *tree_model,
444 				       GtkTreeIter  *iter)
445 {
446 	return FALSE;
447 }
448 
details_iter_n_children(GtkTreeModel * tree_model,GtkTreeIter * iter)449 static gint details_iter_n_children(GtkTreeModel *tree_model, GtkTreeIter *iter)
450 {
451 	ViewDetails *view_details = (ViewDetails *) tree_model;
452 
453 	if (iter == NULL)
454 		return view_details->items->len;
455 
456 	return 0;
457 }
458 
details_iter_nth_child(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreeIter * parent,gint n)459 static gboolean details_iter_nth_child(GtkTreeModel *tree_model,
460 				       GtkTreeIter  *iter,
461 				       GtkTreeIter  *parent,
462 				       gint          n)
463 {
464 	ViewDetails *view_details = (ViewDetails *) tree_model;
465 
466 	if (parent)
467 		return FALSE;
468 
469 	if (n >= 0 && n < view_details->items->len)
470 	{
471 		iter->user_data = GINT_TO_POINTER(n);
472 		return TRUE;
473 	}
474 	else
475 		return FALSE;
476 }
477 
details_iter_parent(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreeIter * child)478 static gboolean details_iter_parent(GtkTreeModel *tree_model,
479 				    GtkTreeIter  *iter,
480 				    GtkTreeIter  *child)
481 {
482 	return FALSE;
483 }
484 
485 /* A ViewDetails is both a GtkTreeView and a GtkTreeModel.
486  * The following functions implement the model interface...
487  */
488 
view_details_tree_model_init(GtkTreeModelIface * iface)489 static void view_details_tree_model_init(GtkTreeModelIface *iface)
490 {
491 	iface->get_flags = details_get_flags;
492 	iface->get_n_columns = details_get_n_columns;
493 	iface->get_column_type = details_get_column_type;
494 	iface->get_iter = details_get_iter;
495 	iface->get_path = details_get_path;
496 	iface->get_value = details_get_value;
497 	iface->iter_next = details_iter_next;
498 	iface->iter_children = details_iter_children;
499 	iface->iter_has_child = details_iter_has_child;
500 	iface->iter_n_children = details_iter_n_children;
501 	iface->iter_nth_child = details_iter_nth_child;
502 	iface->iter_parent = details_iter_parent;
503 }
504 
view_details_sortable_init(GtkTreeSortableIface * iface)505 static void view_details_sortable_init(GtkTreeSortableIface *iface)
506 {
507 	iface->get_sort_column_id = details_get_sort_column_id;
508 	iface->set_sort_column_id = details_set_sort_column_id;
509 	iface->set_sort_func = details_set_sort_func;
510 	iface->set_default_sort_func = details_set_default_sort_func;
511 	iface->has_default_sort_func = details_has_default_sort_func;
512 }
513 
details_get_sort_column_id(GtkTreeSortable * sortable,gint * sort_column_id,GtkSortType * order)514 static gboolean details_get_sort_column_id(GtkTreeSortable *sortable,
515 					   gint            *sort_column_id,
516 					   GtkSortType     *order)
517 {
518 	ViewDetails *view_details = (ViewDetails *) sortable;
519 	FilerWindow *filer_window = view_details->filer_window;
520 	int col;
521 
522 	if (!filer_window)
523 		return FALSE;	/* Not yet initialised */
524 
525 	switch (filer_window->sort_type)
526 	{
527 		case SORT_NAME: col = COL_LEAF; break;
528 		case SORT_TYPE: col = COL_TYPE; break;
529 		case SORT_DATE: col = COL_MTIME; break;
530 		case SORT_SIZE: col = COL_SIZE; break;
531 		case SORT_OWNER: col = COL_OWNER; break;
532 		case SORT_GROUP: col = COL_GROUP; break;
533 		default:
534 			g_warning("details_get_sort_column_id(): error!");
535 			return FALSE;
536 	}
537 	if (sort_column_id)
538 		*sort_column_id = col;
539 	if (order)
540 		*order = filer_window->sort_order;
541 	return TRUE;
542 }
543 
details_set_sort_column_id(GtkTreeSortable * sortable,gint sort_column_id,GtkSortType order)544 static void details_set_sort_column_id(GtkTreeSortable     *sortable,
545 				       gint                sort_column_id,
546 				       GtkSortType         order)
547 {
548 	ViewDetails *view_details = (ViewDetails *) sortable;
549 	FilerWindow *filer_window = view_details->filer_window;
550 
551 	if (!filer_window)
552 		return;		/* Not yet initialised */
553 
554 	switch (sort_column_id)
555 	{
556 		case COL_LEAF:
557 			display_set_sort_type(filer_window, SORT_NAME, order);
558 			break;
559 		case COL_SIZE:
560 			display_set_sort_type(filer_window, SORT_SIZE, order);
561 			break;
562 		case COL_MTIME:
563 			display_set_sort_type(filer_window, SORT_DATE, order);
564 			break;
565 		case COL_TYPE:
566 			display_set_sort_type(filer_window, SORT_TYPE, order);
567 			break;
568 		case COL_OWNER:
569 			display_set_sort_type(filer_window, SORT_OWNER, order);
570 			break;
571 		case COL_GROUP:
572 			display_set_sort_type(filer_window, SORT_GROUP, order);
573 			break;
574 		default:
575 			g_assert_not_reached();
576 	}
577 }
578 
details_set_sort_func(GtkTreeSortable * sortable,gint sort_column_id,GtkTreeIterCompareFunc func,gpointer data,GtkDestroyNotify destroy)579 static void details_set_sort_func(GtkTreeSortable          *sortable,
580 			          gint                    sort_column_id,
581 			          GtkTreeIterCompareFunc  func,
582 			          gpointer                data,
583 			          GtkDestroyNotify        destroy)
584 {
585 	g_assert_not_reached();
586 }
587 
details_set_default_sort_func(GtkTreeSortable * sortable,GtkTreeIterCompareFunc func,gpointer data,GtkDestroyNotify destroy)588 static void details_set_default_sort_func(GtkTreeSortable        *sortable,
589 				          GtkTreeIterCompareFunc  func,
590 				          gpointer                data,
591 				          GtkDestroyNotify        destroy)
592 {
593 	g_assert_not_reached();
594 }
595 
details_has_default_sort_func(GtkTreeSortable * sortable)596 static gboolean details_has_default_sort_func(GtkTreeSortable *sortable)
597 {
598 	return FALSE;
599 }
600 
601 
602 /* End of model implementation */
603 
is_selected(ViewDetails * view_details,int i)604 static gboolean is_selected(ViewDetails *view_details, int i)
605 {
606 	ViewIter iter;
607 	iter.i = i;
608 	return view_details_get_selected((ViewIface *) view_details, &iter);
609 }
610 
view_details_scroll(GtkWidget * widget,GdkEventScroll * event)611 static gboolean view_details_scroll(GtkWidget *widget, GdkEventScroll *event)
612 {
613 	GtkTreeView *tree = (GtkTreeView *) widget;
614 	GtkTreePath *path = NULL;
615 
616 	if (!gtk_tree_view_get_path_at_pos(tree, 0, 1, &path, NULL, NULL, NULL))
617 		return TRUE;	/* Empty? */
618 
619 	if (event->direction == GDK_SCROLL_UP)
620 		gtk_tree_path_prev(path);
621 	else if (event->direction == GDK_SCROLL_DOWN)
622 		gtk_tree_path_next(path);
623 	else
624 		goto out;
625 
626 	gtk_tree_view_scroll_to_cell(tree, path, NULL, TRUE, 0, 0);
627 out:
628 	gtk_tree_path_free(path);
629 	return TRUE;
630 }
631 
view_details_key_press(GtkWidget * widget,GdkEventKey * event)632 static gint view_details_key_press(GtkWidget *widget, GdkEventKey *event)
633 {
634 	if (event->keyval == GDK_Up || event->keyval == GDK_Down ||
635 	    event->keyval == GDK_Prior || event->keyval == GDK_Next ||
636 	    event->keyval == GDK_Home || event->keyval == GDK_End) {
637 		/* Work around a strange GTK bug that prevents you from moving the cursor
638 		 * if nothing is selected.
639 		 */
640 		if (event->state & GDK_CONTROL_MASK)
641 		{
642 			return GTK_WIDGET_CLASS(parent_class)->key_press_event(widget, event);
643 		}
644 		else
645 		{
646 			/* GTK hard-codes the test for CTRL, and won't move the cursor
647 			 * if it isn't set.
648 			 */
649 			event->state |= GDK_CONTROL_MASK;
650 			gtk_propagate_event(widget, (GdkEvent *) event);
651 			return TRUE;
652 		}
653 	}
654 	return FALSE;
655 }
656 
view_details_button_press(GtkWidget * widget,GdkEventButton * bev)657 static gboolean view_details_button_press(GtkWidget *widget,
658 					  GdkEventButton *bev)
659 {
660 	FilerWindow *filer_window = ((ViewDetails *) widget)->filer_window;
661 	GtkTreeView *tree = (GtkTreeView *) widget;
662 
663 	if (bev->window != gtk_tree_view_get_bin_window(tree))
664 		return GTK_WIDGET_CLASS(parent_class)->button_press_event(
665 								widget, bev);
666 
667 	if (dnd_motion_press(widget, bev))
668 		filer_perform_action(filer_window, bev);
669 
670 	return TRUE;
671 }
672 
get_lasso_index(ViewDetails * view_details,int y)673 static int get_lasso_index(ViewDetails *view_details, int y)
674 {
675 	GtkTreeViewColumn *column = NULL;
676 	GtkTreePath *path = NULL;
677 	GtkTreeView *tree = (GtkTreeView *) view_details;
678 	gint cell_y;
679 	int i;
680 
681 	if (y < 0)
682 		y = 0;	/* gtk_tree_view_get_path_at_pos can't handle negatives */
683 
684 	if (gtk_tree_view_get_path_at_pos(tree, 4, y, &path,
685 				&column, NULL, &cell_y))
686 	{
687 		GdkRectangle rect;
688 		i = gtk_tree_path_get_indices(path)[0];
689 		gtk_tree_view_get_cell_area(tree, path, column, &rect);
690 		gtk_tree_path_free(path);
691 
692 		if (2 * cell_y > rect.height)
693 			i += 1;
694 	}
695 	else
696 		i = view_details->items->len;
697 
698 	return i;
699 }
700 
select_lasso_cb(ViewIter * iter,gpointer data)701 static gboolean select_lasso_cb(ViewIter *iter, gpointer data)
702 {
703 	int start = ((int *) data)[0];
704 	int end = ((int *) data)[1];
705 	GdkFunction fn = ((int *) data)[2];
706 	ViewDetails *view_details = (ViewDetails *) iter->view;
707 	GtkTreeIter  titer;
708 
709 	titer.user_data = GINT_TO_POINTER(iter->i);
710 
711 	if (iter->i < start || iter->i >= end)
712 		return gtk_tree_selection_iter_is_selected(
713 				view_details->selection, &titer);
714 
715 	if (fn == GDK_SET)
716 		return TRUE;
717 
718 	return !gtk_tree_selection_iter_is_selected(view_details->selection,
719 						    &titer);
720 }
721 
select_lasso(ViewDetails * view_details,GdkFunction fn)722 static void select_lasso(ViewDetails *view_details, GdkFunction fn)
723 {
724 	GtkAdjustment	*adj;
725 	int range[3];
726 
727 	adj = gtk_tree_view_get_vadjustment((GtkTreeView *) view_details);
728 
729 	range[0] = view_details->lasso_start_index;
730 	range[1] = get_lasso_index(view_details,
731 			      view_details->drag_box_y[1] - adj->value);
732 	range[2] = fn;
733 
734 	if (range[0] == range[1])
735 		return;
736 	if (range[0] > range[1])
737 	{
738 		int tmp = range[0];
739 		range[0] = range[1];
740 		range[1] = tmp;
741 	}
742 
743 	view_select_if((ViewIface *) view_details, select_lasso_cb, &range);
744 }
745 
view_details_button_release(GtkWidget * widget,GdkEventButton * bev)746 static gboolean view_details_button_release(GtkWidget *widget,
747 					    GdkEventButton *bev)
748 {
749 	ViewDetails *view_details = (ViewDetails *) widget;
750 	FilerWindow *filer_window = view_details->filer_window;
751 	GtkTreeView *tree = (GtkTreeView *) widget;
752 
753 	if (bev->window != gtk_tree_view_get_bin_window(tree))
754 		return GTK_WIDGET_CLASS(parent_class)->button_release_event(
755 								widget, bev);
756 
757 	if (!dnd_motion_release(bev))
758 		filer_perform_action(filer_window, bev);
759 
760 	if (motion_buttons_pressed == 0 && view_details->lasso_box)
761 	{
762 		select_lasso(view_details,
763 				bev->button == 1 ? GDK_SET : GDK_INVERT);
764 		filer_set_autoscroll(filer_window, FALSE);
765 		set_lasso(view_details,
766 			  view_details->drag_box_x[0],
767 			  view_details->drag_box_y[0]);
768 		view_details->lasso_box = FALSE;
769 	}
770 
771 	return TRUE;
772 }
773 
view_details_motion_notify(GtkWidget * widget,GdkEventMotion * event)774 static gint view_details_motion_notify(GtkWidget *widget, GdkEventMotion *event)
775 {
776 	ViewDetails *view_details = (ViewDetails *) widget;
777 	GtkTreeView *tree = (GtkTreeView *) widget;
778 
779 	if (event->window != gtk_tree_view_get_bin_window(tree))
780 		return GTK_WIDGET_CLASS(parent_class)->motion_notify_event(
781 								widget, event);
782 
783 	if (view_details->lasso_box)
784 	{
785 		GtkAdjustment	*adj;
786 		adj = gtk_tree_view_get_vadjustment(tree);
787 		set_lasso(view_details, event->x, event->y + adj->value);
788 		return TRUE;
789 	}
790 
791 	return filer_motion_notify(view_details->filer_window, event);
792 }
793 
view_details_expose(GtkWidget * widget,GdkEventExpose * event)794 static gboolean view_details_expose(GtkWidget *widget, GdkEventExpose *event)
795 {
796 	GtkTreeView *tree = (GtkTreeView *) widget;
797 	GtkTreePath *path = NULL;
798 	GdkRectangle focus_rectangle;
799 	ViewDetails *view_details = (ViewDetails *) widget;
800 	gboolean    had_cursor;
801 
802 	had_cursor = (GTK_WIDGET_FLAGS(widget) & GTK_HAS_FOCUS) != 0;
803 
804 	if (view_details->filer_window->selection_state == GTK_STATE_SELECTED)
805 		GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
806 	else
807 		GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS);
808 	GTK_WIDGET_CLASS(parent_class)->expose_event(widget, event);
809 	if (had_cursor)
810 		GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
811 
812 	if (event->window != gtk_tree_view_get_bin_window(tree))
813 		return FALSE;	/* Not the main area */
814 
815 	if (view_details->lasso_box)
816 	{
817 		int x, y, width, height;
818 		GtkAdjustment *adj;
819 
820 		adj = gtk_tree_view_get_vadjustment(tree);
821 		x = MIN(view_details->drag_box_x[0],
822 				view_details->drag_box_x[1]);
823 		y = MIN(view_details->drag_box_y[0],
824 				view_details->drag_box_y[1]);
825 		width = abs(view_details->drag_box_x[1] -
826 				view_details->drag_box_x[0]);
827 		height = abs(view_details->drag_box_y[1] -
828 				view_details->drag_box_y[0]);
829 		y -= adj->value;
830 
831 		if (width && height)
832 			gdk_draw_rectangle(event->window,
833 				widget->style->fg_gc[GTK_STATE_NORMAL],
834 				FALSE, x, y, width - 1, height - 1);
835 	}
836 
837 	if (view_details->wink_item != -1 && view_details->wink_step & 1)
838 	{
839 		GtkTreePath *wink_path;
840 		GdkRectangle wink_area;
841 
842 		wink_path = gtk_tree_path_new();
843 		gtk_tree_path_append_index(wink_path, view_details->wink_item);
844 		gtk_tree_view_get_background_area(tree, wink_path,
845 					NULL, &wink_area);
846 		gtk_tree_path_free(wink_path);
847 
848 		if (wink_area.height)
849 		{
850 			/* (visible) */
851 			wink_area.width = widget->allocation.width;
852 			gdk_draw_rectangle(event->window,
853 				widget->style->fg_gc[GTK_STATE_NORMAL],
854 					FALSE,
855 					wink_area.x + 1,
856 					wink_area.y + 1,
857 					wink_area.width - 3,
858 					wink_area.height - 3);
859 		}
860 	}
861 
862 	gtk_tree_view_get_cursor(tree, &path, NULL);
863 	if (!path)
864 		return FALSE;	/* No cursor */
865 	gtk_tree_view_get_background_area(tree, path, NULL, &focus_rectangle);
866 	gtk_tree_path_free(path);
867 
868 	if (!focus_rectangle.height)
869 		return FALSE;	/* Off screen */
870 
871 	focus_rectangle.width = widget->allocation.width;
872 
873 	gtk_paint_focus(widget->style,
874 			event->window,
875 			GTK_STATE_NORMAL,
876 			NULL,
877 			widget,
878 			"treeview",
879 			focus_rectangle.x,
880 			focus_rectangle.y,
881 			focus_rectangle.width,
882 			focus_rectangle.height);
883 
884 	return FALSE;
885 }
886 
view_details_size_request(GtkWidget * widget,GtkRequisition * requisition)887 static void view_details_size_request(GtkWidget *widget,
888 				      GtkRequisition *requisition)
889 {
890 	ViewDetails *view_details = (ViewDetails *) widget;
891 
892 	(*GTK_WIDGET_CLASS(parent_class)->size_request)(widget, requisition);
893 
894 	view_details->desired_size = *requisition;
895 
896 	requisition->height = 50;
897 	requisition->width = 50;
898 }
899 
view_details_drag_data_received(GtkWidget * widget,GdkDragContext * drag_context,gint x,gint y,GtkSelectionData * data,guint info,guint time)900 static void view_details_drag_data_received(GtkWidget *widget,
901 		GdkDragContext *drag_context,
902 		gint x, gint y, GtkSelectionData *data, guint info, guint time)
903 {
904 	/* Just here to override annoying default handler */
905 }
906 
view_details_destroy(GtkObject * obj)907 static void view_details_destroy(GtkObject *obj)
908 {
909 	ViewDetails *view_details = VIEW_DETAILS(obj);
910 
911 	view_details->filer_window = NULL;
912 	cancel_wink(view_details);
913 }
914 
view_details_finialize(GObject * object)915 static void view_details_finialize(GObject *object)
916 {
917 	ViewDetails *view_details = (ViewDetails *) object;
918 
919 	g_ptr_array_free(view_details->items, TRUE);
920 	view_details->items = NULL;
921 
922 	G_OBJECT_CLASS(parent_class)->finalize(object);
923 }
924 
view_details_class_init(gpointer gclass,gpointer data)925 static void view_details_class_init(gpointer gclass, gpointer data)
926 {
927 	GObjectClass *object = (GObjectClass *) gclass;
928 	GtkWidgetClass *widget = (GtkWidgetClass *) gclass;
929 
930 	parent_class = g_type_class_peek_parent(gclass);
931 
932 	object->finalize = view_details_finialize;
933 	GTK_OBJECT_CLASS(object)->destroy = view_details_destroy;
934 
935 	widget->scroll_event = view_details_scroll;
936 	widget->key_press_event = view_details_key_press;
937 	widget->button_press_event = view_details_button_press;
938 	widget->button_release_event = view_details_button_release;
939 	widget->motion_notify_event = view_details_motion_notify;
940 	widget->expose_event = view_details_expose;
941 	widget->size_request = view_details_size_request;
942 	widget->drag_data_received = view_details_drag_data_received;
943 
944 	/*
945 	 * Add the ViewDetails::mono-font style property.
946 	 * To use this add something like
947 	 *
948 	 * style "details" {
949 	 *   ViewDetails::mono-font = "Courier 8"
950 	 * }
951 	 * class "ViewDetails" style "details"
952 	 *
953 	 * to your ~/.gtkrc-2.0
954 	 */
955 	gtk_widget_class_install_style_property(widget,
956 		       g_param_spec_string("mono-font",
957 					   _("Mono font"),
958 					   _("Font for displaying mono-spaced text"),
959 					   "monospace",
960 					   G_PARAM_READABLE));
961 
962 }
963 
block_focus(GtkWidget * button,GtkDirectionType dir,ViewDetails * view_details)964 static gboolean block_focus(GtkWidget *button, GtkDirectionType dir,
965 			    ViewDetails *view_details)
966 {
967 	GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
968 	return FALSE;
969 }
970 
test_can_change_selection(GtkTreeSelection * sel,GtkTreeModel * model,GtkTreePath * path,gboolean path_currently_selected,gpointer data)971 static gboolean test_can_change_selection(GtkTreeSelection *sel,
972                                           GtkTreeModel *model,
973                                           GtkTreePath *path,
974                                           gboolean path_currently_selected,
975                                           gpointer data)
976 {
977 	ViewDetails *view_details;
978 
979 	view_details = VIEW_DETAILS(gtk_tree_selection_get_tree_view(sel));
980 
981 	return view_details->can_change_selection != 0;
982 }
983 
selection_changed(GtkTreeSelection * selection,gpointer user_data)984 static void selection_changed(GtkTreeSelection *selection,
985 			      gpointer user_data)
986 {
987 	ViewDetails *view_details = VIEW_DETAILS(user_data);
988 
989 	filer_selection_changed(view_details->filer_window,
990 			gtk_get_current_event_time());
991 }
992 
993 /*
994  * Set the font used for a treeview column to that given for the
995  * "mono-font" style property of a widget.  This has to be done _after_ the
996  * widget has been realized, because that is when the style information is
997  * set up.  This is done by connecting this function to run after the
998  * "realize" signal.
999  */
set_column_mono_font(GtkWidget * widget,GObject * object)1000 static void set_column_mono_font(GtkWidget *widget, GObject *object)
1001 {
1002 	const gchar *font_name;
1003 
1004 	gtk_widget_style_get(widget, "mono-font", &font_name, NULL);
1005 	g_object_set(object, "font", font_name, NULL);
1006 }
1007 
1008 #define ADD_TEXT_COLUMN(name, model_column) \
1009 	cell = gtk_cell_renderer_text_new();	\
1010 	column = gtk_tree_view_column_new_with_attributes(name, cell, \
1011 					    "text", model_column,	\
1012 					    "foreground-gdk", COL_COLOUR, \
1013 					    "weight", COL_WEIGHT, 	\
1014 					    NULL);			\
1015 	gtk_tree_view_append_column(treeview, column);			\
1016 	g_signal_connect(column->button, "grab-focus",			\
1017 			G_CALLBACK(block_focus), view_details);
1018 
view_details_init(GTypeInstance * object,gpointer gclass)1019 static void view_details_init(GTypeInstance *object, gpointer gclass)
1020 {
1021 	GtkTreeView *treeview = (GtkTreeView *) object;
1022 	GtkTreeViewColumn *column;
1023 	GtkCellRenderer *cell;
1024 	GtkTreeSortable *sortable_list;
1025 	ViewDetails *view_details = (ViewDetails *) object;
1026 
1027 	view_details->items = g_ptr_array_new();
1028 	view_details->cursor_base = -1;
1029 	view_details->wink_item = -1;
1030 	view_details->desired_size.width = -1;
1031 	view_details->desired_size.height = -1;
1032 	view_details->can_change_selection = 0;
1033 	view_details->lasso_box = FALSE;
1034 
1035 	view_details->selection = gtk_tree_view_get_selection(treeview);
1036 	gtk_tree_selection_set_mode(view_details->selection,
1037 				GTK_SELECTION_MULTIPLE);
1038 	gtk_tree_selection_set_select_function(view_details->selection,
1039 			test_can_change_selection, view_details, NULL);
1040 
1041 	/* Sorting */
1042 	view_details->sort_fn = NULL;
1043 	sortable_list = GTK_TREE_SORTABLE(object);
1044 
1045 	gtk_tree_view_set_model(treeview, GTK_TREE_MODEL(view_details));
1046 	/* Do this after set_model, because that can generate this
1047 	 * signal...
1048 	 */
1049 	g_signal_connect(view_details->selection, "changed",
1050 			G_CALLBACK(selection_changed), view_details);
1051 
1052 	/* Icon */
1053 	cell = cell_icon_new(view_details);
1054 	column = gtk_tree_view_column_new_with_attributes(NULL, cell,
1055 					    "item", COL_VIEW_ITEM,
1056 					    NULL);
1057 	gtk_tree_view_append_column(treeview, column);
1058 
1059 	ADD_TEXT_COLUMN(_("_Name"), COL_LEAF);
1060 	gtk_tree_view_column_set_sort_column_id(column, COL_LEAF);
1061 	gtk_tree_view_column_set_resizable(column, TRUE);
1062 	ADD_TEXT_COLUMN(_("_Type"), COL_TYPE);
1063 	gtk_tree_view_column_set_sort_column_id(column, COL_TYPE);
1064 	gtk_tree_view_column_set_resizable(column, TRUE);
1065 	ADD_TEXT_COLUMN(_("_Permissions"), COL_PERM);
1066 	g_object_set(G_OBJECT(cell), "font", "monospace", NULL);
1067 	g_signal_connect_after(object, "realize",
1068 			       G_CALLBACK(set_column_mono_font),
1069 			       G_OBJECT(cell));
1070 	ADD_TEXT_COLUMN(_("_Owner"), COL_OWNER);
1071 	gtk_tree_view_column_set_sort_column_id(column, COL_OWNER);
1072 	ADD_TEXT_COLUMN(_("_Group"), COL_GROUP);
1073 	gtk_tree_view_column_set_sort_column_id(column, COL_GROUP);
1074 	ADD_TEXT_COLUMN(_("_Size"), COL_SIZE);
1075 	g_object_set(G_OBJECT(cell), "xalign", 1.0, "font", "monospace", NULL);
1076 	g_signal_connect_after(object, "realize",
1077 			       G_CALLBACK(set_column_mono_font),
1078 			       G_OBJECT(cell));
1079 	gtk_tree_view_column_set_sort_column_id(column, COL_SIZE);
1080 	ADD_TEXT_COLUMN(_("Last _Modified"), COL_MTIME);
1081 	gtk_tree_view_column_set_sort_column_id(column, COL_MTIME);
1082 }
1083 
1084 /* Create the handers for the View interface */
view_details_iface_init(gpointer giface,gpointer iface_data)1085 static void view_details_iface_init(gpointer giface, gpointer iface_data)
1086 {
1087 	ViewIfaceClass *iface = giface;
1088 
1089 	g_assert(G_TYPE_FROM_INTERFACE(iface) == VIEW_TYPE_IFACE);
1090 
1091 	/* override stuff */
1092 	iface->sort = view_details_sort;
1093 	iface->style_changed = view_details_style_changed;
1094 	iface->add_items = view_details_add_items;
1095 	iface->update_items = view_details_update_items;
1096 	iface->delete_if = view_details_delete_if;
1097 	iface->clear = view_details_clear;
1098 	iface->select_all = view_details_select_all;
1099 	iface->clear_selection = view_details_clear_selection;
1100 	iface->count_items = view_details_count_items;
1101 	iface->count_selected = view_details_count_selected;
1102 	iface->show_cursor = view_details_show_cursor;
1103 	iface->get_iter = view_details_get_iter;
1104 	iface->get_iter_at_point = view_details_get_iter_at_point;
1105 	iface->cursor_to_iter = view_details_cursor_to_iter;
1106 	iface->set_selected = view_details_set_selected;
1107 	iface->get_selected = view_details_get_selected;
1108 	iface->set_frozen = view_details_set_frozen;
1109 	iface->select_only = view_details_select_only;
1110 	iface->wink_item = view_details_wink_item;
1111 	iface->autosize = view_details_autosize;
1112 	iface->cursor_visible = view_details_cursor_visible;
1113 	iface->set_base = view_details_set_base;
1114 	iface->start_lasso_box = view_details_start_lasso_box;
1115 	iface->extend_tip = view_details_extend_tip;
1116 	iface->auto_scroll_callback = view_details_auto_scroll_callback;
1117 }
1118 
1119 /* Implementations of the View interface. See view_iface.c for comments. */
1120 
view_details_style_changed(ViewIface * view,int flags)1121 static void view_details_style_changed(ViewIface *view, int flags)
1122 {
1123 	ViewDetails *view_details = (ViewDetails *) view;
1124 	GtkTreeModel *model = (GtkTreeModel *) view;
1125 	GtkTreePath *path;
1126 	ViewItem    **items = (ViewItem **) view_details->items->pdata;
1127 	int i;
1128 	int n = view_details->items->len;
1129 
1130 	path = gtk_tree_path_new();
1131 	gtk_tree_path_append_index(path, 0);
1132 
1133 	for (i = 0; i < n; i++)
1134 	{
1135 		GtkTreeIter iter;
1136 		ViewItem    *item = items[i];
1137 
1138 		iter.user_data = GINT_TO_POINTER(i);
1139 		if (item->image)
1140 		{
1141 			g_object_unref(G_OBJECT(item->image));
1142 			item->image = NULL;
1143 		}
1144 		gtk_tree_model_row_changed(model, path, &iter);
1145 		gtk_tree_path_next(path);
1146 	}
1147 
1148 	gtk_tree_path_free(path);
1149 
1150 	gtk_tree_view_columns_autosize((GtkTreeView *) view);
1151 
1152 	if (flags & VIEW_UPDATE_HEADERS)
1153 		details_update_header_visibility(view_details);
1154 }
1155 
wrap_sort(gconstpointer a,gconstpointer b,ViewDetails * view_details)1156 static gint wrap_sort(gconstpointer a, gconstpointer b,
1157 		      ViewDetails *view_details)
1158 {
1159 	ViewItem *ia = *(ViewItem **) a;
1160 	ViewItem *ib = *(ViewItem **) b;
1161 
1162 	if (view_details->filer_window->sort_order == GTK_SORT_ASCENDING)
1163 		return view_details->sort_fn(ia->item, ib->item);
1164 	else
1165 		return -view_details->sort_fn(ia->item, ib->item);
1166 }
1167 
resort(ViewDetails * view_details)1168 static void resort(ViewDetails *view_details)
1169 {
1170 	ViewItem **items = (ViewItem **) view_details->items->pdata;
1171 	gint i, len = view_details->items->len;
1172 	guint *new_order;
1173 	GtkTreePath *path;
1174 	int wink_item = view_details->wink_item;
1175 
1176 	if (!len)
1177 		return;
1178 
1179 	for (i = len - 1; i >= 0; i--)
1180 		items[i]->old_pos = i;
1181 
1182 	switch (view_details->filer_window->sort_type)
1183 	{
1184 		case SORT_NAME: view_details->sort_fn = sort_by_name; break;
1185 		case SORT_TYPE: view_details->sort_fn = sort_by_type; break;
1186 		case SORT_DATE: view_details->sort_fn = sort_by_date; break;
1187 		case SORT_SIZE: view_details->sort_fn = sort_by_size; break;
1188 		case SORT_OWNER: view_details->sort_fn = sort_by_owner; break;
1189 		case SORT_GROUP: view_details->sort_fn = sort_by_group; break;
1190 		default:
1191 			g_assert_not_reached();
1192 	}
1193 
1194 	g_ptr_array_sort_with_data(view_details->items,
1195 				   (GCompareDataFunc) wrap_sort,
1196 				   view_details);
1197 
1198 	new_order = g_new(guint, len);
1199 	for (i = len - 1; i >= 0; i--)
1200 	{
1201 		new_order[i] = items[i]->old_pos;
1202 		if (wink_item == items[i]->old_pos)
1203 			wink_item = i;
1204 	}
1205 
1206 	view_details->wink_item = wink_item;
1207 
1208 	path = gtk_tree_path_new();
1209 	gtk_tree_model_rows_reordered((GtkTreeModel *) view_details,
1210 					path, NULL, new_order);
1211 	gtk_tree_path_free(path);
1212 	g_free(new_order);
1213 }
1214 
view_details_sort(ViewIface * view)1215 static void view_details_sort(ViewIface *view)
1216 {
1217 	resort((ViewDetails *) view);
1218 	gtk_tree_sortable_sort_column_changed((GtkTreeSortable *) view);
1219 }
1220 
view_details_add_items(ViewIface * view,GPtrArray * new_items)1221 static void view_details_add_items(ViewIface *view, GPtrArray *new_items)
1222 {
1223 	ViewDetails *view_details = (ViewDetails *) view;
1224 	FilerWindow *filer_window = view_details->filer_window;
1225 	GPtrArray *items = view_details->items;
1226 	GtkTreeIter iter;
1227 	int i;
1228 	GtkTreePath *path;
1229 	GtkTreeModel *model = (GtkTreeModel *) view;
1230 
1231 	iter.user_data = GINT_TO_POINTER(items->len);
1232 	path = details_get_path(model, &iter);
1233 
1234 	for (i = 0; i < new_items->len; i++)
1235 	{
1236 		DirItem *item = (DirItem *) new_items->pdata[i];
1237 		char	*leafname = item->leafname;
1238 		ViewItem *vitem;
1239 
1240 		if(!filer_match_filter(filer_window, item))
1241 			continue;
1242 		if (leafname[0] == '.')
1243 		{
1244 			if (leafname[1] == '\0')
1245 				continue; /* Never show '.' */
1246 
1247 			if (leafname[1] == '.' &&
1248 					leafname[2] == '\0')
1249 				continue; /* Never show '..' */
1250 		}
1251 
1252 		vitem = g_new(ViewItem, 1);
1253 		vitem->item = item;
1254 		vitem->image = NULL;
1255 		if (!g_utf8_validate(leafname, -1, NULL))
1256 			vitem->utf8_name = to_utf8(leafname);
1257 		else
1258 			vitem->utf8_name = NULL;
1259 
1260 		g_ptr_array_add(items, vitem);
1261 
1262 		iter.user_data = GINT_TO_POINTER(items->len - 1);
1263 		gtk_tree_model_row_inserted(model, path, &iter);
1264 		gtk_tree_path_next(path);
1265 	}
1266 
1267 	gtk_tree_path_free(path);
1268 
1269 	resort(view_details);
1270 }
1271 
1272 /* Find an item in the sorted array.
1273  * Returns the item number, or -1 if not found.
1274  */
details_find_item(ViewDetails * view_details,DirItem * item)1275 static int details_find_item(ViewDetails *view_details, DirItem *item)
1276 {
1277 	ViewItem **items, tmp, *tmpp;
1278 	int	lower, upper;
1279 
1280 	g_return_val_if_fail(view_details != NULL, -1);
1281 	g_return_val_if_fail(item != NULL, -1);
1282 
1283 	tmp.item = item;
1284 	tmpp = &tmp;
1285 
1286 	items = (ViewItem **) view_details->items->pdata;
1287 
1288 	/* If item is here, then: lower <= i < upper */
1289 	lower = 0;
1290 	upper = view_details->items->len;
1291 
1292 	while (lower < upper)
1293 	{
1294 		int	i, cmp;
1295 
1296 		i = (lower + upper) >> 1;
1297 
1298 		cmp = wrap_sort(&items[i], &tmpp, view_details);
1299 		if (cmp == 0)
1300 			return i;
1301 
1302 		if (cmp > 0)
1303 			upper = i;
1304 		else
1305 			lower = i + 1;
1306 	}
1307 
1308 	return -1;
1309 }
1310 
view_details_update_items(ViewIface * view,GPtrArray * items)1311 static void view_details_update_items(ViewIface *view, GPtrArray *items)
1312 {
1313 	ViewDetails	*view_details = (ViewDetails *) view;
1314 	FilerWindow	*filer_window = view_details->filer_window;
1315 	int		i;
1316 	GtkTreeModel	*model = (GtkTreeModel *) view_details;
1317 
1318 	g_return_if_fail(items->len > 0);
1319 
1320 	/* The item data has already been modified, so this gives the
1321 	 * final sort order...
1322 	 */
1323 	resort(view_details);
1324 
1325 	for (i = 0; i < items->len; i++)
1326 	{
1327 		DirItem *item = (DirItem *) items->pdata[i];
1328 		const gchar *leafname = item->leafname;
1329 		int j;
1330 
1331 		if (!filer_match_filter(filer_window, item))
1332 			continue;
1333 
1334 		j = details_find_item(view_details, item);
1335 
1336 		if (j < 0)
1337 			g_warning("Failed to find '%s'\n", leafname);
1338 		else
1339 		{
1340 			GtkTreePath *path;
1341 			GtkTreeIter iter;
1342 			ViewItem *view_item = view_details->items->pdata[j];
1343 			if (view_item->image)
1344 			{
1345 				g_object_unref(G_OBJECT(view_item->image));
1346 				view_item->image = NULL;
1347 			}
1348 			path = gtk_tree_path_new();
1349 			gtk_tree_path_append_index(path, j);
1350 			iter.user_data = GINT_TO_POINTER(j);
1351 			gtk_tree_model_row_changed(model, path, &iter);
1352 		}
1353 	}
1354 }
1355 
view_details_delete_if(ViewIface * view,gboolean (* test)(gpointer item,gpointer data),gpointer data)1356 static void view_details_delete_if(ViewIface *view,
1357 			  gboolean (*test)(gpointer item, gpointer data),
1358 			  gpointer data)
1359 {
1360 	GtkTreePath *path;
1361 	ViewDetails *view_details = (ViewDetails *) view;
1362 	int	    i = 0;
1363 	GPtrArray   *items = view_details->items;
1364 	GtkTreeModel *model = (GtkTreeModel *) view;
1365 
1366 	path = gtk_tree_path_new();
1367 
1368 	gtk_tree_path_append_index(path, i);
1369 
1370 	while (i < items->len)
1371 	{
1372 		ViewItem *item = items->pdata[i];
1373 
1374 		if (test(item->item, data))
1375 		{
1376 			free_view_item(items->pdata[i]);
1377 			g_ptr_array_remove_index(items, i);
1378 			gtk_tree_model_row_deleted(model, path);
1379 		}
1380 		else
1381 		{
1382 			i++;
1383 			gtk_tree_path_next(path);
1384 		}
1385 	}
1386 
1387 	gtk_tree_path_free(path);
1388 }
1389 
view_details_clear(ViewIface * view)1390 static void view_details_clear(ViewIface *view)
1391 {
1392 	GtkTreePath *path;
1393 	GPtrArray *items = ((ViewDetails *) view)->items;
1394 	GtkTreeModel *model = (GtkTreeModel *) view;
1395 
1396 	path = gtk_tree_path_new();
1397 	gtk_tree_path_append_index(path, items->len);
1398 
1399 	while (gtk_tree_path_prev(path))
1400 		gtk_tree_model_row_deleted(model, path);
1401 
1402 	g_ptr_array_set_size(items, 0);
1403 	gtk_tree_path_free(path);
1404 }
1405 
view_details_select_all(ViewIface * view)1406 static void view_details_select_all(ViewIface *view)
1407 {
1408 	ViewDetails *view_details = (ViewDetails *) view;
1409 
1410 	view_details->can_change_selection++;
1411 	gtk_tree_selection_select_all(view_details->selection);
1412 	view_details->can_change_selection--;
1413 }
1414 
view_details_clear_selection(ViewIface * view)1415 static void view_details_clear_selection(ViewIface *view)
1416 {
1417 	ViewDetails *view_details = (ViewDetails *) view;
1418 
1419 	view_details->can_change_selection++;
1420 	gtk_tree_selection_unselect_all(view_details->selection);
1421 	view_details->can_change_selection--;
1422 }
1423 
view_details_count_items(ViewIface * view)1424 static int view_details_count_items(ViewIface *view)
1425 {
1426 	ViewDetails *view_details = (ViewDetails *) view;
1427 
1428 	return view_details->items->len;
1429 }
1430 
1431 #if GTK_MINOR_VERSION < 2
view_details_count_inc(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)1432 static void view_details_count_inc(GtkTreeModel *model, GtkTreePath *path,
1433 				   GtkTreeIter *iter, gpointer data)
1434 {
1435 	int *count = (int *) data;
1436 	(*count) += 1;
1437 }
1438 #endif
1439 
view_details_count_selected(ViewIface * view)1440 static int view_details_count_selected(ViewIface *view)
1441 {
1442 	ViewDetails *view_details = (ViewDetails *) view;
1443 
1444 #if GTK_MINOR_VERSION >= 2
1445 	return gtk_tree_selection_count_selected_rows(view_details->selection);
1446 #else
1447 	int count = 0;
1448 
1449 	gtk_tree_selection_selected_foreach(view_details->selection,
1450 					    view_details_count_inc, &count);
1451 	return count;
1452 #endif
1453 }
1454 
view_details_show_cursor(ViewIface * view)1455 static void view_details_show_cursor(ViewIface *view)
1456 {
1457 }
1458 
view_details_get_iter(ViewIface * view,ViewIter * iter,IterFlags flags)1459 static void view_details_get_iter(ViewIface *view,
1460 				  ViewIter *iter, IterFlags flags)
1461 {
1462 	make_iter((ViewDetails *) view, iter, flags);
1463 }
1464 
view_details_get_iter_at_point(ViewIface * view,ViewIter * iter,GdkWindow * src,int x,int y)1465 static void view_details_get_iter_at_point(ViewIface *view, ViewIter *iter,
1466 					   GdkWindow *src, int x, int y)
1467 {
1468 	ViewDetails *view_details = (ViewDetails *) view;
1469 	GtkTreeModel *model;
1470 	GtkTreeView *tree = (GtkTreeView *) view;
1471 	GtkTreePath *path = NULL;
1472 	int i = -1;
1473 	gint cell_y;
1474 
1475 	model = gtk_tree_view_get_model(tree);
1476 
1477 	if (gtk_tree_view_get_path_at_pos(tree, x, y + 4, &path, NULL,
1478 					  NULL, &cell_y))
1479 	{
1480 		g_return_if_fail(path != NULL);
1481 
1482 		if (cell_y > 8)
1483 			i = gtk_tree_path_get_indices(path)[0];
1484 		gtk_tree_path_free(path);
1485 	}
1486 
1487 	make_item_iter(view_details, iter, i);
1488 }
1489 
view_details_cursor_to_iter(ViewIface * view,ViewIter * iter)1490 static void view_details_cursor_to_iter(ViewIface *view, ViewIter *iter)
1491 {
1492 	GtkTreePath *path;
1493 	ViewDetails *view_details = (ViewDetails *) view;
1494 
1495 	path = gtk_tree_path_new();
1496 
1497 	if (iter)
1498 		gtk_tree_path_append_index(path, iter->i);
1499 	else
1500 	{
1501 		/* Using depth zero or index -1 gives an error, but this
1502 		 * is OK!
1503 		 */
1504 		gtk_tree_path_append_index(path, view_details->items->len);
1505 	}
1506 
1507 	gtk_tree_view_set_cursor((GtkTreeView *) view, path, NULL, FALSE);
1508 	gtk_tree_path_free(path);
1509 }
1510 
set_selected(ViewDetails * view_details,int i,gboolean selected)1511 static void set_selected(ViewDetails *view_details, int i, gboolean selected)
1512 {
1513 	GtkTreeIter iter;
1514 
1515 	iter.user_data = GINT_TO_POINTER(i);
1516 	view_details->can_change_selection++;
1517 	if (selected)
1518 		gtk_tree_selection_select_iter(view_details->selection, &iter);
1519 	else
1520 		gtk_tree_selection_unselect_iter(view_details->selection,
1521 						&iter);
1522 	view_details->can_change_selection--;
1523 }
1524 
view_details_set_selected(ViewIface * view,ViewIter * iter,gboolean selected)1525 static void view_details_set_selected(ViewIface *view,
1526 					 ViewIter *iter,
1527 					 gboolean selected)
1528 {
1529 	set_selected((ViewDetails *) view, iter->i, selected);
1530 }
1531 
get_selected(ViewDetails * view_details,int i)1532 static gboolean get_selected(ViewDetails *view_details, int i)
1533 {
1534 	GtkTreeIter iter;
1535 
1536 	iter.user_data = GINT_TO_POINTER(i);
1537 
1538 	return gtk_tree_selection_iter_is_selected(view_details->selection,
1539 					&iter);
1540 }
1541 
view_details_get_selected(ViewIface * view,ViewIter * iter)1542 static gboolean view_details_get_selected(ViewIface *view, ViewIter *iter)
1543 {
1544 	return get_selected((ViewDetails *) view, iter->i);
1545 }
1546 
view_details_select_only(ViewIface * view,ViewIter * iter)1547 static void view_details_select_only(ViewIface *view, ViewIter *iter)
1548 {
1549 	ViewDetails *view_details = (ViewDetails *) view;
1550 	GtkTreePath *path;
1551 
1552 	path = gtk_tree_path_new();
1553 	gtk_tree_path_append_index(path, iter->i);
1554 	view_details->can_change_selection++;
1555 	gtk_tree_selection_unselect_all(view_details->selection);
1556 	gtk_tree_selection_select_range(view_details->selection, path, path);
1557 	view_details->can_change_selection--;
1558 	gtk_tree_path_free(path);
1559 }
1560 
view_details_set_frozen(ViewIface * view,gboolean frozen)1561 static void view_details_set_frozen(ViewIface *view, gboolean frozen)
1562 {
1563 }
1564 
redraw_wink_area(ViewDetails * view_details)1565 static void redraw_wink_area(ViewDetails *view_details)
1566 {
1567 	GtkTreePath *wink_path;
1568 	GdkRectangle wink_area;
1569 	GtkTreeView *tree = (GtkTreeView *) view_details;
1570 
1571 	g_return_if_fail(view_details->wink_item >= 0);
1572 
1573 	wink_path = gtk_tree_path_new();
1574 	gtk_tree_path_append_index(wink_path, view_details->wink_item);
1575 	gtk_tree_view_get_background_area(tree, wink_path, NULL, &wink_area);
1576 	gtk_tree_path_free(wink_path);
1577 
1578 	if (wink_area.height)
1579 	{
1580 		GdkWindow *window;
1581 		window = gtk_tree_view_get_bin_window(tree);
1582 
1583 		wink_area.width = GTK_WIDGET(tree)->allocation.width;
1584 		gdk_window_invalidate_rect(window, &wink_area, FALSE);
1585 	}
1586 }
1587 
cancel_wink(ViewDetails * view_details)1588 static void cancel_wink(ViewDetails *view_details)
1589 {
1590 	if (view_details->wink_item == -1)
1591 		return;
1592 
1593 	if (view_details->filer_window)
1594 		redraw_wink_area(view_details);
1595 
1596 	view_details->wink_item = -1;
1597 	g_source_remove(view_details->wink_timeout);
1598 }
1599 
wink_timeout(ViewDetails * view_details)1600 static gboolean wink_timeout(ViewDetails *view_details)
1601 {
1602 	view_details->wink_step--;
1603 	if (view_details->wink_step < 1)
1604 	{
1605 		cancel_wink(view_details);
1606 		return FALSE;
1607 	}
1608 
1609 	redraw_wink_area(view_details);
1610 
1611 	return TRUE;
1612 }
1613 
view_details_wink_item(ViewIface * view,ViewIter * iter)1614 static void view_details_wink_item(ViewIface *view, ViewIter *iter)
1615 {
1616 	ViewDetails *view_details = (ViewDetails *) view;
1617 	GtkTreePath *path;
1618 
1619 	cancel_wink(view_details);
1620 	if (!iter)
1621 		return;
1622 
1623 	view_details->wink_item = iter->i;
1624 	view_details->wink_timeout = g_timeout_add(70,
1625 			(GSourceFunc) wink_timeout, view_details);
1626 	view_details->wink_step = 7;
1627 	redraw_wink_area(view_details);
1628 
1629 	path = gtk_tree_path_new();
1630 	gtk_tree_path_append_index(path, iter->i);
1631 	gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(view),
1632 			path, NULL, FALSE, 0, 0);
1633 	gtk_tree_path_free(path);
1634 }
1635 
view_details_autosize(ViewIface * view)1636 static void view_details_autosize(ViewIface *view)
1637 {
1638 	ViewDetails *view_details = (ViewDetails *) view;
1639 	FilerWindow *filer_window = view_details->filer_window;
1640 	int max_width = (o_filer_size_limit.int_value * monitor_width) / 100;
1641 	int max_height = (o_filer_size_limit.int_value * monitor_height) / 100;
1642 	int h;
1643 	GtkRequisition req;
1644 
1645 	gtk_widget_queue_resize(GTK_WIDGET(view));
1646 	gtk_widget_size_request(GTK_WIDGET(view), &req);
1647 
1648 	h = MAX(view_details->desired_size.height, SMALL_HEIGHT);
1649 
1650 	filer_window_set_size(filer_window,
1651 			MIN(view_details->desired_size.width, max_width),
1652 			MIN(h, max_height));
1653 }
1654 
view_details_cursor_visible(ViewIface * view)1655 static gboolean view_details_cursor_visible(ViewIface *view)
1656 {
1657 	GtkTreePath *path = NULL;
1658 
1659 	gtk_tree_view_get_cursor((GtkTreeView *) view, &path, NULL);
1660 
1661 	if (path)
1662 		gtk_tree_path_free(path);
1663 
1664 	return path != NULL;
1665 }
1666 
view_details_set_base(ViewIface * view,ViewIter * iter)1667 static void view_details_set_base(ViewIface *view, ViewIter *iter)
1668 {
1669 	ViewDetails *view_details = (ViewDetails *) view;
1670 
1671 	view_details->cursor_base = iter->i;
1672 }
1673 
1674 /* Change the dynamic corner of the lasso box and trigger a redraw */
set_lasso(ViewDetails * view_details,int x,int y)1675 static void set_lasso(ViewDetails *view_details, int x, int y)
1676 {
1677 	GdkWindow   *window;
1678 	GdkRectangle area;
1679 	int minx, miny, maxx, maxy;
1680 
1681 	if (x == view_details->drag_box_x[1] &&
1682 	    y == view_details->drag_box_y[1])
1683 		return;
1684 
1685 	/* Get old region */
1686 	minx = MIN(view_details->drag_box_x[0], view_details->drag_box_x[1]);
1687 	miny = MIN(view_details->drag_box_y[0], view_details->drag_box_y[1]);
1688 	maxx = MAX(view_details->drag_box_x[0], view_details->drag_box_x[1]);
1689 	maxy = MAX(view_details->drag_box_y[0], view_details->drag_box_y[1]);
1690 
1691 	/* Enlarge to cover new point */
1692 	minx = MIN(minx, x);
1693 	miny = MIN(miny, y);
1694 	maxx = MAX(maxx, x);
1695 	maxy = MAX(maxy, y);
1696 
1697 	area.x = minx;
1698 	area.y = miny;
1699 	area.width = maxx - minx;
1700 	area.height = maxy - miny;
1701 
1702 	view_details->drag_box_x[1] = x;
1703 	view_details->drag_box_y[1] = y;
1704 
1705 	window = gtk_tree_view_get_bin_window((GtkTreeView *) view_details);
1706 	if (area.width && area.height)
1707 	{
1708 		GtkAdjustment *adj;
1709 
1710 		adj = gtk_tree_view_get_vadjustment((GtkTreeView *)
1711 							view_details);
1712 		area.y -= adj->value;
1713 		gdk_window_invalidate_rect(window, &area, FALSE);
1714 	}
1715 }
1716 
view_details_start_lasso_box(ViewIface * view,GdkEventButton * event)1717 static void view_details_start_lasso_box(ViewIface *view, GdkEventButton *event)
1718 {
1719 	ViewDetails *view_details = (ViewDetails *) view;
1720 	GtkAdjustment *adj;
1721 
1722 	adj = gtk_tree_view_get_vadjustment((GtkTreeView *) view_details);
1723 
1724 	view_details->lasso_start_index = get_lasso_index(view_details,
1725 							  event->y);
1726 
1727 	filer_set_autoscroll(view_details->filer_window, TRUE);
1728 
1729 	view_details->drag_box_x[0] = view_details->drag_box_x[1] = event->x;
1730 	view_details->drag_box_y[0] = view_details->drag_box_y[1] = event->y +
1731 								adj->value;
1732 	view_details->lasso_box = TRUE;
1733 }
1734 
view_details_extend_tip(ViewIface * view,ViewIter * iter,GString * tip)1735 static void view_details_extend_tip(ViewIface *view,
1736 				    ViewIter *iter, GString *tip)
1737 {
1738 }
1739 
iter_init(ViewIter * iter)1740 static DirItem *iter_init(ViewIter *iter)
1741 {
1742 	ViewDetails *view_details = (ViewDetails *) iter->view;
1743 	int i = -1;
1744 	int n = view_details->items->len;
1745 	int flags = iter->flags;
1746 
1747 	iter->peek = iter_peek;
1748 
1749 	if (iter->n_remaining == 0)
1750 		return NULL;
1751 
1752 	if (flags & VIEW_ITER_FROM_CURSOR)
1753 	{
1754 		GtkTreePath *path;
1755 		gtk_tree_view_get_cursor((GtkTreeView *) view_details,
1756 					 &path, NULL);
1757 		if (!path)
1758 			return NULL;	/* No cursor */
1759 		i = gtk_tree_path_get_indices(path)[0];
1760 		gtk_tree_path_free(path);
1761 	}
1762 	else if (flags & VIEW_ITER_FROM_BASE)
1763 		i = view_details->cursor_base;
1764 
1765 	if (i < 0 || i >= n)
1766 	{
1767 		/* Either a normal iteration, or an iteration from an
1768 		 * invalid starting point.
1769 		 */
1770 		if (flags & VIEW_ITER_BACKWARDS)
1771 			i = n - 1;
1772 		else
1773 			i = 0;
1774 	}
1775 
1776 	if (i < 0 || i >= n)
1777 		return NULL;	/* No items at all! */
1778 
1779 	iter->next = flags & VIEW_ITER_BACKWARDS ? iter_prev : iter_next;
1780 	iter->n_remaining--;
1781 	iter->i = i;
1782 
1783 	if (flags & VIEW_ITER_SELECTED && !is_selected(view_details, i))
1784 		return iter->next(iter);
1785 	return iter->peek(iter);
1786 }
1787 
iter_prev(ViewIter * iter)1788 static DirItem *iter_prev(ViewIter *iter)
1789 {
1790 	ViewDetails *view_details = (ViewDetails *) iter->view;
1791 	int n = view_details->items->len;
1792 	int i = iter->i;
1793 
1794 	g_return_val_if_fail(iter->n_remaining >= 0, NULL);
1795 
1796 	/* i is the last item returned (always valid) */
1797 
1798 	g_return_val_if_fail(i >= 0 && i < n, NULL);
1799 
1800 	while (iter->n_remaining)
1801 	{
1802 		i--;
1803 		iter->n_remaining--;
1804 
1805 		if (i == -1)
1806 			i = n - 1;
1807 
1808 		g_return_val_if_fail(i >= 0 && i < n, NULL);
1809 
1810 		if (iter->flags & VIEW_ITER_SELECTED &&
1811 		    !is_selected(view_details, i))
1812 			continue;
1813 
1814 		iter->i = i;
1815 		return ((ViewItem *) view_details->items->pdata[i])->item;
1816 	}
1817 
1818 	iter->i = -1;
1819 	return NULL;
1820 }
1821 
iter_next(ViewIter * iter)1822 static DirItem *iter_next(ViewIter *iter)
1823 {
1824 	ViewDetails *view_details = (ViewDetails *) iter->view;
1825 	int n = view_details->items->len;
1826 	int i = iter->i;
1827 
1828 	g_return_val_if_fail(iter->n_remaining >= 0, NULL);
1829 
1830 	/* i is the last item returned (always valid) */
1831 
1832 	g_return_val_if_fail(i >= 0 && i < n, NULL);
1833 
1834 	while (iter->n_remaining)
1835 	{
1836 		i++;
1837 		iter->n_remaining--;
1838 
1839 		if (i == n)
1840 			i = 0;
1841 
1842 		g_return_val_if_fail(i >= 0 && i < n, NULL);
1843 
1844 		if (iter->flags & VIEW_ITER_SELECTED &&
1845 		    !is_selected(view_details, i))
1846 			continue;
1847 
1848 		iter->i = i;
1849 		return ((ViewItem *) view_details->items->pdata[i])->item;
1850 	}
1851 
1852 	iter->i = -1;
1853 	return NULL;
1854 }
1855 
iter_peek(ViewIter * iter)1856 static DirItem *iter_peek(ViewIter *iter)
1857 {
1858 	ViewDetails *view_details = (ViewDetails *) iter->view;
1859 	int n = view_details->items->len;
1860 	int i = iter->i;
1861 
1862 	if (i == -1)
1863 		return NULL;
1864 
1865 	g_return_val_if_fail(i >= 0 && i < n, NULL);
1866 
1867 	return ((ViewItem *) view_details->items->pdata[i])->item;
1868 }
1869 
1870 /* Set the iterator to return 'i' on the next peek().
1871  * If i is -1, returns NULL on next peek().
1872  */
make_item_iter(ViewDetails * view_details,ViewIter * iter,int i)1873 static void make_item_iter(ViewDetails *view_details, ViewIter *iter, int i)
1874 {
1875 	make_iter(view_details, iter, 0);
1876 
1877 	g_return_if_fail(i >= -1 && i < (int) view_details->items->len);
1878 
1879 	iter->i = i;
1880 	iter->next = iter_next;
1881 	iter->peek = iter_peek;
1882 	iter->n_remaining = 0;
1883 }
1884 
make_iter(ViewDetails * view_details,ViewIter * iter,IterFlags flags)1885 static void make_iter(ViewDetails *view_details, ViewIter *iter,
1886 		      IterFlags flags)
1887 {
1888 	iter->view = (ViewIface *) view_details;
1889 	iter->next = iter_init;
1890 	iter->peek = NULL;
1891 	iter->i = -1;
1892 
1893 	iter->flags = flags;
1894 
1895 	if (flags & VIEW_ITER_ONE_ONLY)
1896 	{
1897 		iter->n_remaining = 1;
1898 		iter->next(iter);
1899 	}
1900 	else
1901 		iter->n_remaining = view_details->items->len;
1902 }
1903 
free_view_item(ViewItem * view_item)1904 static void free_view_item(ViewItem *view_item)
1905 {
1906 	if (view_item->image)
1907 		g_object_unref(G_OBJECT(view_item->image));
1908 	g_free(view_item->utf8_name);
1909 	g_free(view_item);
1910 }
1911 
view_details_auto_scroll_callback(ViewIface * view)1912 static gboolean view_details_auto_scroll_callback(ViewIface *view)
1913 {
1914 	GtkTreeView	*tree = (GtkTreeView *) view;
1915 	ViewDetails	*view_details = (ViewDetails *) view;
1916 	FilerWindow	*filer_window = view_details->filer_window;
1917 	GtkRange	*scrollbar = (GtkRange *) filer_window->scrollbar;
1918 	GtkAdjustment	*adj;
1919 	GdkWindow	*window;
1920 	gint		x, y, w, h;
1921 	GdkModifierType	mask;
1922 	int		diff = 0;
1923 
1924 	window = gtk_tree_view_get_bin_window(tree);
1925 
1926 	gdk_window_get_pointer(window, &x, &y, &mask);
1927 	gdk_drawable_get_size(window, &w, NULL);
1928 
1929 	adj = gtk_range_get_adjustment(scrollbar);
1930 	h = adj->page_size;
1931 
1932 	if ((x < 0 || x > w || y < 0 || y > h) && !view_details->lasso_box)
1933 		return FALSE;		/* Out of window - stop */
1934 
1935 	if (y < AUTOSCROLL_STEP)
1936 		diff = y - AUTOSCROLL_STEP;
1937 	else if (y > h - AUTOSCROLL_STEP)
1938 		diff = AUTOSCROLL_STEP + y - h;
1939 
1940 	if (diff)
1941 	{
1942 		int	old = adj->value;
1943 		int	value = old + diff;
1944 
1945 		value = CLAMP(value, 0, adj->upper - adj->page_size);
1946 		gtk_adjustment_set_value(adj, value);
1947 
1948 		if (adj->value != old)
1949 			dnd_spring_abort();
1950 	}
1951 
1952 	return TRUE;
1953 }
1954