r2596: Updated for new version.
[rox-filer.git] / ROX-Filer / src / view_details.c
blobe9670987e3c9c9fb863c89425460c528c6a420ce
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2003, the ROX-Filer team.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* view_details.c - display a list of files in a TreeView */
24 #include "config.h"
26 #include <gtk/gtk.h>
27 #include <gdk/gdkkeysyms.h>
29 #include "global.h"
31 #include "view_iface.h"
32 #include "view_details.h"
33 #include "dir.h"
34 #include "diritem.h"
35 #include "support.h"
36 #include "type.h"
37 #include "filer.h"
38 #include "display.h"
39 #include "pixmaps.h"
40 #include "dnd.h"
41 #include "bind.h"
42 #include "gui_support.h"
43 #include "menu.h"
44 #include "options.h"
45 #include "cell_icon.h"
47 /* These are the column numbers in the ListStore */
48 #define COL_LEAF 0
49 #define COL_TYPE 1
50 #define COL_PERM 2
51 #define COL_OWNER 3
52 #define COL_GROUP 4
53 #define COL_SIZE 5
54 #define COL_MTIME 6
55 #define COL_ITEM 7
56 #define COL_COLOUR 8
57 #define COL_BG_COLOUR 9
58 #define COL_WEIGHT 10
59 #define COL_VIEW_ITEM 11
60 #define N_COLUMNS 12
62 static gpointer parent_class = NULL;
64 struct _ViewDetailsClass {
65 GtkTreeViewClass parent;
68 /* Static prototypes */
69 static void view_details_finialize(GObject *object);
70 static void view_details_class_init(gpointer gclass, gpointer data);
71 static void view_details_init(GTypeInstance *object, gpointer gclass);
73 static void view_details_iface_init(gpointer giface, gpointer iface_data);
75 static void view_details_sort(ViewIface *view);
76 static void view_details_style_changed(ViewIface *view, int flags);
77 static gboolean view_details_autoselect(ViewIface *view, const gchar *leaf);
78 static void view_details_add_items(ViewIface *view, GPtrArray *items);
79 static void view_details_update_items(ViewIface *view, GPtrArray *items);
80 static void view_details_delete_if(ViewIface *view,
81 gboolean (*test)(gpointer item, gpointer data),
82 gpointer data);
83 static void view_details_clear(ViewIface *view);
84 static void view_details_select_all(ViewIface *view);
85 static void view_details_clear_selection(ViewIface *view);
86 static int view_details_count_items(ViewIface *view);
87 static int view_details_count_selected(ViewIface *view);
88 static void view_details_show_cursor(ViewIface *view);
89 static void view_details_get_iter(ViewIface *view,
90 ViewIter *iter, IterFlags flags);
91 static void view_details_get_iter_at_point(ViewIface *view, ViewIter *iter,
92 GdkWindow *src, int x, int y);
93 static void view_details_cursor_to_iter(ViewIface *view, ViewIter *iter);
94 static void view_details_set_selected(ViewIface *view,
95 ViewIter *iter,
96 gboolean selected);
97 static gboolean view_details_get_selected(ViewIface *view, ViewIter *iter);
98 static void view_details_select_only(ViewIface *view, ViewIter *iter);
99 static void view_details_set_frozen(ViewIface *view, gboolean frozen);
100 static void view_details_wink_item(ViewIface *view, ViewIter *iter);
101 static void view_details_autosize(ViewIface *view);
102 static gboolean view_details_cursor_visible(ViewIface *view);
103 static void view_details_set_base(ViewIface *view, ViewIter *iter);
104 static void view_details_start_lasso_box(ViewIface *view,
105 GdkEventButton *event);
106 static void view_details_extend_tip(ViewIface *view,
107 ViewIter *iter, GString *tip);
108 static gboolean view_details_auto_scroll_callback(ViewIface *view);
110 static DirItem *iter_peek(ViewIter *iter);
111 static DirItem *iter_prev(ViewIter *iter);
112 static DirItem *iter_next(ViewIter *iter);
113 static void make_iter(ViewDetails *view_details, ViewIter *iter,
114 IterFlags flags);
115 static void make_item_iter(ViewDetails *view_details, ViewIter *iter, int i);
116 static void view_details_tree_model_init(GtkTreeModelIface *iface);
117 static gboolean details_get_sort_column_id(GtkTreeSortable *sortable,
118 gint *sort_column_id,
119 GtkSortType *order);
120 static void details_set_sort_column_id(GtkTreeSortable *sortable,
121 gint sort_column_id,
122 GtkSortType order);
123 static void details_set_sort_func(GtkTreeSortable *sortable,
124 gint sort_column_id,
125 GtkTreeIterCompareFunc func,
126 gpointer data,
127 GtkDestroyNotify destroy);
128 static void details_set_default_sort_func(GtkTreeSortable *sortable,
129 GtkTreeIterCompareFunc func,
130 gpointer data,
131 GtkDestroyNotify destroy);
132 static gboolean details_has_default_sort_func(GtkTreeSortable *sortable);
133 static void view_details_sortable_init(GtkTreeSortableIface *iface);
134 static void set_selected(ViewDetails *view_details, int i, gboolean selected);
135 static gboolean get_selected(ViewDetails *view_details, int i);
136 static void free_view_item(ViewItem *view_item);
137 static void details_update_header_visibility(ViewDetails *view_details);
140 /****************************************************************
141 * EXTERNAL INTERFACE *
142 ****************************************************************/
144 GtkWidget *view_details_new(FilerWindow *filer_window)
146 ViewDetails *view_details;
148 view_details = g_object_new(view_details_get_type(), NULL);
149 view_details->filer_window = filer_window;
151 gtk_range_set_adjustment(GTK_RANGE(filer_window->scrollbar),
152 gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(view_details)));
154 if (filer_window->sort_type != -1)
155 view_details_sort((ViewIface *) view_details);
157 details_update_header_visibility(view_details);
159 return GTK_WIDGET(view_details);
162 GType view_details_get_type(void)
164 static GType type = 0;
166 if (!type)
168 static const GTypeInfo info =
170 sizeof (ViewDetailsClass),
171 NULL, /* base_init */
172 NULL, /* base_finalise */
173 view_details_class_init,
174 NULL, /* class_finalise */
175 NULL, /* class_data */
176 sizeof(ViewDetails),
177 0, /* n_preallocs */
178 view_details_init
180 static const GInterfaceInfo view_iface_info = {
181 view_details_iface_init,
182 NULL, NULL
184 static const GInterfaceInfo tree_model_info = {
185 (GInterfaceInitFunc) view_details_tree_model_init,
186 NULL, NULL
188 static const GInterfaceInfo sortable_info = {
189 (GInterfaceInitFunc) view_details_sortable_init,
190 NULL, NULL
194 type = g_type_register_static(gtk_tree_view_get_type(),
195 "ViewDetails", &info, 0);
197 g_type_add_interface_static(type, VIEW_TYPE_IFACE,
198 &view_iface_info);
199 g_type_add_interface_static(type, GTK_TYPE_TREE_MODEL,
200 &tree_model_info);
201 g_type_add_interface_static(type, GTK_TYPE_TREE_SORTABLE,
202 &sortable_info);
205 return type;
208 /****************************************************************
209 * INTERNAL FUNCTIONS *
210 ****************************************************************/
212 /* Update the visibility of the list headers */
213 static void details_update_header_visibility(ViewDetails *view_details)
215 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view_details),
216 o_display_show_headers.int_value);
219 /* Fulfill the GtkTreeModel requirements */
220 static guint details_get_flags(GtkTreeModel *tree_model)
222 return GTK_TREE_MODEL_LIST_ONLY;
225 static gint details_get_n_columns(GtkTreeModel *tree_model)
227 return N_COLUMNS;
230 static GType details_get_column_type(GtkTreeModel *tree_model, gint index)
232 g_return_val_if_fail(index < N_COLUMNS && index >= 0, G_TYPE_INVALID);
234 if (index == COL_COLOUR || index == COL_BG_COLOUR)
235 return GDK_TYPE_COLOR;
236 else if (index == COL_ITEM || index == COL_VIEW_ITEM)
237 return G_TYPE_POINTER;
238 else if (index == COL_WEIGHT)
239 return G_TYPE_INT;
240 return G_TYPE_STRING;
243 static gboolean details_get_iter(GtkTreeModel *tree_model,
244 GtkTreeIter *iter,
245 GtkTreePath *path)
247 ViewDetails *view_details = (ViewDetails *) tree_model;
248 gint i;
250 g_return_val_if_fail(gtk_tree_path_get_depth (path) > 0, FALSE);
252 i = gtk_tree_path_get_indices(path)[0];
254 if (i >= view_details->items->len)
255 return FALSE;
257 iter->user_data = GINT_TO_POINTER(i);
259 return TRUE;
262 static GtkTreePath *details_get_path(GtkTreeModel *tree_model,
263 GtkTreeIter *iter)
265 GtkTreePath *retval;
267 retval = gtk_tree_path_new();
268 gtk_tree_path_append_index(retval, GPOINTER_TO_INT(iter->user_data));
270 return retval;
273 static void details_get_value(GtkTreeModel *tree_model,
274 GtkTreeIter *iter,
275 gint column,
276 GValue *value)
278 ViewDetails *view_details = (ViewDetails *) tree_model;
279 gint i;
280 GPtrArray *items = view_details->items;
281 ViewItem *view_item;
282 DirItem *item;
283 mode_t m;
285 g_return_if_fail(column >= 0 && column < N_COLUMNS);
287 i = GPOINTER_TO_INT(iter->user_data);
288 g_return_if_fail(i >= 0 && i < items->len);
289 view_item = (ViewItem *) items->pdata[i];
290 item = view_item->item;
292 if (column == COL_LEAF)
294 g_value_init(value, G_TYPE_STRING);
295 g_value_set_string(value,
296 view_item->utf8_name ? view_item->utf8_name
297 : item->leafname);
298 return;
300 else if (column == COL_VIEW_ITEM)
302 g_value_init(value, G_TYPE_POINTER);
303 g_value_set_pointer(value, view_item);
304 return;
306 else if (column == COL_ITEM)
308 g_value_init(value, G_TYPE_POINTER);
309 g_value_set_pointer(value, item);
310 return;
313 if (item->base_type == TYPE_UNKNOWN)
315 GType type;
316 type = details_get_column_type(tree_model, column);
317 g_value_init(value, type);
318 if (type == G_TYPE_STRING)
319 g_value_set_string(value, "");
320 else if (type == GDK_TYPE_COLOR)
321 g_value_set_boxed(value, NULL);
322 else if (type == G_TYPE_INT)
323 g_value_set_int(value, PANGO_WEIGHT_NORMAL);
324 else
325 g_value_set_object(value, NULL);
327 return;
329 m = item->mode;
331 switch (column)
333 case COL_LEAF:
334 g_value_init(value, G_TYPE_STRING);
335 g_value_set_string(value, item->leafname);
336 break;
337 case COL_COLOUR:
338 g_value_init(value, GDK_TYPE_COLOR);
339 if (view_item->utf8_name)
341 GdkColor red;
342 red.red = 0xffff;
343 red.green = 0;
344 red.blue = 0;
345 g_value_set_boxed(value, &red);
347 else
348 g_value_set_boxed(value,
349 type_get_colour(item, NULL));
350 break;
351 case COL_BG_COLOUR:
352 g_value_init(value, GDK_TYPE_COLOR);
353 #if 0
354 if (view_item->selected)
356 GtkStateType state = view_details->
357 filer_window->selection_state;
358 g_value_set_boxed(value, &style->base[state]);
360 else
361 #endif
362 g_value_set_boxed(value, NULL);
363 break;
364 case COL_OWNER:
365 g_value_init(value, G_TYPE_STRING);
366 g_value_set_string(value, user_name(item->uid));
367 break;
368 case COL_GROUP:
369 g_value_init(value, G_TYPE_STRING);
370 g_value_set_string(value, group_name(item->gid));
371 break;
372 case COL_MTIME:
374 gchar *time;
375 time = pretty_time(&item->mtime);
376 g_value_init(value, G_TYPE_STRING);
377 g_value_set_string(value, time);
378 g_free(time);
379 break;
381 case COL_PERM:
382 g_value_init(value, G_TYPE_STRING);
383 g_value_set_string(value, pretty_permissions(m));
384 break;
385 case COL_SIZE:
386 g_value_init(value, G_TYPE_STRING);
387 if (item->base_type != TYPE_DIRECTORY)
388 g_value_set_string(value,
389 format_size(item->size));
390 break;
391 case COL_TYPE:
392 g_value_init(value, G_TYPE_STRING);
393 g_value_set_string(value,
394 item->flags & ITEM_FLAG_APPDIR? "App" :
395 S_ISDIR(m) ? "Dir" :
396 S_ISCHR(m) ? "Char" :
397 S_ISBLK(m) ? "Blck" :
398 S_ISLNK(m) ? "Link" :
399 S_ISSOCK(m) ? "Sock" :
400 S_ISFIFO(m) ? "Pipe" :
401 S_ISDOOR(m) ? "Door" :
402 "File");
403 break;
404 case COL_WEIGHT:
405 g_value_init(value, G_TYPE_INT);
406 if (item->flags & ITEM_FLAG_RECENT)
407 g_value_set_int(value, PANGO_WEIGHT_BOLD);
408 else
409 g_value_set_int(value, PANGO_WEIGHT_NORMAL);
410 break;
411 default:
412 g_value_init(value, G_TYPE_STRING);
413 g_value_set_string(value, "Hello");
414 break;
418 static gboolean details_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter)
420 ViewDetails *view_details = (ViewDetails *) tree_model;
421 int i;
423 i = GPOINTER_TO_INT(iter->user_data) + 1;
424 iter->user_data = GINT_TO_POINTER(i);
426 return i < view_details->items->len;
429 static gboolean details_iter_children(GtkTreeModel *tree_model,
430 GtkTreeIter *iter,
431 GtkTreeIter *parent)
433 ViewDetails *view_details = (ViewDetails *) tree_model;
435 /* this is a list, nodes have no children */
436 if (parent)
437 return FALSE;
439 /* but if parent == NULL we return the list itself as children of the
440 * "root"
443 if (view_details->items->len)
445 iter->user_data = GINT_TO_POINTER(0);
446 return TRUE;
448 else
449 return FALSE;
452 static gboolean details_iter_has_child(GtkTreeModel *tree_model,
453 GtkTreeIter *iter)
455 return FALSE;
458 static gint details_iter_n_children(GtkTreeModel *tree_model, GtkTreeIter *iter)
460 ViewDetails *view_details = (ViewDetails *) tree_model;
462 if (iter == NULL)
463 return view_details->items->len;
465 return 0;
468 static gboolean details_iter_nth_child(GtkTreeModel *tree_model,
469 GtkTreeIter *iter,
470 GtkTreeIter *parent,
471 gint n)
473 ViewDetails *view_details = (ViewDetails *) tree_model;
475 if (parent)
476 return FALSE;
478 if (n >= 0 && n < view_details->items->len)
480 iter->user_data = GINT_TO_POINTER(n);
481 return TRUE;
483 else
484 return FALSE;
487 static gboolean details_iter_parent(GtkTreeModel *tree_model,
488 GtkTreeIter *iter,
489 GtkTreeIter *child)
491 return FALSE;
494 /* A ViewDetails is both a GtkTreeView and a GtkTreeModel.
495 * The following functions implement the model interface...
498 static void view_details_tree_model_init(GtkTreeModelIface *iface)
500 iface->get_flags = details_get_flags;
501 iface->get_n_columns = details_get_n_columns;
502 iface->get_column_type = details_get_column_type;
503 iface->get_iter = details_get_iter;
504 iface->get_path = details_get_path;
505 iface->get_value = details_get_value;
506 iface->iter_next = details_iter_next;
507 iface->iter_children = details_iter_children;
508 iface->iter_has_child = details_iter_has_child;
509 iface->iter_n_children = details_iter_n_children;
510 iface->iter_nth_child = details_iter_nth_child;
511 iface->iter_parent = details_iter_parent;
514 static void view_details_sortable_init(GtkTreeSortableIface *iface)
516 iface->get_sort_column_id = details_get_sort_column_id;
517 iface->set_sort_column_id = details_set_sort_column_id;
518 iface->set_sort_func = details_set_sort_func;
519 iface->set_default_sort_func = details_set_default_sort_func;
520 iface->has_default_sort_func = details_has_default_sort_func;
523 static gboolean details_get_sort_column_id(GtkTreeSortable *sortable,
524 gint *sort_column_id,
525 GtkSortType *order)
527 ViewDetails *view_details = (ViewDetails *) sortable;
528 FilerWindow *filer_window = view_details->filer_window;
529 int col;
531 if (!filer_window)
532 return FALSE; /* Not yet initialised */
534 switch (filer_window->sort_type)
536 case SORT_NAME: col = COL_LEAF; break;
537 case SORT_TYPE: col = COL_TYPE; break;
538 case SORT_DATE: col = COL_MTIME; break;
539 case SORT_SIZE: col = COL_SIZE; break;
540 case SORT_OWNER: col = COL_OWNER; break;
541 case SORT_GROUP: col = COL_GROUP; break;
542 default:
543 g_warning("details_get_sort_column_id(): error!");
544 return FALSE;
546 if (sort_column_id)
547 *sort_column_id = col;
548 if (order)
549 *order = filer_window->sort_order;
550 return TRUE;
553 static void details_set_sort_column_id(GtkTreeSortable *sortable,
554 gint sort_column_id,
555 GtkSortType order)
557 ViewDetails *view_details = (ViewDetails *) sortable;
558 FilerWindow *filer_window = view_details->filer_window;
560 if (!filer_window)
561 return; /* Not yet initialised */
563 switch (sort_column_id)
565 case COL_LEAF:
566 display_set_sort_type(filer_window, SORT_NAME, order);
567 break;
568 case COL_SIZE:
569 display_set_sort_type(filer_window, SORT_SIZE, order);
570 break;
571 case COL_MTIME:
572 display_set_sort_type(filer_window, SORT_DATE, order);
573 break;
574 case COL_TYPE:
575 display_set_sort_type(filer_window, SORT_TYPE, order);
576 break;
577 case COL_OWNER:
578 display_set_sort_type(filer_window, SORT_OWNER, order);
579 break;
580 case COL_GROUP:
581 display_set_sort_type(filer_window, SORT_GROUP, order);
582 break;
583 default:
584 g_assert_not_reached();
588 static void details_set_sort_func(GtkTreeSortable *sortable,
589 gint sort_column_id,
590 GtkTreeIterCompareFunc func,
591 gpointer data,
592 GtkDestroyNotify destroy)
594 g_assert_not_reached();
597 static void details_set_default_sort_func(GtkTreeSortable *sortable,
598 GtkTreeIterCompareFunc func,
599 gpointer data,
600 GtkDestroyNotify destroy)
602 g_assert_not_reached();
605 static gboolean details_has_default_sort_func(GtkTreeSortable *sortable)
607 return FALSE;
611 /* End of model implementation */
613 static gboolean is_selected(ViewDetails *view_details, int i)
615 ViewIter iter;
616 iter.i = i;
617 return view_details_get_selected((ViewIface *) view_details, &iter);
620 static gboolean view_details_scroll(GtkWidget *widget, GdkEventScroll *event)
622 GtkTreeView *tree = (GtkTreeView *) widget;
623 GtkTreePath *path = NULL;
625 if (!gtk_tree_view_get_path_at_pos(tree, 0, 1, &path, NULL, NULL, NULL))
626 return TRUE; /* Empty? */
628 if (event->direction == GDK_SCROLL_UP)
629 gtk_tree_path_prev(path);
630 else if (event->direction == GDK_SCROLL_DOWN)
631 gtk_tree_path_next(path);
632 else
633 goto out;
635 gtk_tree_view_scroll_to_cell(tree, path, NULL, TRUE, 0, 0);
636 out:
637 gtk_tree_path_free(path);
638 return TRUE;
641 static gboolean view_details_button_press(GtkWidget *widget,
642 GdkEventButton *bev)
644 FilerWindow *filer_window = ((ViewDetails *) widget)->filer_window;
645 GtkTreeView *tree = (GtkTreeView *) widget;
647 if (bev->window != gtk_tree_view_get_bin_window(tree))
648 return GTK_WIDGET_CLASS(parent_class)->button_press_event(
649 widget, bev);
651 if (dnd_motion_press(widget, bev))
652 filer_perform_action(filer_window, bev);
654 return TRUE;
657 static gboolean view_details_button_release(GtkWidget *widget,
658 GdkEventButton *bev)
660 FilerWindow *filer_window = ((ViewDetails *) widget)->filer_window;
661 GtkTreeView *tree = (GtkTreeView *) widget;
663 if (bev->window != gtk_tree_view_get_bin_window(tree))
664 return GTK_WIDGET_CLASS(parent_class)->button_release_event(
665 widget, bev);
667 if (!dnd_motion_release(bev))
668 filer_perform_action(filer_window, bev);
670 return TRUE;
673 static gint view_details_motion_notify(GtkWidget *widget, GdkEventMotion *event)
675 ViewDetails *view_details = (ViewDetails *) widget;
676 GtkTreeView *tree = (GtkTreeView *) widget;
678 if (event->window != gtk_tree_view_get_bin_window(tree))
679 return GTK_WIDGET_CLASS(parent_class)->motion_notify_event(
680 widget, event);
682 return filer_motion_notify(view_details->filer_window, event);
685 static gboolean view_details_expose(GtkWidget *widget, GdkEventExpose *event)
687 GtkTreeView *tree = (GtkTreeView *) widget;
688 GtkTreePath *path = NULL;
689 GdkRectangle focus_rectangle;
690 ViewDetails *view_details = (ViewDetails *) widget;
691 gboolean had_cursor;
693 had_cursor = (GTK_WIDGET_FLAGS(widget) & GTK_HAS_FOCUS) != 0;
695 if (view_details->filer_window->selection_state == GTK_STATE_SELECTED)
696 GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
697 else
698 GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS);
699 GTK_WIDGET_CLASS(parent_class)->expose_event(widget, event);
700 if (had_cursor)
701 GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
703 if (event->window != gtk_tree_view_get_bin_window(tree))
704 return FALSE; /* Not the main area */
706 gtk_tree_view_get_cursor(tree, &path, NULL);
707 if (!path)
708 return FALSE; /* No cursor */
709 gtk_tree_view_get_background_area(tree, path, NULL, &focus_rectangle);
710 gtk_tree_path_free(path);
712 if (!focus_rectangle.height)
713 return FALSE; /* Off screen */
715 focus_rectangle.width = widget->allocation.width;
717 gtk_paint_focus(widget->style,
718 event->window,
719 GTK_STATE_NORMAL,
720 NULL,
721 widget,
722 "treeview",
723 focus_rectangle.x,
724 focus_rectangle.y,
725 focus_rectangle.width,
726 focus_rectangle.height);
728 return FALSE;
731 static void view_details_size_request(GtkWidget *widget,
732 GtkRequisition *requisition)
734 ViewDetails *view_details = (ViewDetails *) widget;
736 (*GTK_WIDGET_CLASS(parent_class)->size_request)(widget, requisition);
738 view_details->desired_size = *requisition;
740 requisition->height = 50;
741 requisition->width = 50;
744 static void view_details_drag_data_received(GtkWidget *widget,
745 GdkDragContext *drag_context,
746 gint x, gint y, GtkSelectionData *data, guint info, guint time)
748 /* Just here to override annoying default handler */
751 static void view_details_destroy(GtkObject *view_details)
753 VIEW_DETAILS(view_details)->filer_window = NULL;
756 static void view_details_finialize(GObject *object)
758 ViewDetails *view_details = (ViewDetails *) object;
760 g_ptr_array_free(view_details->items, TRUE);
761 view_details->items = NULL;
763 G_OBJECT_CLASS(parent_class)->finalize(object);
766 static void view_details_class_init(gpointer gclass, gpointer data)
768 GObjectClass *object = (GObjectClass *) gclass;
769 GtkWidgetClass *widget = (GtkWidgetClass *) gclass;
771 parent_class = g_type_class_peek_parent(gclass);
773 object->finalize = view_details_finialize;
774 GTK_OBJECT_CLASS(object)->destroy = view_details_destroy;
776 widget->scroll_event = view_details_scroll;
777 widget->button_press_event = view_details_button_press;
778 widget->button_release_event = view_details_button_release;
779 widget->motion_notify_event = view_details_motion_notify;
780 widget->expose_event = view_details_expose;
781 widget->size_request = view_details_size_request;
782 widget->drag_data_received = view_details_drag_data_received;
785 static gboolean block_focus(GtkWidget *button, GtkDirectionType dir,
786 ViewDetails *view_details)
788 GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
789 return FALSE;
792 static gboolean test_can_change_selection(GtkTreeSelection *sel,
793 GtkTreeModel *model,
794 GtkTreePath *path,
795 gboolean path_currently_selected,
796 gpointer data)
798 ViewDetails *view_details;
800 view_details = VIEW_DETAILS(gtk_tree_selection_get_tree_view(sel));
802 return view_details->can_change_selection != 0;
805 #define ADD_TEXT_COLUMN(name, model_column) \
806 cell = gtk_cell_renderer_text_new(); \
807 column = gtk_tree_view_column_new_with_attributes(name, cell, \
808 "text", model_column, \
809 "foreground-gdk", COL_COLOUR, \
810 "background-gdk", COL_BG_COLOUR, \
811 "weight", COL_WEIGHT, \
812 NULL); \
813 gtk_tree_view_append_column(treeview, column); \
814 g_signal_connect(column->button, "grab-focus", \
815 G_CALLBACK(block_focus), view_details);
817 static void view_details_init(GTypeInstance *object, gpointer gclass)
819 GtkTreeView *treeview = (GtkTreeView *) object;
820 GtkTreeViewColumn *column;
821 GtkCellRenderer *cell;
822 GtkTreeSortable *sortable_list;
823 ViewDetails *view_details = (ViewDetails *) object;
825 view_details->items = g_ptr_array_new();
826 view_details->cursor_base = -1;
827 view_details->desired_size.width = -1;
828 view_details->desired_size.height = -1;
829 view_details->can_change_selection = 0;
831 view_details->selection = gtk_tree_view_get_selection(treeview);
832 gtk_tree_selection_set_mode(view_details->selection,
833 GTK_SELECTION_MULTIPLE);
834 gtk_tree_selection_set_select_function(view_details->selection,
835 test_can_change_selection, view_details, NULL);
837 /* Sorting */
838 view_details->sort_fn = NULL;
839 sortable_list = GTK_TREE_SORTABLE(object);
841 gtk_tree_view_set_model(treeview, GTK_TREE_MODEL(view_details));
843 /* Icon */
844 cell = cell_icon_new(view_details);
845 column = gtk_tree_view_column_new_with_attributes(NULL, cell,
846 "item", COL_VIEW_ITEM,
847 "background-gdk", COL_BG_COLOUR,
848 NULL);
849 gtk_tree_view_append_column(treeview, column);
851 ADD_TEXT_COLUMN(_("_Name"), COL_LEAF);
852 gtk_tree_view_column_set_sort_column_id(column, COL_LEAF);
853 gtk_tree_view_column_set_resizable(column, TRUE);
854 ADD_TEXT_COLUMN(_("_Type"), COL_TYPE);
855 gtk_tree_view_column_set_sort_column_id(column, COL_TYPE);
856 ADD_TEXT_COLUMN(_("_Permissions"), COL_PERM);
857 ADD_TEXT_COLUMN(_("_Owner"), COL_OWNER);
858 gtk_tree_view_column_set_sort_column_id(column, COL_OWNER);
859 ADD_TEXT_COLUMN(_("_Group"), COL_GROUP);
860 gtk_tree_view_column_set_sort_column_id(column, COL_GROUP);
861 ADD_TEXT_COLUMN(_("_Size"), COL_SIZE);
862 gtk_tree_view_column_set_sort_column_id(column, COL_SIZE);
863 ADD_TEXT_COLUMN(_("Last _Modified"), COL_MTIME);
864 gtk_tree_view_column_set_sort_column_id(column, COL_MTIME);
867 /* Create the handers for the View interface */
868 static void view_details_iface_init(gpointer giface, gpointer iface_data)
870 ViewIfaceClass *iface = giface;
872 g_assert(G_TYPE_FROM_INTERFACE(iface) == VIEW_TYPE_IFACE);
874 /* override stuff */
875 iface->sort = view_details_sort;
876 iface->style_changed = view_details_style_changed;
877 iface->autoselect = view_details_autoselect;
878 iface->add_items = view_details_add_items;
879 iface->update_items = view_details_update_items;
880 iface->delete_if = view_details_delete_if;
881 iface->clear = view_details_clear;
882 iface->select_all = view_details_select_all;
883 iface->clear_selection = view_details_clear_selection;
884 iface->count_items = view_details_count_items;
885 iface->count_selected = view_details_count_selected;
886 iface->show_cursor = view_details_show_cursor;
887 iface->get_iter = view_details_get_iter;
888 iface->get_iter_at_point = view_details_get_iter_at_point;
889 iface->cursor_to_iter = view_details_cursor_to_iter;
890 iface->set_selected = view_details_set_selected;
891 iface->get_selected = view_details_get_selected;
892 iface->set_frozen = view_details_set_frozen;
893 iface->select_only = view_details_select_only;
894 iface->wink_item = view_details_wink_item;
895 iface->autosize = view_details_autosize;
896 iface->cursor_visible = view_details_cursor_visible;
897 iface->set_base = view_details_set_base;
898 iface->start_lasso_box = view_details_start_lasso_box;
899 iface->extend_tip = view_details_extend_tip;
900 iface->auto_scroll_callback = view_details_auto_scroll_callback;
903 /* Implementations of the View interface. See view_iface.c for comments. */
905 static void view_details_style_changed(ViewIface *view, int flags)
907 ViewDetails *view_details = (ViewDetails *) view;
908 GtkTreeModel *model = (GtkTreeModel *) view;
909 GtkTreePath *path;
910 ViewItem **items = (ViewItem **) view_details->items->pdata;
911 int i;
912 int n = view_details->items->len;
914 path = gtk_tree_path_new();
915 gtk_tree_path_append_index(path, 0);
917 for (i = 0; i < n; i++)
919 GtkTreeIter iter;
920 ViewItem *item = items[i];
922 iter.user_data = GINT_TO_POINTER(i);
923 if (item->image)
925 g_object_unref(G_OBJECT(item->image));
926 item->image = NULL;
928 gtk_tree_model_row_changed(model, path, &iter);
929 gtk_tree_path_next(path);
932 gtk_tree_path_free(path);
934 gtk_tree_view_columns_autosize((GtkTreeView *) view);
936 if (flags & VIEW_UPDATE_HEADERS)
937 details_update_header_visibility(view_details);
940 static gint wrap_sort(gconstpointer a, gconstpointer b,
941 ViewDetails *view_details)
943 ViewItem *ia = *(ViewItem **) a;
944 ViewItem *ib = *(ViewItem **) b;
946 if (view_details->filer_window->sort_order == GTK_SORT_ASCENDING)
947 return view_details->sort_fn(ia->item, ib->item);
948 else
949 return -view_details->sort_fn(ia->item, ib->item);
952 static void resort(ViewDetails *view_details)
954 ViewItem **items = (ViewItem **) view_details->items->pdata;
955 gint i, len = view_details->items->len;
956 guint *new_order;
957 GtkTreePath *path;
959 if (!len)
960 return;
962 for (i = len - 1; i >= 0; i--)
963 items[i]->old_pos = i;
965 switch (view_details->filer_window->sort_type)
967 case SORT_NAME: view_details->sort_fn = sort_by_name; break;
968 case SORT_TYPE: view_details->sort_fn = sort_by_type; break;
969 case SORT_DATE: view_details->sort_fn = sort_by_date; break;
970 case SORT_SIZE: view_details->sort_fn = sort_by_size; break;
971 case SORT_OWNER: view_details->sort_fn = sort_by_owner; break;
972 case SORT_GROUP: view_details->sort_fn = sort_by_group; break;
973 default:
974 g_assert_not_reached();
977 g_ptr_array_sort_with_data(view_details->items,
978 (GCompareDataFunc) wrap_sort,
979 view_details);
981 new_order = g_new(guint, len);
982 for (i = len - 1; i >= 0; i--)
983 new_order[i] = items[i]->old_pos;
985 path = gtk_tree_path_new();
986 gtk_tree_model_rows_reordered((GtkTreeModel *) view_details,
987 path, NULL, new_order);
988 gtk_tree_path_free(path);
989 g_free(new_order);
992 static void view_details_sort(ViewIface *view)
994 resort((ViewDetails *) view);
995 gtk_tree_sortable_sort_column_changed((GtkTreeSortable *) view);
998 static gboolean view_details_autoselect(ViewIface *view, const gchar *leaf)
1000 return FALSE;
1003 static void view_details_add_items(ViewIface *view, GPtrArray *new_items)
1005 ViewDetails *view_details = (ViewDetails *) view;
1006 FilerWindow *filer_window = view_details->filer_window;
1007 gboolean show_hidden = filer_window->show_hidden;
1008 GPtrArray *items = view_details->items;
1009 GtkTreeIter iter;
1010 int i;
1011 GtkTreePath *path;
1012 GtkTreeModel *model = (GtkTreeModel *) view;
1014 iter.user_data = GINT_TO_POINTER(items->len);
1015 path = details_get_path(model, &iter);
1017 for (i = 0; i < new_items->len; i++)
1019 DirItem *item = (DirItem *) new_items->pdata[i];
1020 char *leafname = item->leafname;
1021 ViewItem *vitem;
1023 if (leafname[0] == '.')
1025 if (!show_hidden)
1026 continue;
1028 if (leafname[1] == '\0')
1029 continue; /* Never show '.' */
1031 if (leafname[1] == '.' &&
1032 leafname[2] == '\0')
1033 continue; /* Never show '..' */
1036 vitem = g_new(ViewItem, 1);
1037 vitem->item = item;
1038 vitem->image = NULL;
1039 if (!g_utf8_validate(leafname, -1, NULL))
1040 vitem->utf8_name = to_utf8(leafname);
1041 else
1042 vitem->utf8_name = NULL;
1044 g_ptr_array_add(items, vitem);
1046 iter.user_data = GINT_TO_POINTER(items->len - 1);
1047 gtk_tree_model_row_inserted(model, path, &iter);
1048 gtk_tree_path_next(path);
1051 gtk_tree_path_free(path);
1053 resort(view_details);
1056 /* Find an item in the sorted array.
1057 * Returns the item number, or -1 if not found.
1059 static int details_find_item(ViewDetails *view_details, DirItem *item)
1061 ViewItem **items, tmp, *tmpp;
1062 int lower, upper;
1064 g_return_val_if_fail(view_details != NULL, -1);
1065 g_return_val_if_fail(item != NULL, -1);
1067 tmp.item = item;
1068 tmpp = &tmp;
1070 items = (ViewItem **) view_details->items->pdata;
1072 /* If item is here, then: lower <= i < upper */
1073 lower = 0;
1074 upper = view_details->items->len;
1076 while (lower < upper)
1078 int i, cmp;
1080 i = (lower + upper) >> 1;
1082 cmp = wrap_sort(&items[i], &tmpp, view_details);
1083 if (cmp == 0)
1084 return i;
1086 if (cmp > 0)
1087 upper = i;
1088 else
1089 lower = i + 1;
1092 return -1;
1095 static void view_details_update_items(ViewIface *view, GPtrArray *items)
1097 ViewDetails *view_details = (ViewDetails *) view;
1098 FilerWindow *filer_window = view_details->filer_window;
1099 int i;
1100 GtkTreeModel *model = (GtkTreeModel *) view_details;
1102 g_return_if_fail(items->len > 0);
1104 /* The item data has already been modified, so this gives the
1105 * final sort order...
1107 resort(view_details);
1109 for (i = 0; i < items->len; i++)
1111 DirItem *item = (DirItem *) items->pdata[i];
1112 const gchar *leafname = item->leafname;
1113 int j;
1115 if (leafname[0] == '.' && filer_window->show_hidden == FALSE)
1116 continue;
1118 j = details_find_item(view_details, item);
1120 if (j < 0)
1121 g_warning("Failed to find '%s'\n", leafname);
1122 else
1124 GtkTreePath *path;
1125 GtkTreeIter iter;
1126 ViewItem *view_item = view_details->items->pdata[j];
1127 if (view_item->image)
1129 g_object_unref(G_OBJECT(view_item->image));
1130 view_item->image = NULL;
1132 path = gtk_tree_path_new();
1133 gtk_tree_path_append_index(path, j);
1134 iter.user_data = GINT_TO_POINTER(j);
1135 gtk_tree_model_row_changed(model, path, &iter);
1140 static void view_details_delete_if(ViewIface *view,
1141 gboolean (*test)(gpointer item, gpointer data),
1142 gpointer data)
1144 GtkTreePath *path;
1145 ViewDetails *view_details = (ViewDetails *) view;
1146 int i = 0;
1147 GPtrArray *items = view_details->items;
1148 GtkTreeModel *model = (GtkTreeModel *) view;
1150 path = gtk_tree_path_new();
1152 gtk_tree_path_append_index(path, i);
1154 while (i < items->len)
1156 ViewItem *item = items->pdata[i];
1158 if (test(item->item, data))
1160 free_view_item(items->pdata[i]);
1161 g_ptr_array_remove_index(items, i);
1162 gtk_tree_model_row_deleted(model, path);
1164 else
1166 i++;
1167 gtk_tree_path_next(path);
1171 gtk_tree_path_free(path);
1174 static void view_details_clear(ViewIface *view)
1176 GtkTreePath *path;
1177 GPtrArray *items = ((ViewDetails *) view)->items;
1178 GtkTreeModel *model = (GtkTreeModel *) view;
1180 path = gtk_tree_path_new();
1181 gtk_tree_path_append_index(path, items->len);
1183 while (gtk_tree_path_prev(path))
1184 gtk_tree_model_row_deleted(model, path);
1186 g_ptr_array_set_size(items, 0);
1187 gtk_tree_path_free(path);
1190 static void view_details_select_all(ViewIface *view)
1192 ViewDetails *view_details = (ViewDetails *) view;
1194 view_details->can_change_selection++;
1195 gtk_tree_selection_select_all(view_details->selection);
1196 view_details->can_change_selection--;
1199 static void view_details_clear_selection(ViewIface *view)
1201 ViewDetails *view_details = (ViewDetails *) view;
1203 view_details->can_change_selection++;
1204 gtk_tree_selection_unselect_all(view_details->selection);
1205 view_details->can_change_selection--;
1208 static int view_details_count_items(ViewIface *view)
1210 ViewDetails *view_details = (ViewDetails *) view;
1212 return view_details->items->len;
1215 #if GTK_MINOR_VERSION < 2
1216 static void view_details_count_inc(GtkTreeModel *model, GtkTreePath *path,
1217 GtkTreeIter *iter, gpointer data)
1219 int *count = (int *) data;
1220 (*count) += 1;
1222 #endif
1224 static int view_details_count_selected(ViewIface *view)
1226 ViewDetails *view_details = (ViewDetails *) view;
1228 #if GTK_MINOR_VERSION >= 2
1229 return gtk_tree_selection_count_selected_rows(view_details->selection);
1230 #else
1231 int count = 0;
1233 gtk_tree_selection_selected_foreach(view_details->selection,
1234 view_details_count_inc, &count);
1235 return count;
1236 #endif
1239 static void view_details_show_cursor(ViewIface *view)
1243 static void view_details_get_iter(ViewIface *view,
1244 ViewIter *iter, IterFlags flags)
1246 make_iter((ViewDetails *) view, iter, flags);
1249 static void view_details_get_iter_at_point(ViewIface *view, ViewIter *iter,
1250 GdkWindow *src, int x, int y)
1252 ViewDetails *view_details = (ViewDetails *) view;
1253 GtkTreeModel *model;
1254 GtkTreeView *tree = (GtkTreeView *) view;
1255 GtkTreePath *path = NULL;
1256 int i = -1;
1258 model = gtk_tree_view_get_model(tree);
1260 if (gtk_tree_view_get_path_at_pos(tree, x, y, &path, NULL, NULL, NULL))
1262 g_return_if_fail(path != NULL);
1264 i = gtk_tree_path_get_indices(path)[0];
1265 gtk_tree_path_free(path);
1268 make_item_iter(view_details, iter, i);
1271 static void view_details_cursor_to_iter(ViewIface *view, ViewIter *iter)
1273 GtkTreePath *path;
1274 ViewDetails *view_details = (ViewDetails *) view;
1276 path = gtk_tree_path_new();
1278 if (iter)
1279 gtk_tree_path_append_index(path, iter->i);
1280 else
1282 /* Using depth zero or index -1 gives an error, but this
1283 * is OK!
1285 gtk_tree_path_append_index(path, view_details->items->len);
1288 gtk_tree_view_set_cursor((GtkTreeView *) view, path, NULL, FALSE);
1289 gtk_tree_path_free(path);
1292 static void set_selected(ViewDetails *view_details, int i, gboolean selected)
1294 GtkTreeIter iter;
1296 iter.user_data = GINT_TO_POINTER(i);
1297 view_details->can_change_selection++;
1298 if (selected)
1299 gtk_tree_selection_select_iter(view_details->selection, &iter);
1300 else
1301 gtk_tree_selection_unselect_iter(view_details->selection,
1302 &iter);
1303 view_details->can_change_selection--;
1306 static void view_details_set_selected(ViewIface *view,
1307 ViewIter *iter,
1308 gboolean selected)
1310 set_selected((ViewDetails *) view, iter->i, selected);
1313 static gboolean get_selected(ViewDetails *view_details, int i)
1315 GtkTreeIter iter;
1317 iter.user_data = GINT_TO_POINTER(i);
1319 return gtk_tree_selection_iter_is_selected(view_details->selection,
1320 &iter);
1323 static gboolean view_details_get_selected(ViewIface *view, ViewIter *iter)
1325 return get_selected((ViewDetails *) view, iter->i);
1328 static void view_details_select_only(ViewIface *view, ViewIter *iter)
1330 ViewDetails *view_details = (ViewDetails *) view;
1331 GtkTreePath *path;
1333 path = gtk_tree_path_new();
1334 gtk_tree_path_append_index(path, iter->i);
1335 view_details->can_change_selection++;
1336 gtk_tree_selection_select_range(view_details->selection, path, path);
1337 view_details->can_change_selection--;
1338 gtk_tree_path_free(path);
1341 static void view_details_set_frozen(ViewIface *view, gboolean frozen)
1345 static void view_details_wink_item(ViewIface *view, ViewIter *iter)
1347 /* TODO */
1350 static void view_details_autosize(ViewIface *view)
1352 ViewDetails *view_details = (ViewDetails *) view;
1353 FilerWindow *filer_window = view_details->filer_window;
1354 int max_width = (o_filer_size_limit.int_value * screen_width) / 100;
1355 int max_height = (o_filer_size_limit.int_value * screen_height) / 100;
1356 int h;
1357 GtkRequisition req;
1359 gtk_widget_queue_resize(GTK_WIDGET(view));
1360 gtk_widget_size_request(GTK_WIDGET(view), &req);
1362 h = MAX(view_details->desired_size.height, SMALL_HEIGHT);
1364 filer_window_set_size(filer_window,
1365 MIN(view_details->desired_size.width, max_width),
1366 MIN(h, max_height));
1369 static gboolean view_details_cursor_visible(ViewIface *view)
1371 return FALSE;
1374 static void view_details_set_base(ViewIface *view, ViewIter *iter)
1376 ViewDetails *view_details = (ViewDetails *) view;
1378 view_details->cursor_base = iter->i;
1381 static void view_details_start_lasso_box(ViewIface *view, GdkEventButton *event)
1385 static void view_details_extend_tip(ViewIface *view,
1386 ViewIter *iter, GString *tip)
1390 static DirItem *iter_init(ViewIter *iter)
1392 ViewDetails *view_details = (ViewDetails *) iter->view;
1393 int i = -1;
1394 int n = view_details->items->len;
1395 int flags = iter->flags;
1397 iter->peek = iter_peek;
1399 if (iter->n_remaining == 0)
1400 return NULL;
1402 if (flags & VIEW_ITER_FROM_CURSOR)
1404 GtkTreePath *path;
1405 gtk_tree_view_get_cursor((GtkTreeView *) view_details,
1406 &path, NULL);
1407 if (!path)
1408 return NULL; /* No cursor */
1409 i = gtk_tree_path_get_indices(path)[0];
1410 gtk_tree_path_free(path);
1412 else if (flags & VIEW_ITER_FROM_BASE)
1413 i = view_details->cursor_base;
1415 if (i < 0 || i >= n)
1417 /* Either a normal iteration, or an iteration from an
1418 * invalid starting point.
1420 if (flags & VIEW_ITER_BACKWARDS)
1421 i = n - 1;
1422 else
1423 i = 0;
1426 if (i < 0 || i >= n)
1427 return NULL; /* No items at all! */
1429 iter->next = flags & VIEW_ITER_BACKWARDS ? iter_prev : iter_next;
1430 iter->n_remaining--;
1431 iter->i = i;
1433 if (flags & VIEW_ITER_SELECTED && !is_selected(view_details, i))
1434 return iter->next(iter);
1435 return iter->peek(iter);
1438 static DirItem *iter_prev(ViewIter *iter)
1440 ViewDetails *view_details = (ViewDetails *) iter->view;
1441 int n = view_details->items->len;
1442 int i = iter->i;
1444 g_return_val_if_fail(iter->n_remaining >= 0, NULL);
1446 /* i is the last item returned (always valid) */
1448 g_return_val_if_fail(i >= 0 && i < n, NULL);
1450 while (iter->n_remaining)
1452 i--;
1453 iter->n_remaining--;
1455 if (i == -1)
1456 i = n - 1;
1458 g_return_val_if_fail(i >= 0 && i < n, NULL);
1460 if (iter->flags & VIEW_ITER_SELECTED &&
1461 !is_selected(view_details, i))
1462 continue;
1464 iter->i = i;
1465 return ((ViewItem *) view_details->items->pdata[i])->item;
1468 iter->i = -1;
1469 return NULL;
1472 static DirItem *iter_next(ViewIter *iter)
1474 ViewDetails *view_details = (ViewDetails *) iter->view;
1475 int n = view_details->items->len;
1476 int i = iter->i;
1478 g_return_val_if_fail(iter->n_remaining >= 0, NULL);
1480 /* i is the last item returned (always valid) */
1482 g_return_val_if_fail(i >= 0 && i < n, NULL);
1484 while (iter->n_remaining)
1486 i++;
1487 iter->n_remaining--;
1489 if (i == n)
1490 i = 0;
1492 g_return_val_if_fail(i >= 0 && i < n, NULL);
1494 if (iter->flags & VIEW_ITER_SELECTED &&
1495 !is_selected(view_details, i))
1496 continue;
1498 iter->i = i;
1499 return ((ViewItem *) view_details->items->pdata[i])->item;
1502 iter->i = -1;
1503 return NULL;
1506 static DirItem *iter_peek(ViewIter *iter)
1508 ViewDetails *view_details = (ViewDetails *) iter->view;
1509 int n = view_details->items->len;
1510 int i = iter->i;
1512 if (i == -1)
1513 return NULL;
1515 g_return_val_if_fail(i >= 0 && i < n, NULL);
1517 return ((ViewItem *) view_details->items->pdata[i])->item;
1520 /* Set the iterator to return 'i' on the next peek().
1521 * If i is -1, returns NULL on next peek().
1523 static void make_item_iter(ViewDetails *view_details, ViewIter *iter, int i)
1525 make_iter(view_details, iter, 0);
1527 g_return_if_fail(i >= -1 && i < (int) view_details->items->len);
1529 iter->i = i;
1530 iter->next = iter_next;
1531 iter->peek = iter_peek;
1532 iter->n_remaining = 0;
1535 static void make_iter(ViewDetails *view_details, ViewIter *iter,
1536 IterFlags flags)
1538 iter->view = (ViewIface *) view_details;
1539 iter->next = iter_init;
1540 iter->peek = NULL;
1541 iter->i = -1;
1543 iter->flags = flags;
1545 if (flags & VIEW_ITER_ONE_ONLY)
1547 iter->n_remaining = 1;
1548 iter->next(iter);
1550 else
1551 iter->n_remaining = view_details->items->len;
1554 static void free_view_item(ViewItem *view_item)
1556 if (view_item->image)
1557 g_object_unref(G_OBJECT(view_item->image));
1558 g_free(view_item->utf8_name);
1559 g_free(view_item);
1562 static gboolean view_details_auto_scroll_callback(ViewIface *view)
1564 GtkTreeView *tree = (GtkTreeView *) view;
1565 ViewDetails *view_details = (ViewDetails *) view;
1566 FilerWindow *filer_window = view_details->filer_window;
1567 GtkRange *scrollbar = (GtkRange *) filer_window->scrollbar;
1568 GtkAdjustment *adj;
1569 GdkWindow *window;
1570 gint x, y, w, h;
1571 GdkModifierType mask;
1572 int diff = 0;
1574 window = gtk_tree_view_get_bin_window(tree);
1576 gdk_window_get_pointer(window, &x, &y, &mask);
1577 gdk_drawable_get_size(window, &w, NULL);
1579 adj = gtk_range_get_adjustment(scrollbar);
1580 h = adj->page_size;
1582 if ((x < 0 || x > w || y < 0 || y > h)) /* && !view->lasso_box) */
1583 return FALSE; /* Out of window - stop */
1585 if (y < AUTOSCROLL_STEP)
1586 diff = y - AUTOSCROLL_STEP;
1587 else if (y > h - AUTOSCROLL_STEP)
1588 diff = AUTOSCROLL_STEP + y - h;
1590 if (diff)
1592 int old = adj->value;
1593 int value = old + diff;
1595 value = CLAMP(value, 0, adj->upper - adj->page_size);
1596 gtk_adjustment_set_value(adj, value);
1598 if (adj->value != old)
1599 dnd_spring_abort();
1602 return TRUE;