r2216: Cursor and selection work on view details.
[rox-filer.git] / ROX-Filer / src / view_details.c
blob859464cf49c17a96697cacf8b5583a10bb2806ec
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>
28 #include "global.h"
30 #include "view_iface.h"
31 #include "view_details.h"
32 #include "diritem.h"
33 #include "support.h"
34 #include "type.h"
35 #include "filer.h"
36 #include "display.h"
37 #include "pixmaps.h"
38 #include "dnd.h"
39 #include "bind.h"
40 #include "gui_support.h"
41 #include "menu.h"
43 /* These are the column numbers in the ListStore */
44 #define COL_LEAF 0
45 #define COL_TYPE 1
46 #define COL_PERM 2
47 #define COL_OWNER 3
48 #define COL_GROUP 4
49 #define COL_SIZE 5
50 #define COL_MTIME 6
51 #define COL_ITEM 7
52 #define COL_COLOUR 8
53 #define COL_ICON 9
54 #define N_COLUMNS 10
56 static gpointer parent_class = NULL;
58 struct _ViewDetailsClass {
59 GtkTreeViewClass parent;
62 typedef struct _ViewItem ViewItem;
64 struct _ViewItem {
65 DirItem *item;
66 GdkPixbuf *image;
67 int old_pos; /* Used while sorting */
70 typedef struct _ViewDetails ViewDetails;
72 struct _ViewDetails {
73 GtkTreeView treeview;
75 FilerWindow *filer_window; /* Used for styles, etc */
77 GPtrArray *items; /* ViewItem */
79 gint sort_column_id;
80 GtkSortType order;
81 int (*sort_fn)(const void *, const void *);
83 int cursor_base; /* Cursor when minibuffer opened */
86 /* Static prototypes */
87 static void view_details_finialize(GObject *object);
88 static void view_details_class_init(gpointer gclass, gpointer data);
89 static void view_details_init(GTypeInstance *object, gpointer gclass);
91 static void view_details_iface_init(gpointer giface, gpointer iface_data);
93 static void view_details_sort(ViewIface *view);
94 static void view_details_style_changed(ViewIface *view, int flags);
95 static gboolean view_details_autoselect(ViewIface *view, const gchar *leaf);
96 static void view_details_add_items(ViewIface *view, GPtrArray *items);
97 static void view_details_update_items(ViewIface *view, GPtrArray *items);
98 static void view_details_delete_if(ViewIface *view,
99 gboolean (*test)(gpointer item, gpointer data),
100 gpointer data);
101 static void view_details_clear(ViewIface *view);
102 static void view_details_select_all(ViewIface *view);
103 static void view_details_clear_selection(ViewIface *view);
104 static int view_details_count_items(ViewIface *view);
105 static int view_details_count_selected(ViewIface *view);
106 static void view_details_show_cursor(ViewIface *view);
107 static void view_details_get_iter(ViewIface *view,
108 ViewIter *iter, IterFlags flags);
109 static void view_details_cursor_to_iter(ViewIface *view, ViewIter *iter);
110 static void view_details_set_selected(ViewIface *view,
111 ViewIter *iter,
112 gboolean selected);
113 static gboolean view_details_get_selected(ViewIface *view, ViewIter *iter);
114 static void view_details_select_only(ViewIface *view, ViewIter *iter);
115 static void view_details_set_frozen(ViewIface *view, gboolean frozen);
116 static void view_details_wink_item(ViewIface *view, ViewIter *iter);
117 static void view_details_autosize(ViewIface *view);
118 static gboolean view_details_cursor_visible(ViewIface *view);
119 static void view_details_set_base(ViewIface *view, ViewIter *iter);
120 static DirItem *iter_peek(ViewIter *iter);
121 static DirItem *iter_prev(ViewIter *iter);
122 static DirItem *iter_next(ViewIter *iter);
123 static void make_iter(ViewDetails *view_details, ViewIter *iter,
124 IterFlags flags);
125 static void make_item_iter(ViewDetails *view_details, ViewIter *iter, int i);
126 static void view_details_tree_model_init(GtkTreeModelIface *iface);
127 static gboolean details_get_sort_column_id(GtkTreeSortable *sortable,
128 gint *sort_column_id,
129 GtkSortType *order);
130 static void details_set_sort_column_id(GtkTreeSortable *sortable,
131 gint sort_column_id,
132 GtkSortType order);
133 static void details_set_sort_func(GtkTreeSortable *sortable,
134 gint sort_column_id,
135 GtkTreeIterCompareFunc func,
136 gpointer data,
137 GtkDestroyNotify destroy);
138 static void details_set_default_sort_func(GtkTreeSortable *sortable,
139 GtkTreeIterCompareFunc func,
140 gpointer data,
141 GtkDestroyNotify destroy);
142 static gboolean details_has_default_sort_func(GtkTreeSortable *sortable);
143 static void view_details_sortable_init(GtkTreeSortableIface *iface);
146 /****************************************************************
147 * EXTERNAL INTERFACE *
148 ****************************************************************/
150 GtkWidget *view_details_new(FilerWindow *filer_window)
152 ViewDetails *view_details;
154 view_details = g_object_new(view_details_get_type(), NULL);
155 view_details->filer_window = filer_window;
157 gtk_range_set_adjustment(GTK_RANGE(filer_window->scrollbar),
158 gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(view_details)));
160 return GTK_WIDGET(view_details);
163 GType view_details_get_type(void)
165 static GType type = 0;
167 if (!type)
169 static const GTypeInfo info =
171 sizeof (ViewDetailsClass),
172 NULL, /* base_init */
173 NULL, /* base_finalise */
174 view_details_class_init,
175 NULL, /* class_finalise */
176 NULL, /* class_data */
177 sizeof(ViewDetails),
178 0, /* n_preallocs */
179 view_details_init
181 static const GInterfaceInfo view_iface_info = {
182 view_details_iface_init,
183 NULL, NULL
185 static const GInterfaceInfo tree_model_info = {
186 (GInterfaceInitFunc) view_details_tree_model_init,
187 NULL, NULL
189 static const GInterfaceInfo sortable_info = {
190 (GInterfaceInitFunc) view_details_sortable_init,
191 NULL, NULL
195 type = g_type_register_static(gtk_tree_view_get_type(),
196 "ViewDetails", &info, 0);
198 g_type_add_interface_static(type, VIEW_TYPE_IFACE,
199 &view_iface_info);
200 g_type_add_interface_static(type, GTK_TYPE_TREE_MODEL,
201 &tree_model_info);
202 g_type_add_interface_static(type, GTK_TYPE_TREE_SORTABLE,
203 &sortable_info);
206 return type;
209 /****************************************************************
210 * INTERNAL FUNCTIONS *
211 ****************************************************************/
213 /* Fulfill the GtkTreeModel requirements */
214 static guint details_get_flags(GtkTreeModel *tree_model)
216 return GTK_TREE_MODEL_LIST_ONLY;
219 static gint details_get_n_columns(GtkTreeModel *tree_model)
221 return N_COLUMNS;
224 static GType details_get_column_type(GtkTreeModel *tree_model, gint index)
226 g_return_val_if_fail(index < N_COLUMNS && index >= 0, G_TYPE_INVALID);
228 if (index == COL_COLOUR)
229 return GDK_TYPE_COLOR;
230 else if (index == COL_ITEM)
231 return G_TYPE_POINTER;
232 else if (index == COL_ICON)
233 return GDK_TYPE_PIXBUF;
234 return G_TYPE_STRING;
237 static gboolean details_get_iter(GtkTreeModel *tree_model,
238 GtkTreeIter *iter,
239 GtkTreePath *path)
241 ViewDetails *view_details = (ViewDetails *) tree_model;
242 gint i;
244 g_return_val_if_fail(gtk_tree_path_get_depth (path) > 0, FALSE);
246 i = gtk_tree_path_get_indices(path)[0];
248 if (i >= view_details->items->len)
249 return FALSE;
251 iter->user_data = GINT_TO_POINTER(i);
253 return TRUE;
256 static GtkTreePath *details_get_path(GtkTreeModel *tree_model,
257 GtkTreeIter *iter)
259 GtkTreePath *retval;
261 retval = gtk_tree_path_new();
262 gtk_tree_path_append_index(retval, GPOINTER_TO_INT(iter->user_data));
264 return retval;
267 static void details_get_value(GtkTreeModel *tree_model,
268 GtkTreeIter *iter,
269 gint column,
270 GValue *value)
272 ViewDetails *view_details = (ViewDetails *) tree_model;
273 gint i;
274 GPtrArray *items = view_details->items;
275 DirItem *item;
276 mode_t m;
278 g_return_if_fail(column >= 0 && column < N_COLUMNS);
280 i = GPOINTER_TO_INT(iter->user_data);
281 g_return_if_fail(i >= 0 && i < items->len);
282 item = ((ViewItem *) items->pdata[i])->item;
284 /* g_print("[ get %d ]\n", column); */
286 if (column == COL_LEAF)
288 g_value_init(value, G_TYPE_STRING);
289 g_value_set_string(value, item->leafname);
290 return;
292 else if (column == COL_ITEM)
294 g_value_init(value, G_TYPE_POINTER);
295 g_value_set_pointer(value, item);
296 return;
299 if (item->base_type == TYPE_UNKNOWN)
301 GType type;
302 type = details_get_column_type(tree_model, column);
303 g_value_init(value, type);
304 if (type == G_TYPE_STRING)
305 g_value_set_string(value, "");
306 else if (type == GDK_TYPE_COLOR)
307 g_value_set_boxed(value, NULL);
308 else
309 g_value_set_object(value, NULL);
311 return;
313 m = item->mode;
315 switch (column)
317 case COL_LEAF:
318 g_value_init(value, G_TYPE_STRING);
319 g_value_set_string(value, item->leafname);
320 break;
321 case COL_ICON:
322 g_value_init(value, GDK_TYPE_PIXBUF);
323 if (!item->image->sm_pixbuf)
324 pixmap_make_small(item->image);
325 g_value_set_object(value, item->image->sm_pixbuf);
326 break;
327 case COL_COLOUR:
328 g_value_init(value, GDK_TYPE_COLOR);
329 g_value_set_boxed(value, type_get_colour(item, NULL));
330 break;
331 case COL_OWNER:
332 g_value_init(value, G_TYPE_STRING);
333 g_value_set_string(value, user_name(item->uid));
334 break;
335 case COL_GROUP:
336 g_value_init(value, G_TYPE_STRING);
337 g_value_set_string(value, group_name(item->gid));
338 break;
339 case COL_MTIME:
341 gchar *time;
342 time = pretty_time(&item->mtime);
343 g_value_init(value, G_TYPE_STRING);
344 g_value_set_string(value, time);
345 g_free(time);
346 break;
348 case COL_PERM:
349 g_value_init(value, G_TYPE_STRING);
350 g_value_set_string(value, pretty_permissions(m));
351 break;
352 case COL_SIZE:
353 g_value_init(value, G_TYPE_STRING);
354 if (item->base_type != TYPE_DIRECTORY)
355 g_value_set_string(value,
356 format_size(item->size));
357 break;
358 case COL_TYPE:
359 g_value_init(value, G_TYPE_STRING);
360 g_value_set_string(value,
361 item->flags & ITEM_FLAG_APPDIR? "App" :
362 S_ISDIR(m) ? "Dir" :
363 S_ISCHR(m) ? "Char" :
364 S_ISBLK(m) ? "Blck" :
365 S_ISLNK(m) ? "Link" :
366 S_ISSOCK(m) ? "Sock" :
367 S_ISFIFO(m) ? "Pipe" :
368 S_ISDOOR(m) ? "Door" :
369 "File");
370 break;
371 default:
372 g_value_init(value, G_TYPE_STRING);
373 g_value_set_string(value, "Hello");
374 break;
378 static gboolean details_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter)
380 ViewDetails *view_details = (ViewDetails *) tree_model;
381 int i;
383 i = GPOINTER_TO_INT(iter->user_data) + 1;
384 iter->user_data = GINT_TO_POINTER(i);
386 return i < view_details->items->len;
389 static gboolean details_iter_children(GtkTreeModel *tree_model,
390 GtkTreeIter *iter,
391 GtkTreeIter *parent)
393 ViewDetails *view_details = (ViewDetails *) tree_model;
395 /* this is a list, nodes have no children */
396 if (parent)
397 return FALSE;
399 /* but if parent == NULL we return the list itself as children of the
400 * "root"
403 if (view_details->items->len)
405 iter->user_data = GINT_TO_POINTER(0);
406 return TRUE;
408 else
409 return FALSE;
412 static gboolean details_iter_has_child(GtkTreeModel *tree_model,
413 GtkTreeIter *iter)
415 return FALSE;
418 static gint details_iter_n_children(GtkTreeModel *tree_model, GtkTreeIter *iter)
420 ViewDetails *view_details = (ViewDetails *) tree_model;
422 if (iter == NULL)
423 return view_details->items->len;
425 return 0;
428 static gboolean details_iter_nth_child(GtkTreeModel *tree_model,
429 GtkTreeIter *iter,
430 GtkTreeIter *parent,
431 gint n)
433 ViewDetails *view_details = (ViewDetails *) tree_model;
435 if (parent)
436 return FALSE;
438 if (n >= 0 && n < view_details->items->len)
440 iter->user_data = GINT_TO_POINTER(n);
441 return TRUE;
443 else
444 return FALSE;
447 static gboolean details_iter_parent(GtkTreeModel *tree_model,
448 GtkTreeIter *iter,
449 GtkTreeIter *child)
451 return FALSE;
454 /* A ViewDetails is both a GtkTreeView and a GtkTreeModel.
455 * The following functions implement the model interface...
458 static void view_details_tree_model_init(GtkTreeModelIface *iface)
460 iface->get_flags = details_get_flags;
461 iface->get_n_columns = details_get_n_columns;
462 iface->get_column_type = details_get_column_type;
463 iface->get_iter = details_get_iter;
464 iface->get_path = details_get_path;
465 iface->get_value = details_get_value;
466 iface->iter_next = details_iter_next;
467 iface->iter_children = details_iter_children;
468 iface->iter_has_child = details_iter_has_child;
469 iface->iter_n_children = details_iter_n_children;
470 iface->iter_nth_child = details_iter_nth_child;
471 iface->iter_parent = details_iter_parent;
474 static void view_details_sortable_init(GtkTreeSortableIface *iface)
476 iface->get_sort_column_id = details_get_sort_column_id;
477 iface->set_sort_column_id = details_set_sort_column_id;
478 iface->set_sort_func = details_set_sort_func;
479 iface->set_default_sort_func = details_set_default_sort_func;
480 iface->has_default_sort_func = details_has_default_sort_func;
483 static gboolean details_get_sort_column_id(GtkTreeSortable *sortable,
484 gint *sort_column_id,
485 GtkSortType *order)
487 ViewDetails *view_details = (ViewDetails *) sortable;
489 if (view_details->sort_column_id == -1)
490 return FALSE;
492 if (sort_column_id)
493 *sort_column_id = view_details->sort_column_id;
494 if (order)
495 *order = view_details->order;
496 return TRUE;
499 static void details_set_sort_column_id(GtkTreeSortable *sortable,
500 gint sort_column_id,
501 GtkSortType order)
503 ViewDetails *view_details = (ViewDetails *) sortable;
505 if (view_details->sort_column_id == sort_column_id &&
506 view_details->order == order)
507 return;
509 view_details->sort_column_id = sort_column_id;
510 view_details->order = order;
512 switch (sort_column_id)
514 case COL_LEAF:
515 view_details->sort_fn = sort_by_name;
516 break;
517 case COL_SIZE:
518 view_details->sort_fn = sort_by_size;
519 break;
520 case COL_MTIME:
521 view_details->sort_fn = sort_by_date;
522 break;
523 case COL_TYPE:
524 view_details->sort_fn = sort_by_type;
525 break;
526 case COL_OWNER:
527 view_details->sort_fn = sort_by_owner;
528 break;
529 case COL_GROUP:
530 view_details->sort_fn = sort_by_group;
531 break;
532 default:
533 g_assert_not_reached();
536 view_details_sort((ViewIface *) view_details);
538 gtk_tree_sortable_sort_column_changed(sortable);
541 static void details_set_sort_func(GtkTreeSortable *sortable,
542 gint sort_column_id,
543 GtkTreeIterCompareFunc func,
544 gpointer data,
545 GtkDestroyNotify destroy)
547 g_assert_not_reached();
550 static void details_set_default_sort_func(GtkTreeSortable *sortable,
551 GtkTreeIterCompareFunc func,
552 gpointer data,
553 GtkDestroyNotify destroy)
555 g_assert_not_reached();
558 static gboolean details_has_default_sort_func(GtkTreeSortable *sortable)
560 return FALSE;
564 /* End of model implementation */
566 static gboolean is_selected(ViewDetails *view_details, int i)
568 GtkTreeIter iter;
569 GtkTreeSelection *selection;
571 selection = gtk_tree_view_get_selection((GtkTreeView *) view_details);
573 iter.user_data = GINT_TO_POINTER(i);
574 return gtk_tree_selection_iter_is_selected(selection, &iter);
577 static void toggle_selected(GtkTreeSelection *selection, GtkTreeIter *iter)
579 if (gtk_tree_selection_iter_is_selected(selection, iter))
580 gtk_tree_selection_unselect_iter(selection, iter);
581 else
582 gtk_tree_selection_select_iter(selection, iter);
585 static void perform_action(ViewDetails *view_details, GdkEventButton *event)
587 BindAction action;
588 FilerWindow *filer_window = view_details->filer_window;
589 DirItem *item = NULL;
590 GtkTreeView *tree = (GtkTreeView *) view_details;
591 GtkTreePath *path = NULL;
592 GtkTreeIter iter;
593 GtkTreeModel *model;
594 GtkTreeSelection *selection;
595 int i = -1;
596 /* OpenFlags flags = 0; */
598 model = gtk_tree_view_get_model(tree);
599 selection = gtk_tree_view_get_selection(tree);
601 if (gtk_tree_view_get_path_at_pos(tree, event->x, event->y,
602 &path, NULL, NULL, NULL))
604 g_return_if_fail(path != NULL);
606 i = gtk_tree_path_get_indices(path)[0];
607 gtk_tree_path_free(path);
610 if (i != -1)
611 item = ((ViewItem *) view_details->items->pdata[i])->item;
613 g_print("[ item %d clicked ]\n", i);
615 /* TODO: Cancel slow DnD */
616 /* TODO: Target callbacks */
618 action = bind_lookup_bev(
619 item ? BIND_DIRECTORY_ICON : BIND_DIRECTORY,
620 event);
622 switch (action)
624 case ACT_CLEAR_SELECTION:
625 gtk_tree_selection_unselect_all(selection);
626 break;
627 case ACT_TOGGLE_SELECTED:
628 toggle_selected(selection, &iter);
629 break;
630 #if 0
631 case ACT_SELECT_EXCL:
632 collection_clear_except(collection, item);
633 break;
634 case ACT_EDIT_ITEM:
635 flags |= OPEN_SHIFT;
636 /* (no break) */
637 case ACT_OPEN_ITEM:
639 ViewIter viter;
641 make_item_iter(view_details, &viter, &iter);
643 if (event->button != 1 || event->state & GDK_MOD1_MASK)
644 flags |= OPEN_CLOSE_WINDOW;
645 else
646 flags |= OPEN_SAME_WINDOW;
647 if (o_new_button_1.int_value)
648 flags ^= OPEN_SAME_WINDOW;
649 /* TODO */
650 if (event->type == GDK_2BUTTON_PRESS)
651 collection_unselect_item(collection, item);
652 dnd_motion_ungrab();
654 filer_openitem(filer_window, &viter, flags);
655 break;
657 #endif
658 case ACT_POPUP_MENU:
660 ViewIter viter;
662 dnd_motion_ungrab();
663 tooltip_show(NULL);
665 make_item_iter(view_details, &viter, i);
666 show_filer_menu(filer_window,
667 (GdkEvent *) event, &viter);
668 break;
670 #if 0
671 case ACT_PRIME_AND_SELECT:
672 if (!selected)
673 collection_clear_except(collection, item);
674 dnd_motion_start(MOTION_READY_FOR_DND);
675 break;
676 case ACT_PRIME_AND_TOGGLE:
677 collection_toggle_item(collection, item);
678 dnd_motion_start(MOTION_READY_FOR_DND);
679 break;
680 case ACT_PRIME_FOR_DND:
681 dnd_motion_start(MOTION_READY_FOR_DND);
682 break;
683 case ACT_IGNORE:
684 if (press && event->button < 4)
686 if (item)
687 collection_wink_item(collection, item);
688 dnd_motion_start(MOTION_NONE);
690 break;
691 case ACT_LASSO_CLEAR:
692 collection_clear_selection(collection);
693 /* (no break) */
694 case ACT_LASSO_MODIFY:
695 collection_lasso_box(collection, event->x, event->y);
696 break;
697 case ACT_RESIZE:
698 filer_window_autosize(filer_window);
699 break;
700 #endif
701 default:
702 g_warning("Unsupported action : %d\n", action);
703 break;
707 static gboolean view_details_button_press(GtkWidget *widget,
708 GdkEventButton *bev)
710 if (dnd_motion_press(widget, bev))
711 perform_action((ViewDetails *) widget, bev);
713 return TRUE;
716 static gboolean view_details_button_release(GtkWidget *widget,
717 GdkEventButton *bev)
719 if (!dnd_motion_release(bev))
720 perform_action((ViewDetails *) widget, bev);
722 return TRUE;
725 static void view_details_destroy(GtkObject *view_details)
727 VIEW_DETAILS(view_details)->filer_window = NULL;
730 static void view_details_finialize(GObject *object)
732 ViewDetails *view_details = (ViewDetails *) object;
734 g_ptr_array_free(view_details->items, TRUE);
735 view_details->items = NULL;
737 G_OBJECT_CLASS(parent_class)->finalize(object);
740 static void view_details_class_init(gpointer gclass, gpointer data)
742 GObjectClass *object = (GObjectClass *) gclass;
743 GtkWidgetClass *widget = (GtkWidgetClass *) gclass;
745 parent_class = g_type_class_peek_parent(gclass);
747 object->finalize = view_details_finialize;
748 GTK_OBJECT_CLASS(object)->destroy = view_details_destroy;
750 widget->button_press_event = view_details_button_press;
751 widget->button_release_event = view_details_button_release;
754 static void view_details_init(GTypeInstance *object, gpointer gclass)
756 GtkTreeView *treeview = (GtkTreeView *) object;
757 GtkTreeViewColumn *column;
758 GtkCellRenderer *cell;
759 GtkTreeSortable *sortable_list;
760 ViewDetails *view_details = (ViewDetails *) object;
762 view_details->items = g_ptr_array_new();
763 view_details->cursor_base = -1;
765 /* Sorting */
766 view_details->sort_column_id = -1;
767 view_details->sort_fn = NULL;
768 sortable_list = GTK_TREE_SORTABLE(object);
769 gtk_tree_sortable_set_sort_column_id(sortable_list, COL_LEAF,
770 GTK_SORT_ASCENDING);
772 gtk_tree_view_set_model(treeview, GTK_TREE_MODEL(view_details));
774 /* Icon */
775 cell = gtk_cell_renderer_pixbuf_new();
776 column = gtk_tree_view_column_new_with_attributes(NULL, cell,
777 "pixbuf", COL_ICON, NULL);
778 gtk_tree_view_append_column(treeview, column);
780 /* The rest are text... */
781 cell = gtk_cell_renderer_text_new();
783 /* Name */
784 column = gtk_tree_view_column_new_with_attributes(_("Name"), cell,
785 "text", COL_LEAF,
786 "foreground-gdk", COL_COLOUR, NULL);
787 gtk_tree_view_append_column(treeview, column);
788 gtk_tree_view_column_set_sort_column_id(column, COL_LEAF);
790 /* Type */
791 column = gtk_tree_view_column_new_with_attributes(_("Type"), cell,
792 "text", COL_TYPE, NULL);
793 gtk_tree_view_append_column(treeview, column);
794 gtk_tree_view_column_set_sort_column_id(column, COL_TYPE);
796 /* Perm */
797 column = gtk_tree_view_column_new_with_attributes(_("Permissions"),
798 cell, "text", COL_PERM, NULL);
799 gtk_tree_view_append_column(treeview, column);
801 /* Owner */
802 column = gtk_tree_view_column_new_with_attributes(_("Owner"), cell,
803 "text", COL_OWNER, NULL);
804 gtk_tree_view_append_column(treeview, column);
805 gtk_tree_view_column_set_sort_column_id(column, COL_OWNER);
807 /* Group */
808 column = gtk_tree_view_column_new_with_attributes(_("Group"), cell,
809 "text", COL_GROUP, NULL);
810 gtk_tree_view_append_column(treeview, column);
811 gtk_tree_view_column_set_sort_column_id(column, COL_GROUP);
813 /* Size */
814 column = gtk_tree_view_column_new_with_attributes(_("Size"), cell,
815 "text", COL_SIZE, NULL);
816 gtk_tree_view_append_column(treeview, column);
817 gtk_tree_view_column_set_sort_column_id(column, COL_SIZE);
819 /* MTime */
820 column = gtk_tree_view_column_new_with_attributes(_("M-Time"), cell,
821 "text", COL_MTIME, NULL);
822 gtk_tree_view_append_column(treeview, column);
823 gtk_tree_view_column_set_sort_column_id(column, COL_MTIME);
825 gtk_widget_set_size_request(GTK_WIDGET(treeview), -1, 50);
828 /* Create the handers for the View interface */
829 static void view_details_iface_init(gpointer giface, gpointer iface_data)
831 ViewIfaceClass *iface = giface;
833 g_assert(G_TYPE_FROM_INTERFACE(iface) == VIEW_TYPE_IFACE);
835 /* override stuff */
836 iface->sort = view_details_sort;
837 iface->style_changed = view_details_style_changed;
838 iface->autoselect = view_details_autoselect;
839 iface->add_items = view_details_add_items;
840 iface->update_items = view_details_update_items;
841 iface->delete_if = view_details_delete_if;
842 iface->clear = view_details_clear;
843 iface->select_all = view_details_select_all;
844 iface->clear_selection = view_details_clear_selection;
845 iface->count_items = view_details_count_items;
846 iface->count_selected = view_details_count_selected;
847 iface->show_cursor = view_details_show_cursor;
848 iface->get_iter = view_details_get_iter;
849 iface->cursor_to_iter = view_details_cursor_to_iter;
850 iface->set_selected = view_details_set_selected;
851 iface->get_selected = view_details_get_selected;
852 iface->set_frozen = view_details_set_frozen;
853 iface->select_only = view_details_select_only;
854 iface->wink_item = view_details_wink_item;
855 iface->autosize = view_details_autosize;
856 iface->cursor_visible = view_details_cursor_visible;
857 iface->set_base = view_details_set_base;
860 /* Implementations of the View interface. See view_iface.c for comments. */
862 static void view_details_style_changed(ViewIface *view, int flags)
866 static gint wrap_sort(gconstpointer a, gconstpointer b,
867 ViewDetails *view_details)
869 ViewItem *ia = *(ViewItem **) a;
870 ViewItem *ib = *(ViewItem **) b;
872 if (view_details->order == GTK_SORT_ASCENDING)
873 return view_details->sort_fn(ia->item, ib->item);
874 else
875 return -view_details->sort_fn(ia->item, ib->item);
878 static void view_details_sort(ViewIface *view)
880 ViewDetails *view_details = (ViewDetails *) view;
881 ViewItem **items = (ViewItem **) view_details->items->pdata;
882 GArray *new_order;
883 gint i, len = view_details->items->len;
884 GtkTreePath *path;
886 g_return_if_fail(view_details->sort_fn != NULL);
888 if (!len)
889 return;
891 for (i = len - 1; i >= 0; i--)
892 items[i]->old_pos = i;
894 g_ptr_array_sort_with_data(view_details->items,
895 (GCompareDataFunc) wrap_sort,
896 view_details);
898 new_order = g_array_sized_new(FALSE, FALSE, sizeof(gint), len);
899 g_array_set_size(new_order, len);
901 for (i = len - 1; i >= 0; i--)
902 g_array_insert_val(new_order, items[i]->old_pos, i);
904 path = gtk_tree_path_new();
905 gtk_tree_model_rows_reordered((GtkTreeModel *) view,
906 path, NULL,
907 (gint *) new_order->data);
908 gtk_tree_path_free(path);
909 g_array_free(new_order, TRUE);
912 static gboolean view_details_autoselect(ViewIface *view, const gchar *leaf)
914 return FALSE;
917 static void view_details_add_items(ViewIface *view, GPtrArray *new_items)
919 ViewDetails *view_details = (ViewDetails *) view;
920 FilerWindow *filer_window = view_details->filer_window;
921 gboolean show_hidden = filer_window->show_hidden;
922 GPtrArray *items = view_details->items;
923 GtkTreeIter iter;
924 int i;
925 GtkTreePath *path;
926 GtkTreeModel *model = (GtkTreeModel *) view;
928 iter.user_data = GINT_TO_POINTER(items->len);
929 path = details_get_path(model, &iter);
931 for (i = 0; i < new_items->len; i++)
933 DirItem *item = (DirItem *) new_items->pdata[i];
934 char *leafname = item->leafname;
935 ViewItem *vitem;
937 if (leafname[0] == '.')
939 if (!show_hidden)
940 continue;
942 if (leafname[1] == '\0')
943 continue; /* Never show '.' */
945 if (leafname[1] == '.' &&
946 leafname[2] == '\0')
947 continue; /* Never show '..' */
950 vitem = g_new(ViewItem, 1);
951 vitem->item = item;
952 vitem->image = NULL;
954 g_ptr_array_add(items, vitem);
956 iter.user_data = GINT_TO_POINTER(items->len - 1);
957 gtk_tree_model_row_inserted(model, path, &iter);
958 gtk_tree_path_next(path);
961 gtk_tree_path_free(path);
963 view_details_sort(view);
966 /* Find an item in the sorted array.
967 * Returns the item number, or -1 if not found.
969 static int details_find_item(ViewDetails *view_details, DirItem *item)
971 ViewItem **items, tmp, *tmpp;
972 int lower, upper;
973 int (*compar)(const void *, const void *);
975 g_return_val_if_fail(view_details != NULL, -1);
976 g_return_val_if_fail(item != NULL, -1);
978 tmp.item = item;
979 tmpp = &tmp;
981 items = (ViewItem **) view_details->items->pdata;
982 compar = view_details->sort_fn;
984 g_return_val_if_fail(compar != NULL, -1);
986 /* If item is here, then: lower <= i < upper */
987 lower = 0;
988 upper = view_details->items->len;
990 while (lower < upper)
992 int i, cmp;
994 i = (lower + upper) >> 1;
996 cmp = wrap_sort(&items[i], &tmpp, view_details);
997 if (cmp == 0)
998 return i;
1000 if (cmp > 0)
1001 upper = i;
1002 else
1003 lower = i + 1;
1006 return -1;
1009 static void view_details_update_items(ViewIface *view, GPtrArray *items)
1011 ViewDetails *view_details = (ViewDetails *) view;
1012 FilerWindow *filer_window = view_details->filer_window;
1013 int i;
1014 GtkTreeModel *model = (GtkTreeModel *) view_details;
1016 g_return_if_fail(items->len > 0);
1018 /* The item data has already been modified, so this gives the
1019 * final sort order...
1021 view_details_sort(view);
1023 for (i = 0; i < items->len; i++)
1025 DirItem *item = (DirItem *) items->pdata[i];
1026 const gchar *leafname = item->leafname;
1027 int j;
1029 if (leafname[0] == '.' && filer_window->show_hidden == FALSE)
1030 continue;
1032 j = details_find_item(view_details, item);
1034 if (j < 0)
1035 g_warning("Failed to find '%s'\n", leafname);
1036 else
1038 GtkTreePath *path;
1039 GtkTreeIter iter;
1040 path = gtk_tree_path_new();
1041 gtk_tree_path_append_index(path, j);
1042 iter.user_data = GINT_TO_POINTER(j);
1043 gtk_tree_model_row_changed(model, path, &iter);
1048 static void view_details_delete_if(ViewIface *view,
1049 gboolean (*test)(gpointer item, gpointer data),
1050 gpointer data)
1054 static void view_details_clear(ViewIface *view)
1056 GtkTreePath *path;
1057 GPtrArray *items = ((ViewDetails *) view)->items;
1058 GtkTreeModel *model = (GtkTreeModel *) view;
1060 path = gtk_tree_path_new();
1061 gtk_tree_path_append_index(path, items->len);
1063 while (gtk_tree_path_prev(path))
1064 gtk_tree_model_row_deleted(model, path);
1066 g_ptr_array_set_size(items, 0);
1067 gtk_tree_path_free(path);
1070 static void view_details_select_all(ViewIface *view)
1074 static void view_details_clear_selection(ViewIface *view)
1078 static int view_details_count_items(ViewIface *view)
1080 ViewDetails *view_details = (ViewDetails *) view;
1082 return view_details->items->len;
1085 static void inc(GtkTreeModel *model, GtkTreePath *path,
1086 GtkTreeIter *iter, gpointer data)
1088 (*((int *) data))++;
1091 static int view_details_count_selected(ViewIface *view)
1093 GtkTreeView *tree = (GtkTreeView *) view;
1094 GtkTreeSelection *selection;
1095 int count = 0;
1097 selection = gtk_tree_view_get_selection(tree);
1099 gtk_tree_selection_selected_foreach(selection, inc, &count);
1100 return count;
1103 static void view_details_show_cursor(ViewIface *view)
1107 static void view_details_get_iter(ViewIface *view,
1108 ViewIter *iter, IterFlags flags)
1110 make_iter((ViewDetails *) view, iter, flags);
1113 static void view_details_cursor_to_iter(ViewIface *view, ViewIter *iter)
1115 GtkTreePath *path;
1117 path = gtk_tree_path_new();
1119 /* XXX: How do we get rid of the cursor? */
1120 if (iter)
1121 gtk_tree_path_append_index(path, iter->i);
1122 gtk_tree_view_set_cursor((GtkTreeView *) view, path, NULL, FALSE);
1123 gtk_tree_path_free(path);
1126 static void view_details_set_selected(ViewIface *view,
1127 ViewIter *iter,
1128 gboolean selected)
1130 GtkTreeView *tree = (GtkTreeView *) view;
1131 GtkTreeSelection *selection;
1132 GtkTreeIter t_iter;
1134 selection = gtk_tree_view_get_selection(tree);
1136 t_iter.user_data = GINT_TO_POINTER(iter->i);
1137 if (selected)
1138 gtk_tree_selection_select_iter(selection, &t_iter);
1139 else
1140 gtk_tree_selection_unselect_iter(selection, &t_iter);
1143 static gboolean view_details_get_selected(ViewIface *view, ViewIter *iter)
1145 return FALSE;
1148 static void view_details_select_only(ViewIface *view, ViewIter *iter)
1152 static void view_details_set_frozen(ViewIface *view, gboolean frozen)
1156 static void view_details_wink_item(ViewIface *view, ViewIter *iter)
1160 static void view_details_autosize(ViewIface *view)
1164 static gboolean view_details_cursor_visible(ViewIface *view)
1166 return FALSE;
1169 static void view_details_set_base(ViewIface *view, ViewIter *iter)
1171 ViewDetails *view_details = (ViewDetails *) view;
1173 view_details->cursor_base = iter->i;
1176 static DirItem *iter_init(ViewIter *iter)
1178 ViewDetails *view_details = (ViewDetails *) iter->view;
1179 int i = -1;
1180 int n = view_details->items->len;
1181 int flags = iter->flags;
1183 iter->peek = iter_peek;
1185 if (iter->n_remaining == 0)
1186 return NULL;
1188 if (flags & VIEW_ITER_FROM_CURSOR)
1190 GtkTreePath *path;
1191 gtk_tree_view_get_cursor((GtkTreeView *) view_details,
1192 &path, NULL);
1193 if (!path)
1194 return NULL; /* No cursor */
1195 i = gtk_tree_path_get_indices(path)[0];
1196 gtk_tree_path_free(path);
1198 else if (flags & VIEW_ITER_FROM_BASE)
1199 i = view_details->cursor_base;
1201 if (i < 0 || i >= n)
1203 /* Either a normal iteration, or an iteration from an
1204 * invalid starting point.
1206 if (flags & VIEW_ITER_BACKWARDS)
1207 i = n - 1;
1208 else
1209 i = 0;
1212 if (i < 0 || i >= n)
1213 return NULL; /* No items at all! */
1215 iter->next = flags & VIEW_ITER_BACKWARDS ? iter_prev : iter_next;
1216 iter->n_remaining--;
1217 iter->i = i;
1219 if (flags & VIEW_ITER_SELECTED && !is_selected(view_details, i))
1220 return iter->next(iter);
1221 return iter->peek(iter);
1224 static DirItem *iter_prev(ViewIter *iter)
1226 ViewDetails *view_details = (ViewDetails *) iter->view;
1227 int n = view_details->items->len;
1228 int i = iter->i;
1230 g_return_val_if_fail(iter->n_remaining >= 0, NULL);
1232 /* i is the last item returned (always valid) */
1234 g_return_val_if_fail(i >= 0 && i < n, NULL);
1236 while (iter->n_remaining)
1238 i--;
1239 iter->n_remaining--;
1241 if (i == -1)
1242 i = n - 1;
1244 g_return_val_if_fail(i >= 0 && i < n, NULL);
1246 if (iter->flags & VIEW_ITER_SELECTED &&
1247 !is_selected(view_details, i))
1248 continue;
1250 iter->i = i;
1251 return ((ViewItem *) view_details->items->pdata[i])->item;
1254 iter->i = -1;
1255 return NULL;
1258 static DirItem *iter_next(ViewIter *iter)
1260 ViewDetails *view_details = (ViewDetails *) iter->view;
1261 int n = view_details->items->len;
1262 int i = iter->i;
1264 g_return_val_if_fail(iter->n_remaining >= 0, NULL);
1266 /* i is the last item returned (always valid) */
1268 g_return_val_if_fail(i >= 0 && i < n, NULL);
1270 while (iter->n_remaining)
1272 i++;
1273 iter->n_remaining--;
1275 if (i == n)
1276 i = 0;
1278 g_return_val_if_fail(i >= 0 && i < n, NULL);
1280 if (iter->flags & VIEW_ITER_SELECTED &&
1281 !is_selected(view_details, i))
1282 continue;
1284 iter->i = i;
1285 return ((ViewItem *) view_details->items->pdata[i])->item;
1288 iter->i = -1;
1289 return NULL;
1292 static DirItem *iter_peek(ViewIter *iter)
1294 ViewDetails *view_details = (ViewDetails *) iter->view;
1295 int n = view_details->items->len;
1296 int i = iter->i;
1298 if (i == -1)
1299 return NULL;
1301 g_return_val_if_fail(i >= 0 && i < n, NULL);
1303 return ((ViewItem *) view_details->items->pdata[i])->item;
1306 /* Set the iterator to return 'i' on the next peek() */
1307 static void make_item_iter(ViewDetails *view_details, ViewIter *iter, int i)
1309 make_iter(view_details, iter, 0);
1311 g_return_if_fail(i >= -1 && i < (int) view_details->items->len);
1313 iter->i = i;
1314 iter->next = iter_next;
1315 iter->peek = iter_peek;
1316 iter->n_remaining = 0;
1319 static void make_iter(ViewDetails *view_details, ViewIter *iter,
1320 IterFlags flags)
1322 iter->view = (ViewIface *) view_details;
1323 iter->next = iter_init;
1324 iter->peek = NULL;
1325 iter->i = -1;
1327 iter->flags = flags;
1329 if (flags & VIEW_ITER_ONE_ONLY)
1331 iter->n_remaining = 1;
1332 iter->next(iter);
1334 else
1335 iter->n_remaining = view_details->items->len;