r2228: Made 'Automatic' an icon size, rather than a separate option.
[rox-filer.git] / ROX-Filer / src / view_details.c
blob9cb4c5b38665140dfc3b8882bdd7f9bf51add0c1
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2002, 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_ICON 9
58 #define COL_BG_COLOUR 10
59 #define N_COLUMNS 11
61 static gpointer parent_class = NULL;
63 struct _ViewDetailsClass {
64 GtkTreeViewClass parent;
67 typedef struct _ViewItem ViewItem;
69 struct _ViewItem {
70 DirItem *item;
71 GdkPixbuf *image;
72 int old_pos; /* Used while sorting */
73 gboolean selected;
76 typedef struct _ViewDetails ViewDetails;
78 struct _ViewDetails {
79 GtkTreeView treeview;
81 FilerWindow *filer_window; /* Used for styles, etc */
83 GPtrArray *items; /* ViewItem */
85 gint sort_column_id;
86 GtkSortType order;
87 int (*sort_fn)(const void *, const void *);
89 int cursor_base; /* Cursor when minibuffer opened */
92 /* Static prototypes */
93 static void view_details_finialize(GObject *object);
94 static void view_details_class_init(gpointer gclass, gpointer data);
95 static void view_details_init(GTypeInstance *object, gpointer gclass);
97 static void view_details_iface_init(gpointer giface, gpointer iface_data);
99 static void view_details_sort(ViewIface *view);
100 static void view_details_style_changed(ViewIface *view, int flags);
101 static gboolean view_details_autoselect(ViewIface *view, const gchar *leaf);
102 static void view_details_add_items(ViewIface *view, GPtrArray *items);
103 static void view_details_update_items(ViewIface *view, GPtrArray *items);
104 static void view_details_delete_if(ViewIface *view,
105 gboolean (*test)(gpointer item, gpointer data),
106 gpointer data);
107 static void view_details_clear(ViewIface *view);
108 static void view_details_select_all(ViewIface *view);
109 static void view_details_clear_selection(ViewIface *view);
110 static int view_details_count_items(ViewIface *view);
111 static int view_details_count_selected(ViewIface *view);
112 static void view_details_show_cursor(ViewIface *view);
113 static void view_details_get_iter(ViewIface *view,
114 ViewIter *iter, IterFlags flags);
115 static void view_details_get_iter_at_point(ViewIface *view, ViewIter *iter,
116 int x, int y);
117 static void view_details_cursor_to_iter(ViewIface *view, ViewIter *iter);
118 static void view_details_set_selected(ViewIface *view,
119 ViewIter *iter,
120 gboolean selected);
121 static gboolean view_details_get_selected(ViewIface *view, ViewIter *iter);
122 static void view_details_select_only(ViewIface *view, ViewIter *iter);
123 static void view_details_set_frozen(ViewIface *view, gboolean frozen);
124 static void view_details_wink_item(ViewIface *view, ViewIter *iter);
125 static void view_details_autosize(ViewIface *view);
126 static gboolean view_details_cursor_visible(ViewIface *view);
127 static void view_details_set_base(ViewIface *view, ViewIter *iter);
128 static void view_details_start_lasso_box(ViewIface *view,
129 GdkEventButton *event);
130 static void view_details_extend_tip(ViewIface *view,
131 ViewIter *iter, GString *tip);
133 static DirItem *iter_peek(ViewIter *iter);
134 static DirItem *iter_prev(ViewIter *iter);
135 static DirItem *iter_next(ViewIter *iter);
136 static void make_iter(ViewDetails *view_details, ViewIter *iter,
137 IterFlags flags);
138 static void make_item_iter(ViewDetails *view_details, ViewIter *iter, int i);
139 static void view_details_tree_model_init(GtkTreeModelIface *iface);
140 static gboolean details_get_sort_column_id(GtkTreeSortable *sortable,
141 gint *sort_column_id,
142 GtkSortType *order);
143 static void details_set_sort_column_id(GtkTreeSortable *sortable,
144 gint sort_column_id,
145 GtkSortType order);
146 static void details_set_sort_func(GtkTreeSortable *sortable,
147 gint sort_column_id,
148 GtkTreeIterCompareFunc func,
149 gpointer data,
150 GtkDestroyNotify destroy);
151 static void details_set_default_sort_func(GtkTreeSortable *sortable,
152 GtkTreeIterCompareFunc func,
153 gpointer data,
154 GtkDestroyNotify destroy);
155 static gboolean details_has_default_sort_func(GtkTreeSortable *sortable);
156 static void view_details_sortable_init(GtkTreeSortableIface *iface);
157 static void set_selected(ViewDetails *view_details, int i, gboolean selected);
158 static gboolean get_selected(ViewDetails *view_details, int i);
161 /****************************************************************
162 * EXTERNAL INTERFACE *
163 ****************************************************************/
165 GtkWidget *view_details_new(FilerWindow *filer_window)
167 ViewDetails *view_details;
169 view_details = g_object_new(view_details_get_type(), NULL);
170 view_details->filer_window = filer_window;
172 gtk_range_set_adjustment(GTK_RANGE(filer_window->scrollbar),
173 gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(view_details)));
175 return GTK_WIDGET(view_details);
178 GType view_details_get_type(void)
180 static GType type = 0;
182 if (!type)
184 static const GTypeInfo info =
186 sizeof (ViewDetailsClass),
187 NULL, /* base_init */
188 NULL, /* base_finalise */
189 view_details_class_init,
190 NULL, /* class_finalise */
191 NULL, /* class_data */
192 sizeof(ViewDetails),
193 0, /* n_preallocs */
194 view_details_init
196 static const GInterfaceInfo view_iface_info = {
197 view_details_iface_init,
198 NULL, NULL
200 static const GInterfaceInfo tree_model_info = {
201 (GInterfaceInitFunc) view_details_tree_model_init,
202 NULL, NULL
204 static const GInterfaceInfo sortable_info = {
205 (GInterfaceInitFunc) view_details_sortable_init,
206 NULL, NULL
210 type = g_type_register_static(gtk_tree_view_get_type(),
211 "ViewDetails", &info, 0);
213 g_type_add_interface_static(type, VIEW_TYPE_IFACE,
214 &view_iface_info);
215 g_type_add_interface_static(type, GTK_TYPE_TREE_MODEL,
216 &tree_model_info);
217 g_type_add_interface_static(type, GTK_TYPE_TREE_SORTABLE,
218 &sortable_info);
221 return type;
224 /****************************************************************
225 * INTERNAL FUNCTIONS *
226 ****************************************************************/
228 /* Fulfill the GtkTreeModel requirements */
229 static guint details_get_flags(GtkTreeModel *tree_model)
231 return GTK_TREE_MODEL_LIST_ONLY;
234 static gint details_get_n_columns(GtkTreeModel *tree_model)
236 return N_COLUMNS;
239 static GType details_get_column_type(GtkTreeModel *tree_model, gint index)
241 g_return_val_if_fail(index < N_COLUMNS && index >= 0, G_TYPE_INVALID);
243 if (index == COL_COLOUR || index == COL_BG_COLOUR)
244 return GDK_TYPE_COLOR;
245 else if (index == COL_ITEM)
246 return G_TYPE_POINTER;
247 else if (index == COL_ICON)
248 return GDK_TYPE_PIXBUF;
249 return G_TYPE_STRING;
252 static gboolean details_get_iter(GtkTreeModel *tree_model,
253 GtkTreeIter *iter,
254 GtkTreePath *path)
256 ViewDetails *view_details = (ViewDetails *) tree_model;
257 gint i;
259 g_return_val_if_fail(gtk_tree_path_get_depth (path) > 0, FALSE);
261 i = gtk_tree_path_get_indices(path)[0];
263 if (i >= view_details->items->len)
264 return FALSE;
266 iter->user_data = GINT_TO_POINTER(i);
268 return TRUE;
271 static GtkTreePath *details_get_path(GtkTreeModel *tree_model,
272 GtkTreeIter *iter)
274 GtkTreePath *retval;
276 retval = gtk_tree_path_new();
277 gtk_tree_path_append_index(retval, GPOINTER_TO_INT(iter->user_data));
279 return retval;
282 static void details_get_value(GtkTreeModel *tree_model,
283 GtkTreeIter *iter,
284 gint column,
285 GValue *value)
287 ViewDetails *view_details = (ViewDetails *) tree_model;
288 GtkStyle *style = ((GtkWidget *) tree_model)->style;
289 gint i;
290 GPtrArray *items = view_details->items;
291 ViewItem *view_item;
292 DirItem *item;
293 mode_t m;
295 g_return_if_fail(column >= 0 && column < N_COLUMNS);
297 i = GPOINTER_TO_INT(iter->user_data);
298 g_return_if_fail(i >= 0 && i < items->len);
299 view_item = (ViewItem *) items->pdata[i];
300 item = view_item->item;
302 if (column == COL_LEAF)
304 g_value_init(value, G_TYPE_STRING);
305 g_value_set_string(value, item->leafname);
306 return;
308 else if (column == COL_ITEM)
310 g_value_init(value, G_TYPE_POINTER);
311 g_value_set_pointer(value, item);
312 return;
315 if (item->base_type == TYPE_UNKNOWN)
317 GType type;
318 type = details_get_column_type(tree_model, column);
319 g_value_init(value, type);
320 if (type == G_TYPE_STRING)
321 g_value_set_string(value, "");
322 else if (type == GDK_TYPE_COLOR)
323 g_value_set_boxed(value, NULL);
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_ICON:
338 g_value_init(value, GDK_TYPE_PIXBUF);
339 if (!item->image->sm_pixbuf)
340 pixmap_make_small(item->image);
341 g_value_set_object(value, item->image->sm_pixbuf);
342 break;
343 case COL_COLOUR:
344 g_value_init(value, GDK_TYPE_COLOR);
345 g_value_set_boxed(value, type_get_colour(item, NULL));
346 break;
347 case COL_BG_COLOUR:
348 g_value_init(value, GDK_TYPE_COLOR);
349 g_value_set_boxed(value, view_item->selected
350 ? &style->base[GTK_STATE_SELECTED]
351 : NULL);
352 break;
353 case COL_OWNER:
354 g_value_init(value, G_TYPE_STRING);
355 g_value_set_string(value, user_name(item->uid));
356 break;
357 case COL_GROUP:
358 g_value_init(value, G_TYPE_STRING);
359 g_value_set_string(value, group_name(item->gid));
360 break;
361 case COL_MTIME:
363 gchar *time;
364 time = pretty_time(&item->mtime);
365 g_value_init(value, G_TYPE_STRING);
366 g_value_set_string(value, time);
367 g_free(time);
368 break;
370 case COL_PERM:
371 g_value_init(value, G_TYPE_STRING);
372 g_value_set_string(value, pretty_permissions(m));
373 break;
374 case COL_SIZE:
375 g_value_init(value, G_TYPE_STRING);
376 if (item->base_type != TYPE_DIRECTORY)
377 g_value_set_string(value,
378 format_size(item->size));
379 break;
380 case COL_TYPE:
381 g_value_init(value, G_TYPE_STRING);
382 g_value_set_string(value,
383 item->flags & ITEM_FLAG_APPDIR? "App" :
384 S_ISDIR(m) ? "Dir" :
385 S_ISCHR(m) ? "Char" :
386 S_ISBLK(m) ? "Blck" :
387 S_ISLNK(m) ? "Link" :
388 S_ISSOCK(m) ? "Sock" :
389 S_ISFIFO(m) ? "Pipe" :
390 S_ISDOOR(m) ? "Door" :
391 "File");
392 break;
393 default:
394 g_value_init(value, G_TYPE_STRING);
395 g_value_set_string(value, "Hello");
396 break;
400 static gboolean details_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter)
402 ViewDetails *view_details = (ViewDetails *) tree_model;
403 int i;
405 i = GPOINTER_TO_INT(iter->user_data) + 1;
406 iter->user_data = GINT_TO_POINTER(i);
408 return i < view_details->items->len;
411 static gboolean details_iter_children(GtkTreeModel *tree_model,
412 GtkTreeIter *iter,
413 GtkTreeIter *parent)
415 ViewDetails *view_details = (ViewDetails *) tree_model;
417 /* this is a list, nodes have no children */
418 if (parent)
419 return FALSE;
421 /* but if parent == NULL we return the list itself as children of the
422 * "root"
425 if (view_details->items->len)
427 iter->user_data = GINT_TO_POINTER(0);
428 return TRUE;
430 else
431 return FALSE;
434 static gboolean details_iter_has_child(GtkTreeModel *tree_model,
435 GtkTreeIter *iter)
437 return FALSE;
440 static gint details_iter_n_children(GtkTreeModel *tree_model, GtkTreeIter *iter)
442 ViewDetails *view_details = (ViewDetails *) tree_model;
444 if (iter == NULL)
445 return view_details->items->len;
447 return 0;
450 static gboolean details_iter_nth_child(GtkTreeModel *tree_model,
451 GtkTreeIter *iter,
452 GtkTreeIter *parent,
453 gint n)
455 ViewDetails *view_details = (ViewDetails *) tree_model;
457 if (parent)
458 return FALSE;
460 if (n >= 0 && n < view_details->items->len)
462 iter->user_data = GINT_TO_POINTER(n);
463 return TRUE;
465 else
466 return FALSE;
469 static gboolean details_iter_parent(GtkTreeModel *tree_model,
470 GtkTreeIter *iter,
471 GtkTreeIter *child)
473 return FALSE;
476 /* A ViewDetails is both a GtkTreeView and a GtkTreeModel.
477 * The following functions implement the model interface...
480 static void view_details_tree_model_init(GtkTreeModelIface *iface)
482 iface->get_flags = details_get_flags;
483 iface->get_n_columns = details_get_n_columns;
484 iface->get_column_type = details_get_column_type;
485 iface->get_iter = details_get_iter;
486 iface->get_path = details_get_path;
487 iface->get_value = details_get_value;
488 iface->iter_next = details_iter_next;
489 iface->iter_children = details_iter_children;
490 iface->iter_has_child = details_iter_has_child;
491 iface->iter_n_children = details_iter_n_children;
492 iface->iter_nth_child = details_iter_nth_child;
493 iface->iter_parent = details_iter_parent;
496 static void view_details_sortable_init(GtkTreeSortableIface *iface)
498 iface->get_sort_column_id = details_get_sort_column_id;
499 iface->set_sort_column_id = details_set_sort_column_id;
500 iface->set_sort_func = details_set_sort_func;
501 iface->set_default_sort_func = details_set_default_sort_func;
502 iface->has_default_sort_func = details_has_default_sort_func;
505 static gboolean details_get_sort_column_id(GtkTreeSortable *sortable,
506 gint *sort_column_id,
507 GtkSortType *order)
509 ViewDetails *view_details = (ViewDetails *) sortable;
511 if (view_details->sort_column_id == -1)
512 return FALSE;
514 if (sort_column_id)
515 *sort_column_id = view_details->sort_column_id;
516 if (order)
517 *order = view_details->order;
518 return TRUE;
521 static void details_set_sort_column_id(GtkTreeSortable *sortable,
522 gint sort_column_id,
523 GtkSortType order)
525 ViewDetails *view_details = (ViewDetails *) sortable;
527 if (view_details->sort_column_id == sort_column_id &&
528 view_details->order == order)
529 return;
531 view_details->sort_column_id = sort_column_id;
532 view_details->order = order;
534 switch (sort_column_id)
536 case COL_LEAF:
537 view_details->sort_fn = sort_by_name;
538 break;
539 case COL_SIZE:
540 view_details->sort_fn = sort_by_size;
541 break;
542 case COL_MTIME:
543 view_details->sort_fn = sort_by_date;
544 break;
545 case COL_TYPE:
546 view_details->sort_fn = sort_by_type;
547 break;
548 case COL_OWNER:
549 view_details->sort_fn = sort_by_owner;
550 break;
551 case COL_GROUP:
552 view_details->sort_fn = sort_by_group;
553 break;
554 default:
555 g_assert_not_reached();
558 view_details_sort((ViewIface *) view_details);
560 gtk_tree_sortable_sort_column_changed(sortable);
563 static void details_set_sort_func(GtkTreeSortable *sortable,
564 gint sort_column_id,
565 GtkTreeIterCompareFunc func,
566 gpointer data,
567 GtkDestroyNotify destroy)
569 g_assert_not_reached();
572 static void details_set_default_sort_func(GtkTreeSortable *sortable,
573 GtkTreeIterCompareFunc func,
574 gpointer data,
575 GtkDestroyNotify destroy)
577 g_assert_not_reached();
580 static gboolean details_has_default_sort_func(GtkTreeSortable *sortable)
582 return FALSE;
586 /* End of model implementation */
588 static gboolean is_selected(ViewDetails *view_details, int i)
590 ViewIter iter;
591 iter.i = i;
592 return view_details_get_selected((ViewIface *) view_details, &iter);
595 static gboolean view_details_button_press(GtkWidget *widget,
596 GdkEventButton *bev)
598 FilerWindow *filer_window = ((ViewDetails *) widget)->filer_window;
600 if (dnd_motion_press(widget, bev))
601 filer_perform_action(filer_window, bev);
603 return TRUE;
606 static gboolean view_details_button_release(GtkWidget *widget,
607 GdkEventButton *bev)
609 FilerWindow *filer_window = ((ViewDetails *) widget)->filer_window;
611 if (!dnd_motion_release(bev))
612 filer_perform_action(filer_window, bev);
614 return TRUE;
617 static gint view_details_key_press_event(GtkWidget *widget, GdkEventKey *event)
619 ViewDetails *view_details = (ViewDetails *) widget;
620 FilerWindow *filer_window = view_details->filer_window;
622 switch (event->keyval)
624 case ' ':
625 filer_window_toggle_cursor_item_selected(filer_window);
626 return TRUE;
627 case GDK_BackSpace:
628 change_to_parent(filer_window);
629 return TRUE;
632 return GTK_WIDGET_CLASS(parent_class)->key_press_event(widget, event);
635 static gint view_details_motion_notify(GtkWidget *widget, GdkEventMotion *event)
637 ViewDetails *view_details = (ViewDetails *) widget;
639 return filer_motion_notify(view_details->filer_window, event);
642 static gboolean view_details_expose(GtkWidget *widget, GdkEventExpose *event)
644 GtkTreeView *tree = (GtkTreeView *) widget;
645 GtkTreePath *path = NULL;
646 GdkRectangle focus_rectangle;
647 gboolean had_cursor;
649 had_cursor = (GTK_WIDGET_FLAGS(widget) & GTK_HAS_FOCUS) != 0;
651 if (had_cursor)
652 GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS);
653 GTK_WIDGET_CLASS(parent_class)->expose_event(widget, event);
654 if (had_cursor)
655 GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
657 if (event->window != gtk_tree_view_get_bin_window(tree))
658 return FALSE; /* Not the main area */
660 gtk_tree_view_get_cursor(tree, &path, NULL);
661 if (!path)
662 return FALSE; /* No cursor */
663 gtk_tree_view_get_background_area(tree, path, NULL, &focus_rectangle);
664 gtk_tree_path_free(path);
666 if (!focus_rectangle.height)
667 return FALSE; /* Off screen */
669 focus_rectangle.width = widget->allocation.width;
671 gtk_paint_focus(widget->style,
672 event->window,
673 GTK_STATE_NORMAL,
674 NULL,
675 widget,
676 "treeview",
677 focus_rectangle.x,
678 focus_rectangle.y,
679 focus_rectangle.width,
680 focus_rectangle.height);
682 return FALSE;
685 static void view_details_destroy(GtkObject *view_details)
687 VIEW_DETAILS(view_details)->filer_window = NULL;
690 static void view_details_finialize(GObject *object)
692 ViewDetails *view_details = (ViewDetails *) object;
694 g_ptr_array_free(view_details->items, TRUE);
695 view_details->items = NULL;
697 G_OBJECT_CLASS(parent_class)->finalize(object);
700 static void view_details_class_init(gpointer gclass, gpointer data)
702 GObjectClass *object = (GObjectClass *) gclass;
703 GtkWidgetClass *widget = (GtkWidgetClass *) gclass;
705 parent_class = g_type_class_peek_parent(gclass);
707 object->finalize = view_details_finialize;
708 GTK_OBJECT_CLASS(object)->destroy = view_details_destroy;
710 widget->button_press_event = view_details_button_press;
711 widget->button_release_event = view_details_button_release;
712 widget->key_press_event = view_details_key_press_event;
713 widget->motion_notify_event = view_details_motion_notify;
714 widget->expose_event = view_details_expose;
717 static void view_details_init(GTypeInstance *object, gpointer gclass)
719 GtkTreeView *treeview = (GtkTreeView *) object;
720 GtkTreeViewColumn *column;
721 GtkCellRenderer *cell;
722 GtkTreeSortable *sortable_list;
723 GtkTreeSelection *selection;
724 ViewDetails *view_details = (ViewDetails *) object;
726 view_details->items = g_ptr_array_new();
727 view_details->cursor_base = -1;
729 selection = gtk_tree_view_get_selection(treeview);
730 gtk_tree_selection_set_mode(selection, GTK_SELECTION_NONE);
732 /* Sorting */
733 view_details->sort_column_id = -1;
734 view_details->sort_fn = NULL;
735 sortable_list = GTK_TREE_SORTABLE(object);
736 gtk_tree_sortable_set_sort_column_id(sortable_list, COL_LEAF,
737 GTK_SORT_ASCENDING);
739 gtk_tree_view_set_model(treeview, GTK_TREE_MODEL(view_details));
741 /* Icon */
742 cell = cell_icon_new();
743 column = gtk_tree_view_column_new_with_attributes(NULL, cell,
744 "item", COL_ITEM,
745 "background-gdk", COL_BG_COLOUR,
746 NULL);
747 gtk_tree_view_append_column(treeview, column);
749 /* Name */
750 cell = gtk_cell_renderer_text_new();
751 column = gtk_tree_view_column_new_with_attributes(_("_Name"), cell,
752 "text", COL_LEAF,
753 "foreground-gdk", COL_COLOUR,
754 "background-gdk", COL_BG_COLOUR,
755 NULL);
756 gtk_tree_view_append_column(treeview, column);
757 gtk_tree_view_column_set_sort_column_id(column, COL_LEAF);
759 /* Type */
760 cell = gtk_cell_renderer_text_new();
761 column = gtk_tree_view_column_new_with_attributes(_("_Type"), cell,
762 "text", COL_TYPE,
763 "foreground-gdk", COL_COLOUR,
764 "background-gdk", COL_BG_COLOUR,
765 NULL);
766 gtk_tree_view_append_column(treeview, column);
767 gtk_tree_view_column_set_sort_column_id(column, COL_TYPE);
769 /* Perm */
770 cell = gtk_cell_renderer_text_new();
771 column = gtk_tree_view_column_new_with_attributes(_("_Permissions"),
772 cell, "text", COL_PERM,
773 "foreground-gdk", COL_COLOUR,
774 "background-gdk", COL_BG_COLOUR,
775 NULL);
776 gtk_tree_view_append_column(treeview, column);
778 /* Owner */
779 cell = gtk_cell_renderer_text_new();
780 column = gtk_tree_view_column_new_with_attributes(_("_Owner"), cell,
781 "text", COL_OWNER,
782 "foreground-gdk", COL_COLOUR,
783 "background-gdk", COL_BG_COLOUR,
784 NULL);
785 gtk_tree_view_append_column(treeview, column);
786 gtk_tree_view_column_set_sort_column_id(column, COL_OWNER);
788 /* Group */
789 cell = gtk_cell_renderer_text_new();
790 column = gtk_tree_view_column_new_with_attributes(_("_Group"), cell,
791 "text", COL_GROUP,
792 "foreground-gdk", COL_COLOUR,
793 "background-gdk", COL_BG_COLOUR,
794 NULL);
795 gtk_tree_view_append_column(treeview, column);
796 gtk_tree_view_column_set_sort_column_id(column, COL_GROUP);
798 /* Size */
799 cell = gtk_cell_renderer_text_new();
800 column = gtk_tree_view_column_new_with_attributes(_("_Size"), cell,
801 "text", COL_SIZE,
802 "foreground-gdk", COL_COLOUR,
803 "background-gdk", COL_BG_COLOUR,
804 NULL);
805 gtk_tree_view_append_column(treeview, column);
806 gtk_tree_view_column_set_sort_column_id(column, COL_SIZE);
808 /* MTime */
809 cell = gtk_cell_renderer_text_new();
810 column = gtk_tree_view_column_new_with_attributes(_("_M-Time"), cell,
811 "text", COL_MTIME,
812 "foreground-gdk", COL_COLOUR,
813 "background-gdk", COL_BG_COLOUR,
814 NULL);
815 gtk_tree_view_append_column(treeview, column);
816 gtk_tree_view_column_set_sort_column_id(column, COL_MTIME);
818 gtk_widget_set_size_request(GTK_WIDGET(treeview), -1, 50);
821 /* Create the handers for the View interface */
822 static void view_details_iface_init(gpointer giface, gpointer iface_data)
824 ViewIfaceClass *iface = giface;
826 g_assert(G_TYPE_FROM_INTERFACE(iface) == VIEW_TYPE_IFACE);
828 /* override stuff */
829 iface->sort = view_details_sort;
830 iface->style_changed = view_details_style_changed;
831 iface->autoselect = view_details_autoselect;
832 iface->add_items = view_details_add_items;
833 iface->update_items = view_details_update_items;
834 iface->delete_if = view_details_delete_if;
835 iface->clear = view_details_clear;
836 iface->select_all = view_details_select_all;
837 iface->clear_selection = view_details_clear_selection;
838 iface->count_items = view_details_count_items;
839 iface->count_selected = view_details_count_selected;
840 iface->show_cursor = view_details_show_cursor;
841 iface->get_iter = view_details_get_iter;
842 iface->get_iter_at_point = view_details_get_iter_at_point;
843 iface->cursor_to_iter = view_details_cursor_to_iter;
844 iface->set_selected = view_details_set_selected;
845 iface->get_selected = view_details_get_selected;
846 iface->set_frozen = view_details_set_frozen;
847 iface->select_only = view_details_select_only;
848 iface->wink_item = view_details_wink_item;
849 iface->autosize = view_details_autosize;
850 iface->cursor_visible = view_details_cursor_visible;
851 iface->set_base = view_details_set_base;
852 iface->start_lasso_box = view_details_start_lasso_box;
853 iface->extend_tip = view_details_extend_tip;
856 /* Implementations of the View interface. See view_iface.c for comments. */
858 static void view_details_style_changed(ViewIface *view, int flags)
862 static gint wrap_sort(gconstpointer a, gconstpointer b,
863 ViewDetails *view_details)
865 ViewItem *ia = *(ViewItem **) a;
866 ViewItem *ib = *(ViewItem **) b;
868 if (view_details->order == GTK_SORT_ASCENDING)
869 return view_details->sort_fn(ia->item, ib->item);
870 else
871 return -view_details->sort_fn(ia->item, ib->item);
874 static void view_details_sort(ViewIface *view)
876 ViewDetails *view_details = (ViewDetails *) view;
877 ViewItem **items = (ViewItem **) view_details->items->pdata;
878 GArray *new_order;
879 gint i, len = view_details->items->len;
880 GtkTreePath *path;
882 g_return_if_fail(view_details->sort_fn != NULL);
884 if (!len)
885 return;
887 for (i = len - 1; i >= 0; i--)
888 items[i]->old_pos = i;
890 g_ptr_array_sort_with_data(view_details->items,
891 (GCompareDataFunc) wrap_sort,
892 view_details);
894 new_order = g_array_sized_new(FALSE, FALSE, sizeof(gint), len);
895 g_array_set_size(new_order, len);
897 for (i = len - 1; i >= 0; i--)
898 g_array_insert_val(new_order, items[i]->old_pos, i);
900 path = gtk_tree_path_new();
901 gtk_tree_model_rows_reordered((GtkTreeModel *) view,
902 path, NULL,
903 (gint *) new_order->data);
904 gtk_tree_path_free(path);
905 g_array_free(new_order, TRUE);
908 static gboolean view_details_autoselect(ViewIface *view, const gchar *leaf)
910 return FALSE;
913 static void view_details_add_items(ViewIface *view, GPtrArray *new_items)
915 ViewDetails *view_details = (ViewDetails *) view;
916 FilerWindow *filer_window = view_details->filer_window;
917 gboolean show_hidden = filer_window->show_hidden;
918 GPtrArray *items = view_details->items;
919 GtkTreeIter iter;
920 int i;
921 GtkTreePath *path;
922 GtkTreeModel *model = (GtkTreeModel *) view;
924 iter.user_data = GINT_TO_POINTER(items->len);
925 path = details_get_path(model, &iter);
927 for (i = 0; i < new_items->len; i++)
929 DirItem *item = (DirItem *) new_items->pdata[i];
930 char *leafname = item->leafname;
931 ViewItem *vitem;
933 if (leafname[0] == '.')
935 if (!show_hidden)
936 continue;
938 if (leafname[1] == '\0')
939 continue; /* Never show '.' */
941 if (leafname[1] == '.' &&
942 leafname[2] == '\0')
943 continue; /* Never show '..' */
946 vitem = g_new(ViewItem, 1);
947 vitem->item = item;
948 vitem->image = NULL;
949 vitem->selected = FALSE;
951 g_ptr_array_add(items, vitem);
953 iter.user_data = GINT_TO_POINTER(items->len - 1);
954 gtk_tree_model_row_inserted(model, path, &iter);
955 gtk_tree_path_next(path);
958 gtk_tree_path_free(path);
960 view_details_sort(view);
963 /* Find an item in the sorted array.
964 * Returns the item number, or -1 if not found.
966 static int details_find_item(ViewDetails *view_details, DirItem *item)
968 ViewItem **items, tmp, *tmpp;
969 int lower, upper;
970 int (*compar)(const void *, const void *);
972 g_return_val_if_fail(view_details != NULL, -1);
973 g_return_val_if_fail(item != NULL, -1);
975 tmp.item = item;
976 tmpp = &tmp;
978 items = (ViewItem **) view_details->items->pdata;
979 compar = view_details->sort_fn;
981 g_return_val_if_fail(compar != NULL, -1);
983 /* If item is here, then: lower <= i < upper */
984 lower = 0;
985 upper = view_details->items->len;
987 while (lower < upper)
989 int i, cmp;
991 i = (lower + upper) >> 1;
993 cmp = wrap_sort(&items[i], &tmpp, view_details);
994 if (cmp == 0)
995 return i;
997 if (cmp > 0)
998 upper = i;
999 else
1000 lower = i + 1;
1003 return -1;
1006 static void view_details_update_items(ViewIface *view, GPtrArray *items)
1008 ViewDetails *view_details = (ViewDetails *) view;
1009 FilerWindow *filer_window = view_details->filer_window;
1010 int i;
1011 GtkTreeModel *model = (GtkTreeModel *) view_details;
1013 g_return_if_fail(items->len > 0);
1015 /* The item data has already been modified, so this gives the
1016 * final sort order...
1018 view_details_sort(view);
1020 for (i = 0; i < items->len; i++)
1022 DirItem *item = (DirItem *) items->pdata[i];
1023 const gchar *leafname = item->leafname;
1024 int j;
1026 if (leafname[0] == '.' && filer_window->show_hidden == FALSE)
1027 continue;
1029 j = details_find_item(view_details, item);
1031 if (j < 0)
1032 g_warning("Failed to find '%s'\n", leafname);
1033 else
1035 GtkTreePath *path;
1036 GtkTreeIter iter;
1037 path = gtk_tree_path_new();
1038 gtk_tree_path_append_index(path, j);
1039 iter.user_data = GINT_TO_POINTER(j);
1040 gtk_tree_model_row_changed(model, path, &iter);
1045 static void view_details_delete_if(ViewIface *view,
1046 gboolean (*test)(gpointer item, gpointer data),
1047 gpointer data)
1049 GtkTreePath *path;
1050 ViewDetails *view_details = (ViewDetails *) view;
1051 int i = 0;
1052 GPtrArray *items = view_details->items;
1053 GtkTreeModel *model = (GtkTreeModel *) view;
1055 path = gtk_tree_path_new();
1057 gtk_tree_path_append_index(path, i);
1059 while (i < items->len)
1061 ViewItem *item = items->pdata[i];
1063 if (test(item->item, data))
1065 g_free(items->pdata[i]);
1066 g_ptr_array_remove_index(items, i);
1067 gtk_tree_model_row_deleted(model, path);
1069 else
1071 i++;
1072 gtk_tree_path_next(path);
1076 gtk_tree_path_free(path);
1079 static void view_details_clear(ViewIface *view)
1081 GtkTreePath *path;
1082 GPtrArray *items = ((ViewDetails *) view)->items;
1083 GtkTreeModel *model = (GtkTreeModel *) view;
1085 path = gtk_tree_path_new();
1086 gtk_tree_path_append_index(path, items->len);
1088 while (gtk_tree_path_prev(path))
1089 gtk_tree_model_row_deleted(model, path);
1091 g_ptr_array_set_size(items, 0);
1092 gtk_tree_path_free(path);
1095 static void view_details_select_all(ViewIface *view)
1097 int i;
1098 int n = ((ViewDetails *) view)->items->len;
1100 for (i = 0; i < n; i++)
1102 ViewIter iter;
1103 iter.i = i;
1104 view_details_set_selected(view, &iter, TRUE);
1108 static void view_details_clear_selection(ViewIface *view)
1110 int i;
1111 int n = ((ViewDetails *) view)->items->len;
1113 for (i = 0; i < n; i++)
1115 ViewIter iter;
1116 iter.i = i;
1117 view_details_set_selected(view, &iter, FALSE);
1121 static int view_details_count_items(ViewIface *view)
1123 ViewDetails *view_details = (ViewDetails *) view;
1125 return view_details->items->len;
1128 static int view_details_count_selected(ViewIface *view)
1130 ViewDetails *view_details = (ViewDetails *) view;
1131 int n = view_details->items->len;
1132 ViewItem **items = (ViewItem **) view_details->items->pdata;
1133 int count = 0;
1134 int i;
1136 for (i = 0; i < n; i++)
1137 if (items[i]->selected)
1138 count++;
1140 return count;
1143 static void view_details_show_cursor(ViewIface *view)
1147 static void view_details_get_iter(ViewIface *view,
1148 ViewIter *iter, IterFlags flags)
1150 make_iter((ViewDetails *) view, iter, flags);
1153 static void view_details_get_iter_at_point(ViewIface *view, ViewIter *iter,
1154 int x, int y)
1156 ViewDetails *view_details = (ViewDetails *) view;
1157 GtkTreeModel *model;
1158 GtkTreeView *tree = (GtkTreeView *) view;
1159 GtkTreePath *path = NULL;
1160 int i = -1;
1162 model = gtk_tree_view_get_model(tree);
1164 if (gtk_tree_view_get_path_at_pos(tree, x, y, &path, NULL, NULL, NULL))
1166 g_return_if_fail(path != NULL);
1168 i = gtk_tree_path_get_indices(path)[0];
1169 gtk_tree_path_free(path);
1172 make_item_iter(view_details, iter, i);
1175 static void view_details_cursor_to_iter(ViewIface *view, ViewIter *iter)
1177 GtkTreePath *path;
1178 ViewDetails *view_details = (ViewDetails *) view;
1180 path = gtk_tree_path_new();
1182 if (iter)
1183 gtk_tree_path_append_index(path, iter->i);
1184 else
1186 /* Using depth zero or index -1 gives an error, but this
1187 * is OK!
1189 gtk_tree_path_append_index(path, view_details->items->len);
1192 gtk_tree_view_set_cursor((GtkTreeView *) view, path, NULL, FALSE);
1193 gtk_tree_path_free(path);
1196 static void set_selected(ViewDetails *view_details, int i, gboolean selected)
1198 GtkTreeModel *model = (GtkTreeModel *) view_details;
1199 GtkTreeIter t_iter;
1200 GtkTreePath *path;
1201 GPtrArray *items = view_details->items;
1202 ViewItem *view_item;
1204 g_return_if_fail(i >= 0 && i < items->len);
1205 view_item = (ViewItem *) items->pdata[i];
1207 if (view_item->selected == selected)
1208 return;
1210 view_item->selected = selected;
1212 path = gtk_tree_path_new();
1213 gtk_tree_path_append_index(path, i);
1214 t_iter.user_data = GINT_TO_POINTER(i);
1215 gtk_tree_model_row_changed(model, path, &t_iter);
1216 gtk_tree_path_free(path);
1219 static void view_details_set_selected(ViewIface *view,
1220 ViewIter *iter,
1221 gboolean selected)
1223 set_selected((ViewDetails *) view, iter->i, selected);
1226 static gboolean get_selected(ViewDetails *view_details, int i)
1228 GPtrArray *items = view_details->items;
1230 g_return_val_if_fail(i >= 0 && i < items->len, FALSE);
1232 return ((ViewItem *) items->pdata[i])->selected;
1235 static gboolean view_details_get_selected(ViewIface *view, ViewIter *iter)
1237 return get_selected((ViewDetails *) view, iter->i);
1240 static void view_details_select_only(ViewIface *view, ViewIter *iter)
1242 ViewDetails *view_details = (ViewDetails *) view;
1243 int i = iter->i;
1244 int n = view_details->items->len;
1246 set_selected(view_details, i, TRUE);
1248 while (n > 0)
1250 n--;
1252 if (n != i)
1253 set_selected(view_details, n, FALSE);
1257 static void view_details_set_frozen(ViewIface *view, gboolean frozen)
1261 static void view_details_wink_item(ViewIface *view, ViewIter *iter)
1265 static void view_details_autosize(ViewIface *view)
1267 ViewDetails *view_details = (ViewDetails *) view;
1268 FilerWindow *filer_window = view_details->filer_window;
1269 GdkWindow *bin;
1270 int max_height = (o_filer_size_limit.int_value * screen_height) / 100;
1271 int h, y;
1272 GtkTreeView *tree = (GtkTreeView *) view;
1273 GtkTreeViewColumn *column;
1274 GtkRequisition req;
1276 gtk_widget_size_request(GTK_WIDGET(view), &req);
1277 column = gtk_tree_view_get_column(tree, 1);
1278 gtk_tree_view_column_cell_get_size(column, NULL, NULL, NULL, NULL, &h);
1280 bin = gtk_tree_view_get_bin_window(GTK_TREE_VIEW(view));
1282 gdk_window_get_position(bin, NULL, &y);
1284 h = MAX(h, SMALL_HEIGHT);
1286 h = (view_details->items->len + 2) * h + y;
1288 h = MIN(h, max_height);
1290 filer_window_set_size(filer_window, 5, h);
1293 static gboolean view_details_cursor_visible(ViewIface *view)
1295 return FALSE;
1298 static void view_details_set_base(ViewIface *view, ViewIter *iter)
1300 ViewDetails *view_details = (ViewDetails *) view;
1302 view_details->cursor_base = iter->i;
1305 static void view_details_start_lasso_box(ViewIface *view, GdkEventButton *event)
1309 static void view_details_extend_tip(ViewIface *view,
1310 ViewIter *iter, GString *tip)
1314 static DirItem *iter_init(ViewIter *iter)
1316 ViewDetails *view_details = (ViewDetails *) iter->view;
1317 int i = -1;
1318 int n = view_details->items->len;
1319 int flags = iter->flags;
1321 iter->peek = iter_peek;
1323 if (iter->n_remaining == 0)
1324 return NULL;
1326 if (flags & VIEW_ITER_FROM_CURSOR)
1328 GtkTreePath *path;
1329 gtk_tree_view_get_cursor((GtkTreeView *) view_details,
1330 &path, NULL);
1331 if (!path)
1332 return NULL; /* No cursor */
1333 i = gtk_tree_path_get_indices(path)[0];
1334 gtk_tree_path_free(path);
1336 else if (flags & VIEW_ITER_FROM_BASE)
1337 i = view_details->cursor_base;
1339 if (i < 0 || i >= n)
1341 /* Either a normal iteration, or an iteration from an
1342 * invalid starting point.
1344 if (flags & VIEW_ITER_BACKWARDS)
1345 i = n - 1;
1346 else
1347 i = 0;
1350 if (i < 0 || i >= n)
1351 return NULL; /* No items at all! */
1353 iter->next = flags & VIEW_ITER_BACKWARDS ? iter_prev : iter_next;
1354 iter->n_remaining--;
1355 iter->i = i;
1357 if (flags & VIEW_ITER_SELECTED && !is_selected(view_details, i))
1358 return iter->next(iter);
1359 return iter->peek(iter);
1362 static DirItem *iter_prev(ViewIter *iter)
1364 ViewDetails *view_details = (ViewDetails *) iter->view;
1365 int n = view_details->items->len;
1366 int i = iter->i;
1368 g_return_val_if_fail(iter->n_remaining >= 0, NULL);
1370 /* i is the last item returned (always valid) */
1372 g_return_val_if_fail(i >= 0 && i < n, NULL);
1374 while (iter->n_remaining)
1376 i--;
1377 iter->n_remaining--;
1379 if (i == -1)
1380 i = n - 1;
1382 g_return_val_if_fail(i >= 0 && i < n, NULL);
1384 if (iter->flags & VIEW_ITER_SELECTED &&
1385 !is_selected(view_details, i))
1386 continue;
1388 iter->i = i;
1389 return ((ViewItem *) view_details->items->pdata[i])->item;
1392 iter->i = -1;
1393 return NULL;
1396 static DirItem *iter_next(ViewIter *iter)
1398 ViewDetails *view_details = (ViewDetails *) iter->view;
1399 int n = view_details->items->len;
1400 int i = iter->i;
1402 g_return_val_if_fail(iter->n_remaining >= 0, NULL);
1404 /* i is the last item returned (always valid) */
1406 g_return_val_if_fail(i >= 0 && i < n, NULL);
1408 while (iter->n_remaining)
1410 i++;
1411 iter->n_remaining--;
1413 if (i == n)
1414 i = 0;
1416 g_return_val_if_fail(i >= 0 && i < n, NULL);
1418 if (iter->flags & VIEW_ITER_SELECTED &&
1419 !is_selected(view_details, i))
1420 continue;
1422 iter->i = i;
1423 return ((ViewItem *) view_details->items->pdata[i])->item;
1426 iter->i = -1;
1427 return NULL;
1430 static DirItem *iter_peek(ViewIter *iter)
1432 ViewDetails *view_details = (ViewDetails *) iter->view;
1433 int n = view_details->items->len;
1434 int i = iter->i;
1436 if (i == -1)
1437 return NULL;
1439 g_return_val_if_fail(i >= 0 && i < n, NULL);
1441 return ((ViewItem *) view_details->items->pdata[i])->item;
1444 /* Set the iterator to return 'i' on the next peek().
1445 * If i is -1, returns NULL on next peek().
1447 static void make_item_iter(ViewDetails *view_details, ViewIter *iter, int i)
1449 make_iter(view_details, iter, 0);
1451 g_return_if_fail(i >= -1 && i < (int) view_details->items->len);
1453 iter->i = i;
1454 iter->next = iter_next;
1455 iter->peek = iter_peek;
1456 iter->n_remaining = 0;
1459 static void make_iter(ViewDetails *view_details, ViewIter *iter,
1460 IterFlags flags)
1462 iter->view = (ViewIface *) view_details;
1463 iter->next = iter_init;
1464 iter->peek = NULL;
1465 iter->i = -1;
1467 iter->flags = flags;
1469 if (flags & VIEW_ITER_ONE_ONLY)
1471 iter->n_remaining = 1;
1472 iter->next(iter);
1474 else
1475 iter->n_remaining = view_details->items->len;