r2222: Abstracted some code from view_collection to filer.
[rox-filer.git] / ROX-Filer / src / view_details.c
blob8ab6524bf78a2f8a0d0c2a50b6f41302766f1137
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"
46 /* These are the column numbers in the ListStore */
47 #define COL_LEAF 0
48 #define COL_TYPE 1
49 #define COL_PERM 2
50 #define COL_OWNER 3
51 #define COL_GROUP 4
52 #define COL_SIZE 5
53 #define COL_MTIME 6
54 #define COL_ITEM 7
55 #define COL_COLOUR 8
56 #define COL_ICON 9
57 #define COL_BG_COLOUR 10
58 #define N_COLUMNS 11
60 static gpointer parent_class = NULL;
62 struct _ViewDetailsClass {
63 GtkTreeViewClass parent;
66 typedef struct _ViewItem ViewItem;
68 struct _ViewItem {
69 DirItem *item;
70 GdkPixbuf *image;
71 int old_pos; /* Used while sorting */
72 gboolean selected;
75 typedef struct _ViewDetails ViewDetails;
77 struct _ViewDetails {
78 GtkTreeView treeview;
80 FilerWindow *filer_window; /* Used for styles, etc */
82 GPtrArray *items; /* ViewItem */
84 gint sort_column_id;
85 GtkSortType order;
86 int (*sort_fn)(const void *, const void *);
88 int cursor_base; /* Cursor when minibuffer opened */
91 /* Static prototypes */
92 static void view_details_finialize(GObject *object);
93 static void view_details_class_init(gpointer gclass, gpointer data);
94 static void view_details_init(GTypeInstance *object, gpointer gclass);
96 static void view_details_iface_init(gpointer giface, gpointer iface_data);
98 static void view_details_sort(ViewIface *view);
99 static void view_details_style_changed(ViewIface *view, int flags);
100 static gboolean view_details_autoselect(ViewIface *view, const gchar *leaf);
101 static void view_details_add_items(ViewIface *view, GPtrArray *items);
102 static void view_details_update_items(ViewIface *view, GPtrArray *items);
103 static void view_details_delete_if(ViewIface *view,
104 gboolean (*test)(gpointer item, gpointer data),
105 gpointer data);
106 static void view_details_clear(ViewIface *view);
107 static void view_details_select_all(ViewIface *view);
108 static void view_details_clear_selection(ViewIface *view);
109 static int view_details_count_items(ViewIface *view);
110 static int view_details_count_selected(ViewIface *view);
111 static void view_details_show_cursor(ViewIface *view);
112 static void view_details_get_iter(ViewIface *view,
113 ViewIter *iter, IterFlags flags);
114 static void view_details_get_iter_at_point(ViewIface *view, ViewIter *iter,
115 int x, int y);
116 static void view_details_cursor_to_iter(ViewIface *view, ViewIter *iter);
117 static void view_details_set_selected(ViewIface *view,
118 ViewIter *iter,
119 gboolean selected);
120 static gboolean view_details_get_selected(ViewIface *view, ViewIter *iter);
121 static void view_details_select_only(ViewIface *view, ViewIter *iter);
122 static void view_details_set_frozen(ViewIface *view, gboolean frozen);
123 static void view_details_wink_item(ViewIface *view, ViewIter *iter);
124 static void view_details_autosize(ViewIface *view);
125 static gboolean view_details_cursor_visible(ViewIface *view);
126 static void view_details_set_base(ViewIface *view, ViewIter *iter);
127 static void view_details_start_lasso_box(ViewIface *view,
128 GdkEventButton *event);
129 static void view_details_extend_tip(ViewIface *view,
130 ViewIter *iter, GString *tip);
132 static DirItem *iter_peek(ViewIter *iter);
133 static DirItem *iter_prev(ViewIter *iter);
134 static DirItem *iter_next(ViewIter *iter);
135 static void make_iter(ViewDetails *view_details, ViewIter *iter,
136 IterFlags flags);
137 static void make_item_iter(ViewDetails *view_details, ViewIter *iter, int i);
138 static void view_details_tree_model_init(GtkTreeModelIface *iface);
139 static gboolean details_get_sort_column_id(GtkTreeSortable *sortable,
140 gint *sort_column_id,
141 GtkSortType *order);
142 static void details_set_sort_column_id(GtkTreeSortable *sortable,
143 gint sort_column_id,
144 GtkSortType order);
145 static void details_set_sort_func(GtkTreeSortable *sortable,
146 gint sort_column_id,
147 GtkTreeIterCompareFunc func,
148 gpointer data,
149 GtkDestroyNotify destroy);
150 static void details_set_default_sort_func(GtkTreeSortable *sortable,
151 GtkTreeIterCompareFunc func,
152 gpointer data,
153 GtkDestroyNotify destroy);
154 static gboolean details_has_default_sort_func(GtkTreeSortable *sortable);
155 static void view_details_sortable_init(GtkTreeSortableIface *iface);
156 static void set_selected(ViewDetails *view_details, int i, gboolean selected);
157 static gboolean get_selected(ViewDetails *view_details, int i);
160 /****************************************************************
161 * EXTERNAL INTERFACE *
162 ****************************************************************/
164 GtkWidget *view_details_new(FilerWindow *filer_window)
166 ViewDetails *view_details;
168 view_details = g_object_new(view_details_get_type(), NULL);
169 view_details->filer_window = filer_window;
171 gtk_range_set_adjustment(GTK_RANGE(filer_window->scrollbar),
172 gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(view_details)));
174 return GTK_WIDGET(view_details);
177 GType view_details_get_type(void)
179 static GType type = 0;
181 if (!type)
183 static const GTypeInfo info =
185 sizeof (ViewDetailsClass),
186 NULL, /* base_init */
187 NULL, /* base_finalise */
188 view_details_class_init,
189 NULL, /* class_finalise */
190 NULL, /* class_data */
191 sizeof(ViewDetails),
192 0, /* n_preallocs */
193 view_details_init
195 static const GInterfaceInfo view_iface_info = {
196 view_details_iface_init,
197 NULL, NULL
199 static const GInterfaceInfo tree_model_info = {
200 (GInterfaceInitFunc) view_details_tree_model_init,
201 NULL, NULL
203 static const GInterfaceInfo sortable_info = {
204 (GInterfaceInitFunc) view_details_sortable_init,
205 NULL, NULL
209 type = g_type_register_static(gtk_tree_view_get_type(),
210 "ViewDetails", &info, 0);
212 g_type_add_interface_static(type, VIEW_TYPE_IFACE,
213 &view_iface_info);
214 g_type_add_interface_static(type, GTK_TYPE_TREE_MODEL,
215 &tree_model_info);
216 g_type_add_interface_static(type, GTK_TYPE_TREE_SORTABLE,
217 &sortable_info);
220 return type;
223 /****************************************************************
224 * INTERNAL FUNCTIONS *
225 ****************************************************************/
227 /* Fulfill the GtkTreeModel requirements */
228 static guint details_get_flags(GtkTreeModel *tree_model)
230 return GTK_TREE_MODEL_LIST_ONLY;
233 static gint details_get_n_columns(GtkTreeModel *tree_model)
235 return N_COLUMNS;
238 static GType details_get_column_type(GtkTreeModel *tree_model, gint index)
240 g_return_val_if_fail(index < N_COLUMNS && index >= 0, G_TYPE_INVALID);
242 if (index == COL_COLOUR || index == COL_BG_COLOUR)
243 return GDK_TYPE_COLOR;
244 else if (index == COL_ITEM)
245 return G_TYPE_POINTER;
246 else if (index == COL_ICON)
247 return GDK_TYPE_PIXBUF;
248 return G_TYPE_STRING;
251 static gboolean details_get_iter(GtkTreeModel *tree_model,
252 GtkTreeIter *iter,
253 GtkTreePath *path)
255 ViewDetails *view_details = (ViewDetails *) tree_model;
256 gint i;
258 g_return_val_if_fail(gtk_tree_path_get_depth (path) > 0, FALSE);
260 i = gtk_tree_path_get_indices(path)[0];
262 if (i >= view_details->items->len)
263 return FALSE;
265 iter->user_data = GINT_TO_POINTER(i);
267 return TRUE;
270 static GtkTreePath *details_get_path(GtkTreeModel *tree_model,
271 GtkTreeIter *iter)
273 GtkTreePath *retval;
275 retval = gtk_tree_path_new();
276 gtk_tree_path_append_index(retval, GPOINTER_TO_INT(iter->user_data));
278 return retval;
281 static void details_get_value(GtkTreeModel *tree_model,
282 GtkTreeIter *iter,
283 gint column,
284 GValue *value)
286 ViewDetails *view_details = (ViewDetails *) tree_model;
287 GtkStyle *style = ((GtkWidget *) tree_model)->style;
288 gint i;
289 GPtrArray *items = view_details->items;
290 ViewItem *view_item;
291 DirItem *item;
292 mode_t m;
294 g_return_if_fail(column >= 0 && column < N_COLUMNS);
296 i = GPOINTER_TO_INT(iter->user_data);
297 g_return_if_fail(i >= 0 && i < items->len);
298 view_item = (ViewItem *) items->pdata[i];
299 item = view_item->item;
301 /* g_print("[ get %d ]\n", column); */
303 if (column == COL_LEAF)
305 g_value_init(value, G_TYPE_STRING);
306 g_value_set_string(value, item->leafname);
307 return;
309 else if (column == COL_ITEM)
311 g_value_init(value, G_TYPE_POINTER);
312 g_value_set_pointer(value, item);
313 return;
316 if (item->base_type == TYPE_UNKNOWN)
318 GType type;
319 type = details_get_column_type(tree_model, column);
320 g_value_init(value, type);
321 if (type == G_TYPE_STRING)
322 g_value_set_string(value, "");
323 else if (type == GDK_TYPE_COLOR)
324 g_value_set_boxed(value, NULL);
325 else
326 g_value_set_object(value, NULL);
328 return;
330 m = item->mode;
332 switch (column)
334 case COL_LEAF:
335 g_value_init(value, G_TYPE_STRING);
336 g_value_set_string(value, item->leafname);
337 break;
338 case COL_ICON:
339 g_value_init(value, GDK_TYPE_PIXBUF);
340 if (!item->image->sm_pixbuf)
341 pixmap_make_small(item->image);
342 g_value_set_object(value, item->image->sm_pixbuf);
343 break;
344 case COL_COLOUR:
345 g_value_init(value, GDK_TYPE_COLOR);
346 g_value_set_boxed(value, type_get_colour(item, NULL));
347 break;
348 case COL_BG_COLOUR:
349 g_value_init(value, GDK_TYPE_COLOR);
350 g_value_set_boxed(value, view_item->selected
351 ? &style->base[GTK_STATE_SELECTED]
352 : &style->base[GTK_STATE_NORMAL]);
353 break;
354 case COL_OWNER:
355 g_value_init(value, G_TYPE_STRING);
356 g_value_set_string(value, user_name(item->uid));
357 break;
358 case COL_GROUP:
359 g_value_init(value, G_TYPE_STRING);
360 g_value_set_string(value, group_name(item->gid));
361 break;
362 case COL_MTIME:
364 gchar *time;
365 time = pretty_time(&item->mtime);
366 g_value_init(value, G_TYPE_STRING);
367 g_value_set_string(value, time);
368 g_free(time);
369 break;
371 case COL_PERM:
372 g_value_init(value, G_TYPE_STRING);
373 g_value_set_string(value, pretty_permissions(m));
374 break;
375 case COL_SIZE:
376 g_value_init(value, G_TYPE_STRING);
377 if (item->base_type != TYPE_DIRECTORY)
378 g_value_set_string(value,
379 format_size(item->size));
380 break;
381 case COL_TYPE:
382 g_value_init(value, G_TYPE_STRING);
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 break;
394 default:
395 g_value_init(value, G_TYPE_STRING);
396 g_value_set_string(value, "Hello");
397 break;
401 static gboolean details_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter)
403 ViewDetails *view_details = (ViewDetails *) tree_model;
404 int i;
406 i = GPOINTER_TO_INT(iter->user_data) + 1;
407 iter->user_data = GINT_TO_POINTER(i);
409 return i < view_details->items->len;
412 static gboolean details_iter_children(GtkTreeModel *tree_model,
413 GtkTreeIter *iter,
414 GtkTreeIter *parent)
416 ViewDetails *view_details = (ViewDetails *) tree_model;
418 /* this is a list, nodes have no children */
419 if (parent)
420 return FALSE;
422 /* but if parent == NULL we return the list itself as children of the
423 * "root"
426 if (view_details->items->len)
428 iter->user_data = GINT_TO_POINTER(0);
429 return TRUE;
431 else
432 return FALSE;
435 static gboolean details_iter_has_child(GtkTreeModel *tree_model,
436 GtkTreeIter *iter)
438 return FALSE;
441 static gint details_iter_n_children(GtkTreeModel *tree_model, GtkTreeIter *iter)
443 ViewDetails *view_details = (ViewDetails *) tree_model;
445 if (iter == NULL)
446 return view_details->items->len;
448 return 0;
451 static gboolean details_iter_nth_child(GtkTreeModel *tree_model,
452 GtkTreeIter *iter,
453 GtkTreeIter *parent,
454 gint n)
456 ViewDetails *view_details = (ViewDetails *) tree_model;
458 if (parent)
459 return FALSE;
461 if (n >= 0 && n < view_details->items->len)
463 iter->user_data = GINT_TO_POINTER(n);
464 return TRUE;
466 else
467 return FALSE;
470 static gboolean details_iter_parent(GtkTreeModel *tree_model,
471 GtkTreeIter *iter,
472 GtkTreeIter *child)
474 return FALSE;
477 /* A ViewDetails is both a GtkTreeView and a GtkTreeModel.
478 * The following functions implement the model interface...
481 static void view_details_tree_model_init(GtkTreeModelIface *iface)
483 iface->get_flags = details_get_flags;
484 iface->get_n_columns = details_get_n_columns;
485 iface->get_column_type = details_get_column_type;
486 iface->get_iter = details_get_iter;
487 iface->get_path = details_get_path;
488 iface->get_value = details_get_value;
489 iface->iter_next = details_iter_next;
490 iface->iter_children = details_iter_children;
491 iface->iter_has_child = details_iter_has_child;
492 iface->iter_n_children = details_iter_n_children;
493 iface->iter_nth_child = details_iter_nth_child;
494 iface->iter_parent = details_iter_parent;
497 static void view_details_sortable_init(GtkTreeSortableIface *iface)
499 iface->get_sort_column_id = details_get_sort_column_id;
500 iface->set_sort_column_id = details_set_sort_column_id;
501 iface->set_sort_func = details_set_sort_func;
502 iface->set_default_sort_func = details_set_default_sort_func;
503 iface->has_default_sort_func = details_has_default_sort_func;
506 static gboolean details_get_sort_column_id(GtkTreeSortable *sortable,
507 gint *sort_column_id,
508 GtkSortType *order)
510 ViewDetails *view_details = (ViewDetails *) sortable;
512 if (view_details->sort_column_id == -1)
513 return FALSE;
515 if (sort_column_id)
516 *sort_column_id = view_details->sort_column_id;
517 if (order)
518 *order = view_details->order;
519 return TRUE;
522 static void details_set_sort_column_id(GtkTreeSortable *sortable,
523 gint sort_column_id,
524 GtkSortType order)
526 ViewDetails *view_details = (ViewDetails *) sortable;
528 if (view_details->sort_column_id == sort_column_id &&
529 view_details->order == order)
530 return;
532 view_details->sort_column_id = sort_column_id;
533 view_details->order = order;
535 switch (sort_column_id)
537 case COL_LEAF:
538 view_details->sort_fn = sort_by_name;
539 break;
540 case COL_SIZE:
541 view_details->sort_fn = sort_by_size;
542 break;
543 case COL_MTIME:
544 view_details->sort_fn = sort_by_date;
545 break;
546 case COL_TYPE:
547 view_details->sort_fn = sort_by_type;
548 break;
549 case COL_OWNER:
550 view_details->sort_fn = sort_by_owner;
551 break;
552 case COL_GROUP:
553 view_details->sort_fn = sort_by_group;
554 break;
555 default:
556 g_assert_not_reached();
559 view_details_sort((ViewIface *) view_details);
561 gtk_tree_sortable_sort_column_changed(sortable);
564 static void details_set_sort_func(GtkTreeSortable *sortable,
565 gint sort_column_id,
566 GtkTreeIterCompareFunc func,
567 gpointer data,
568 GtkDestroyNotify destroy)
570 g_assert_not_reached();
573 static void details_set_default_sort_func(GtkTreeSortable *sortable,
574 GtkTreeIterCompareFunc func,
575 gpointer data,
576 GtkDestroyNotify destroy)
578 g_assert_not_reached();
581 static gboolean details_has_default_sort_func(GtkTreeSortable *sortable)
583 return FALSE;
587 /* End of model implementation */
589 static gboolean is_selected(ViewDetails *view_details, int i)
591 ViewIter iter;
592 iter.i = i;
593 return view_details_get_selected((ViewIface *) view_details, &iter);
596 static gboolean view_details_button_press(GtkWidget *widget,
597 GdkEventButton *bev)
599 FilerWindow *filer_window = ((ViewDetails *) widget)->filer_window;
601 if (dnd_motion_press(widget, bev))
602 filer_perform_action(filer_window, bev);
604 return TRUE;
607 static gboolean view_details_button_release(GtkWidget *widget,
608 GdkEventButton *bev)
610 FilerWindow *filer_window = ((ViewDetails *) widget)->filer_window;
612 if (!dnd_motion_release(bev))
613 filer_perform_action(filer_window, bev);
615 return TRUE;
618 static gint view_details_key_press_event(GtkWidget *widget, GdkEventKey *event)
620 if (event->keyval == ' ')
622 ViewDetails *view_details = (ViewDetails *) widget;
623 FilerWindow *filer_window = view_details->filer_window;
625 filer_window_toggle_cursor_item_selected(filer_window);
626 return TRUE;
629 return GTK_WIDGET_CLASS(parent_class)->key_press_event(widget, event);
632 static gint view_details_motion_notify(GtkWidget *widget, GdkEventMotion *event)
634 ViewDetails *view_details = (ViewDetails *) widget;
636 return filer_motion_notify(view_details->filer_window, event);
639 static void view_details_destroy(GtkObject *view_details)
641 VIEW_DETAILS(view_details)->filer_window = NULL;
644 static void view_details_finialize(GObject *object)
646 ViewDetails *view_details = (ViewDetails *) object;
648 g_ptr_array_free(view_details->items, TRUE);
649 view_details->items = NULL;
651 G_OBJECT_CLASS(parent_class)->finalize(object);
654 static void view_details_class_init(gpointer gclass, gpointer data)
656 GObjectClass *object = (GObjectClass *) gclass;
657 GtkWidgetClass *widget = (GtkWidgetClass *) gclass;
659 parent_class = g_type_class_peek_parent(gclass);
661 object->finalize = view_details_finialize;
662 GTK_OBJECT_CLASS(object)->destroy = view_details_destroy;
664 widget->button_press_event = view_details_button_press;
665 widget->button_release_event = view_details_button_release;
666 widget->key_press_event = view_details_key_press_event;
667 widget->motion_notify_event = view_details_motion_notify;
670 static void view_details_init(GTypeInstance *object, gpointer gclass)
672 GtkTreeView *treeview = (GtkTreeView *) object;
673 GtkTreeViewColumn *column;
674 GtkCellRenderer *cell;
675 GtkTreeSortable *sortable_list;
676 GtkTreeSelection *selection;
677 ViewDetails *view_details = (ViewDetails *) object;
679 view_details->items = g_ptr_array_new();
680 view_details->cursor_base = -1;
682 selection = gtk_tree_view_get_selection(treeview);
683 gtk_tree_selection_set_mode(selection, GTK_SELECTION_NONE);
685 /* Sorting */
686 view_details->sort_column_id = -1;
687 view_details->sort_fn = NULL;
688 sortable_list = GTK_TREE_SORTABLE(object);
689 gtk_tree_sortable_set_sort_column_id(sortable_list, COL_LEAF,
690 GTK_SORT_ASCENDING);
692 gtk_tree_view_set_model(treeview, GTK_TREE_MODEL(view_details));
694 /* Icon */
695 cell = gtk_cell_renderer_pixbuf_new();
696 column = gtk_tree_view_column_new_with_attributes(NULL, cell,
697 "pixbuf", COL_ICON, NULL);
698 gtk_tree_view_append_column(treeview, column);
700 /* Name */
701 cell = gtk_cell_renderer_text_new();
702 column = gtk_tree_view_column_new_with_attributes(_("Name"), cell,
703 "text", COL_LEAF,
704 "foreground-gdk", COL_COLOUR,
705 "background-gdk", COL_BG_COLOUR,
706 NULL);
707 gtk_tree_view_append_column(treeview, column);
708 gtk_tree_view_column_set_sort_column_id(column, COL_LEAF);
710 /* Type */
711 cell = gtk_cell_renderer_text_new();
712 column = gtk_tree_view_column_new_with_attributes(_("Type"), cell,
713 "text", COL_TYPE,
714 "foreground-gdk", COL_COLOUR,
715 "background-gdk", COL_BG_COLOUR,
716 NULL);
717 gtk_tree_view_append_column(treeview, column);
718 gtk_tree_view_column_set_sort_column_id(column, COL_TYPE);
720 /* Perm */
721 cell = gtk_cell_renderer_text_new();
722 column = gtk_tree_view_column_new_with_attributes(_("Permissions"),
723 cell, "text", COL_PERM,
724 "foreground-gdk", COL_COLOUR,
725 "background-gdk", COL_BG_COLOUR,
726 NULL);
727 gtk_tree_view_append_column(treeview, column);
729 /* Owner */
730 cell = gtk_cell_renderer_text_new();
731 column = gtk_tree_view_column_new_with_attributes(_("Owner"), cell,
732 "text", COL_OWNER,
733 "foreground-gdk", COL_COLOUR,
734 "background-gdk", COL_BG_COLOUR,
735 NULL);
736 gtk_tree_view_append_column(treeview, column);
737 gtk_tree_view_column_set_sort_column_id(column, COL_OWNER);
739 /* Group */
740 cell = gtk_cell_renderer_text_new();
741 column = gtk_tree_view_column_new_with_attributes(_("Group"), cell,
742 "text", COL_GROUP,
743 "foreground-gdk", COL_COLOUR,
744 "background-gdk", COL_BG_COLOUR,
745 NULL);
746 gtk_tree_view_append_column(treeview, column);
747 gtk_tree_view_column_set_sort_column_id(column, COL_GROUP);
749 /* Size */
750 cell = gtk_cell_renderer_text_new();
751 column = gtk_tree_view_column_new_with_attributes(_("Size"), cell,
752 "text", COL_SIZE,
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_SIZE);
759 /* MTime */
760 cell = gtk_cell_renderer_text_new();
761 column = gtk_tree_view_column_new_with_attributes(_("M-Time"), cell,
762 "text", COL_MTIME,
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_MTIME);
769 gtk_widget_set_size_request(GTK_WIDGET(treeview), -1, 50);
772 /* Create the handers for the View interface */
773 static void view_details_iface_init(gpointer giface, gpointer iface_data)
775 ViewIfaceClass *iface = giface;
777 g_assert(G_TYPE_FROM_INTERFACE(iface) == VIEW_TYPE_IFACE);
779 /* override stuff */
780 iface->sort = view_details_sort;
781 iface->style_changed = view_details_style_changed;
782 iface->autoselect = view_details_autoselect;
783 iface->add_items = view_details_add_items;
784 iface->update_items = view_details_update_items;
785 iface->delete_if = view_details_delete_if;
786 iface->clear = view_details_clear;
787 iface->select_all = view_details_select_all;
788 iface->clear_selection = view_details_clear_selection;
789 iface->count_items = view_details_count_items;
790 iface->count_selected = view_details_count_selected;
791 iface->show_cursor = view_details_show_cursor;
792 iface->get_iter = view_details_get_iter;
793 iface->get_iter_at_point = view_details_get_iter_at_point;
794 iface->cursor_to_iter = view_details_cursor_to_iter;
795 iface->set_selected = view_details_set_selected;
796 iface->get_selected = view_details_get_selected;
797 iface->set_frozen = view_details_set_frozen;
798 iface->select_only = view_details_select_only;
799 iface->wink_item = view_details_wink_item;
800 iface->autosize = view_details_autosize;
801 iface->cursor_visible = view_details_cursor_visible;
802 iface->set_base = view_details_set_base;
803 iface->start_lasso_box = view_details_start_lasso_box;
804 iface->extend_tip = view_details_extend_tip;
807 /* Implementations of the View interface. See view_iface.c for comments. */
809 static void view_details_style_changed(ViewIface *view, int flags)
813 static gint wrap_sort(gconstpointer a, gconstpointer b,
814 ViewDetails *view_details)
816 ViewItem *ia = *(ViewItem **) a;
817 ViewItem *ib = *(ViewItem **) b;
819 if (view_details->order == GTK_SORT_ASCENDING)
820 return view_details->sort_fn(ia->item, ib->item);
821 else
822 return -view_details->sort_fn(ia->item, ib->item);
825 static void view_details_sort(ViewIface *view)
827 ViewDetails *view_details = (ViewDetails *) view;
828 ViewItem **items = (ViewItem **) view_details->items->pdata;
829 GArray *new_order;
830 gint i, len = view_details->items->len;
831 GtkTreePath *path;
833 g_return_if_fail(view_details->sort_fn != NULL);
835 if (!len)
836 return;
838 for (i = len - 1; i >= 0; i--)
839 items[i]->old_pos = i;
841 g_ptr_array_sort_with_data(view_details->items,
842 (GCompareDataFunc) wrap_sort,
843 view_details);
845 new_order = g_array_sized_new(FALSE, FALSE, sizeof(gint), len);
846 g_array_set_size(new_order, len);
848 for (i = len - 1; i >= 0; i--)
849 g_array_insert_val(new_order, items[i]->old_pos, i);
851 path = gtk_tree_path_new();
852 gtk_tree_model_rows_reordered((GtkTreeModel *) view,
853 path, NULL,
854 (gint *) new_order->data);
855 gtk_tree_path_free(path);
856 g_array_free(new_order, TRUE);
859 static gboolean view_details_autoselect(ViewIface *view, const gchar *leaf)
861 return FALSE;
864 static void view_details_add_items(ViewIface *view, GPtrArray *new_items)
866 ViewDetails *view_details = (ViewDetails *) view;
867 FilerWindow *filer_window = view_details->filer_window;
868 gboolean show_hidden = filer_window->show_hidden;
869 GPtrArray *items = view_details->items;
870 GtkTreeIter iter;
871 int i;
872 GtkTreePath *path;
873 GtkTreeModel *model = (GtkTreeModel *) view;
875 iter.user_data = GINT_TO_POINTER(items->len);
876 path = details_get_path(model, &iter);
878 for (i = 0; i < new_items->len; i++)
880 DirItem *item = (DirItem *) new_items->pdata[i];
881 char *leafname = item->leafname;
882 ViewItem *vitem;
884 if (leafname[0] == '.')
886 if (!show_hidden)
887 continue;
889 if (leafname[1] == '\0')
890 continue; /* Never show '.' */
892 if (leafname[1] == '.' &&
893 leafname[2] == '\0')
894 continue; /* Never show '..' */
897 vitem = g_new(ViewItem, 1);
898 vitem->item = item;
899 vitem->image = NULL;
900 vitem->selected = FALSE;
902 g_ptr_array_add(items, vitem);
904 iter.user_data = GINT_TO_POINTER(items->len - 1);
905 gtk_tree_model_row_inserted(model, path, &iter);
906 gtk_tree_path_next(path);
909 gtk_tree_path_free(path);
911 view_details_sort(view);
914 /* Find an item in the sorted array.
915 * Returns the item number, or -1 if not found.
917 static int details_find_item(ViewDetails *view_details, DirItem *item)
919 ViewItem **items, tmp, *tmpp;
920 int lower, upper;
921 int (*compar)(const void *, const void *);
923 g_return_val_if_fail(view_details != NULL, -1);
924 g_return_val_if_fail(item != NULL, -1);
926 tmp.item = item;
927 tmpp = &tmp;
929 items = (ViewItem **) view_details->items->pdata;
930 compar = view_details->sort_fn;
932 g_return_val_if_fail(compar != NULL, -1);
934 /* If item is here, then: lower <= i < upper */
935 lower = 0;
936 upper = view_details->items->len;
938 while (lower < upper)
940 int i, cmp;
942 i = (lower + upper) >> 1;
944 cmp = wrap_sort(&items[i], &tmpp, view_details);
945 if (cmp == 0)
946 return i;
948 if (cmp > 0)
949 upper = i;
950 else
951 lower = i + 1;
954 return -1;
957 static void view_details_update_items(ViewIface *view, GPtrArray *items)
959 ViewDetails *view_details = (ViewDetails *) view;
960 FilerWindow *filer_window = view_details->filer_window;
961 int i;
962 GtkTreeModel *model = (GtkTreeModel *) view_details;
964 g_return_if_fail(items->len > 0);
966 /* The item data has already been modified, so this gives the
967 * final sort order...
969 view_details_sort(view);
971 for (i = 0; i < items->len; i++)
973 DirItem *item = (DirItem *) items->pdata[i];
974 const gchar *leafname = item->leafname;
975 int j;
977 if (leafname[0] == '.' && filer_window->show_hidden == FALSE)
978 continue;
980 j = details_find_item(view_details, item);
982 if (j < 0)
983 g_warning("Failed to find '%s'\n", leafname);
984 else
986 GtkTreePath *path;
987 GtkTreeIter iter;
988 path = gtk_tree_path_new();
989 gtk_tree_path_append_index(path, j);
990 iter.user_data = GINT_TO_POINTER(j);
991 gtk_tree_model_row_changed(model, path, &iter);
996 static void view_details_delete_if(ViewIface *view,
997 gboolean (*test)(gpointer item, gpointer data),
998 gpointer data)
1000 GtkTreePath *path;
1001 ViewDetails *view_details = (ViewDetails *) view;
1002 int i = 0;
1003 GPtrArray *items = view_details->items;
1004 GtkTreeModel *model = (GtkTreeModel *) view;
1006 path = gtk_tree_path_new();
1008 gtk_tree_path_append_index(path, i);
1010 while (i < items->len)
1012 ViewItem *item = items->pdata[i];
1014 if (test(item->item, data))
1016 g_free(items->pdata[i]);
1017 g_ptr_array_remove_index(items, i);
1018 gtk_tree_model_row_deleted(model, path);
1020 else
1022 i++;
1023 gtk_tree_path_next(path);
1027 gtk_tree_path_free(path);
1030 static void view_details_clear(ViewIface *view)
1032 GtkTreePath *path;
1033 GPtrArray *items = ((ViewDetails *) view)->items;
1034 GtkTreeModel *model = (GtkTreeModel *) view;
1036 path = gtk_tree_path_new();
1037 gtk_tree_path_append_index(path, items->len);
1039 while (gtk_tree_path_prev(path))
1040 gtk_tree_model_row_deleted(model, path);
1042 g_ptr_array_set_size(items, 0);
1043 gtk_tree_path_free(path);
1046 static void view_details_select_all(ViewIface *view)
1048 int i;
1049 int n = ((ViewDetails *) view)->items->len;
1051 for (i = 0; i < n; i++)
1053 ViewIter iter;
1054 iter.i = i;
1055 view_details_set_selected(view, &iter, TRUE);
1059 static void view_details_clear_selection(ViewIface *view)
1061 int i;
1062 int n = ((ViewDetails *) view)->items->len;
1064 for (i = 0; i < n; i++)
1066 ViewIter iter;
1067 iter.i = i;
1068 view_details_set_selected(view, &iter, FALSE);
1072 static int view_details_count_items(ViewIface *view)
1074 ViewDetails *view_details = (ViewDetails *) view;
1076 return view_details->items->len;
1079 static int view_details_count_selected(ViewIface *view)
1081 ViewDetails *view_details = (ViewDetails *) view;
1082 int n = view_details->items->len;
1083 ViewItem **items = (ViewItem **) view_details->items->pdata;
1084 int count = 0;
1085 int i;
1087 for (i = 0; i < n; i++)
1088 if (items[i]->selected)
1089 count++;
1091 return count;
1094 static void view_details_show_cursor(ViewIface *view)
1098 static void view_details_get_iter(ViewIface *view,
1099 ViewIter *iter, IterFlags flags)
1101 make_iter((ViewDetails *) view, iter, flags);
1104 static void view_details_get_iter_at_point(ViewIface *view, ViewIter *iter,
1105 int x, int y)
1107 ViewDetails *view_details = (ViewDetails *) view;
1108 GtkTreeModel *model;
1109 GtkTreeView *tree = (GtkTreeView *) view;
1110 GtkTreePath *path = NULL;
1111 int i = -1;
1113 model = gtk_tree_view_get_model(tree);
1115 if (gtk_tree_view_get_path_at_pos(tree, x, y, &path, NULL, NULL, NULL))
1117 g_return_if_fail(path != NULL);
1119 i = gtk_tree_path_get_indices(path)[0];
1120 gtk_tree_path_free(path);
1123 make_item_iter(view_details, iter, i);
1126 static void view_details_cursor_to_iter(ViewIface *view, ViewIter *iter)
1128 GtkTreePath *path;
1130 if (!iter)
1132 /* XXX: How do we get rid of the cursor? */
1133 g_print("FIXME: Remove cursor\n");
1134 return;
1137 path = gtk_tree_path_new();
1139 gtk_tree_path_append_index(path, iter->i);
1140 gtk_tree_view_set_cursor((GtkTreeView *) view, path, NULL, FALSE);
1141 gtk_tree_path_free(path);
1144 static void set_selected(ViewDetails *view_details, int i, gboolean selected)
1146 GtkTreeModel *model = (GtkTreeModel *) view_details;
1147 GtkTreeIter t_iter;
1148 GtkTreePath *path;
1149 GPtrArray *items = view_details->items;
1150 ViewItem *view_item;
1152 g_return_if_fail(i >= 0 && i < items->len);
1153 view_item = (ViewItem *) items->pdata[i];
1155 if (view_item->selected == selected)
1156 return;
1158 view_item->selected = selected;
1160 path = gtk_tree_path_new();
1161 gtk_tree_path_append_index(path, i);
1162 t_iter.user_data = GINT_TO_POINTER(i);
1163 gtk_tree_model_row_changed(model, path, &t_iter);
1164 gtk_tree_path_free(path);
1167 static void view_details_set_selected(ViewIface *view,
1168 ViewIter *iter,
1169 gboolean selected)
1171 set_selected((ViewDetails *) view, iter->i, selected);
1174 static gboolean get_selected(ViewDetails *view_details, int i)
1176 GPtrArray *items = view_details->items;
1178 g_return_val_if_fail(i >= 0 && i < items->len, FALSE);
1180 return ((ViewItem *) items->pdata[i])->selected;
1183 static gboolean view_details_get_selected(ViewIface *view, ViewIter *iter)
1185 return get_selected((ViewDetails *) view, iter->i);
1188 static void view_details_select_only(ViewIface *view, ViewIter *iter)
1190 ViewDetails *view_details = (ViewDetails *) view;
1191 int i = iter->i;
1192 int n = view_details->items->len;
1194 set_selected(view_details, i, TRUE);
1196 while (n > 0)
1198 n--;
1200 if (n != i)
1201 set_selected(view_details, n, FALSE);
1205 static void view_details_set_frozen(ViewIface *view, gboolean frozen)
1209 static void view_details_wink_item(ViewIface *view, ViewIter *iter)
1213 static void view_details_autosize(ViewIface *view)
1217 static gboolean view_details_cursor_visible(ViewIface *view)
1219 return FALSE;
1222 static void view_details_set_base(ViewIface *view, ViewIter *iter)
1224 ViewDetails *view_details = (ViewDetails *) view;
1226 view_details->cursor_base = iter->i;
1229 static void view_details_start_lasso_box(ViewIface *view, GdkEventButton *event)
1231 g_print("TODO: lasso drag\n");
1234 static void view_details_extend_tip(ViewIface *view,
1235 ViewIter *iter, GString *tip)
1239 static DirItem *iter_init(ViewIter *iter)
1241 ViewDetails *view_details = (ViewDetails *) iter->view;
1242 int i = -1;
1243 int n = view_details->items->len;
1244 int flags = iter->flags;
1246 iter->peek = iter_peek;
1248 if (iter->n_remaining == 0)
1249 return NULL;
1251 if (flags & VIEW_ITER_FROM_CURSOR)
1253 GtkTreePath *path;
1254 gtk_tree_view_get_cursor((GtkTreeView *) view_details,
1255 &path, NULL);
1256 if (!path)
1257 return NULL; /* No cursor */
1258 i = gtk_tree_path_get_indices(path)[0];
1259 gtk_tree_path_free(path);
1261 else if (flags & VIEW_ITER_FROM_BASE)
1262 i = view_details->cursor_base;
1264 if (i < 0 || i >= n)
1266 /* Either a normal iteration, or an iteration from an
1267 * invalid starting point.
1269 if (flags & VIEW_ITER_BACKWARDS)
1270 i = n - 1;
1271 else
1272 i = 0;
1275 if (i < 0 || i >= n)
1276 return NULL; /* No items at all! */
1278 iter->next = flags & VIEW_ITER_BACKWARDS ? iter_prev : iter_next;
1279 iter->n_remaining--;
1280 iter->i = i;
1282 if (flags & VIEW_ITER_SELECTED && !is_selected(view_details, i))
1283 return iter->next(iter);
1284 return iter->peek(iter);
1287 static DirItem *iter_prev(ViewIter *iter)
1289 ViewDetails *view_details = (ViewDetails *) iter->view;
1290 int n = view_details->items->len;
1291 int i = iter->i;
1293 g_return_val_if_fail(iter->n_remaining >= 0, NULL);
1295 /* i is the last item returned (always valid) */
1297 g_return_val_if_fail(i >= 0 && i < n, NULL);
1299 while (iter->n_remaining)
1301 i--;
1302 iter->n_remaining--;
1304 if (i == -1)
1305 i = n - 1;
1307 g_return_val_if_fail(i >= 0 && i < n, NULL);
1309 if (iter->flags & VIEW_ITER_SELECTED &&
1310 !is_selected(view_details, i))
1311 continue;
1313 iter->i = i;
1314 return ((ViewItem *) view_details->items->pdata[i])->item;
1317 iter->i = -1;
1318 return NULL;
1321 static DirItem *iter_next(ViewIter *iter)
1323 ViewDetails *view_details = (ViewDetails *) iter->view;
1324 int n = view_details->items->len;
1325 int i = iter->i;
1327 g_return_val_if_fail(iter->n_remaining >= 0, NULL);
1329 /* i is the last item returned (always valid) */
1331 g_return_val_if_fail(i >= 0 && i < n, NULL);
1333 while (iter->n_remaining)
1335 i++;
1336 iter->n_remaining--;
1338 if (i == n)
1339 i = 0;
1341 g_return_val_if_fail(i >= 0 && i < n, NULL);
1343 if (iter->flags & VIEW_ITER_SELECTED &&
1344 !is_selected(view_details, i))
1345 continue;
1347 iter->i = i;
1348 return ((ViewItem *) view_details->items->pdata[i])->item;
1351 iter->i = -1;
1352 return NULL;
1355 static DirItem *iter_peek(ViewIter *iter)
1357 ViewDetails *view_details = (ViewDetails *) iter->view;
1358 int n = view_details->items->len;
1359 int i = iter->i;
1361 if (i == -1)
1362 return NULL;
1364 g_return_val_if_fail(i >= 0 && i < n, NULL);
1366 return ((ViewItem *) view_details->items->pdata[i])->item;
1369 /* Set the iterator to return 'i' on the next peek().
1370 * If i is -1, returns NULL on next peek().
1372 static void make_item_iter(ViewDetails *view_details, ViewIter *iter, int i)
1374 make_iter(view_details, iter, 0);
1376 g_return_if_fail(i >= -1 && i < (int) view_details->items->len);
1378 iter->i = i;
1379 iter->next = iter_next;
1380 iter->peek = iter_peek;
1381 iter->n_remaining = 0;
1384 static void make_iter(ViewDetails *view_details, ViewIter *iter,
1385 IterFlags flags)
1387 iter->view = (ViewIface *) view_details;
1388 iter->next = iter_init;
1389 iter->peek = NULL;
1390 iter->i = -1;
1392 iter->flags = flags;
1394 if (flags & VIEW_ITER_ONE_ONLY)
1396 iter->n_remaining = 1;
1397 iter->next(iter);
1399 else
1400 iter->n_remaining = view_details->items->len;