r2219: Removed warning message from --new. It can be useful for a session manager...
[rox-filer.git] / ROX-Filer / src / view_details.c
blobcb9edc1bcc43cec39b7c17d9b617fab0f9ed330a
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 "diritem.h"
34 #include "support.h"
35 #include "type.h"
36 #include "filer.h"
37 #include "display.h"
38 #include "pixmaps.h"
39 #include "dnd.h"
40 #include "bind.h"
41 #include "gui_support.h"
42 #include "menu.h"
43 #include "options.h"
45 /* These are the column numbers in the ListStore */
46 #define COL_LEAF 0
47 #define COL_TYPE 1
48 #define COL_PERM 2
49 #define COL_OWNER 3
50 #define COL_GROUP 4
51 #define COL_SIZE 5
52 #define COL_MTIME 6
53 #define COL_ITEM 7
54 #define COL_COLOUR 8
55 #define COL_ICON 9
56 #define COL_BG_COLOUR 10
57 #define N_COLUMNS 11
59 static gpointer parent_class = NULL;
61 struct _ViewDetailsClass {
62 GtkTreeViewClass parent;
65 typedef struct _ViewItem ViewItem;
67 struct _ViewItem {
68 DirItem *item;
69 GdkPixbuf *image;
70 int old_pos; /* Used while sorting */
71 gboolean selected;
74 typedef struct _ViewDetails ViewDetails;
76 struct _ViewDetails {
77 GtkTreeView treeview;
79 FilerWindow *filer_window; /* Used for styles, etc */
81 GPtrArray *items; /* ViewItem */
83 gint sort_column_id;
84 GtkSortType order;
85 int (*sort_fn)(const void *, const void *);
87 int cursor_base; /* Cursor when minibuffer opened */
90 /* Static prototypes */
91 static void view_details_finialize(GObject *object);
92 static void view_details_class_init(gpointer gclass, gpointer data);
93 static void view_details_init(GTypeInstance *object, gpointer gclass);
95 static void view_details_iface_init(gpointer giface, gpointer iface_data);
97 static void view_details_sort(ViewIface *view);
98 static void view_details_style_changed(ViewIface *view, int flags);
99 static gboolean view_details_autoselect(ViewIface *view, const gchar *leaf);
100 static void view_details_add_items(ViewIface *view, GPtrArray *items);
101 static void view_details_update_items(ViewIface *view, GPtrArray *items);
102 static void view_details_delete_if(ViewIface *view,
103 gboolean (*test)(gpointer item, gpointer data),
104 gpointer data);
105 static void view_details_clear(ViewIface *view);
106 static void view_details_select_all(ViewIface *view);
107 static void view_details_clear_selection(ViewIface *view);
108 static int view_details_count_items(ViewIface *view);
109 static int view_details_count_selected(ViewIface *view);
110 static void view_details_show_cursor(ViewIface *view);
111 static void view_details_get_iter(ViewIface *view,
112 ViewIter *iter, IterFlags flags);
113 static void view_details_cursor_to_iter(ViewIface *view, ViewIter *iter);
114 static void view_details_set_selected(ViewIface *view,
115 ViewIter *iter,
116 gboolean selected);
117 static gboolean view_details_get_selected(ViewIface *view, ViewIter *iter);
118 static void view_details_select_only(ViewIface *view, ViewIter *iter);
119 static void view_details_set_frozen(ViewIface *view, gboolean frozen);
120 static void view_details_wink_item(ViewIface *view, ViewIter *iter);
121 static void view_details_autosize(ViewIface *view);
122 static gboolean view_details_cursor_visible(ViewIface *view);
123 static void view_details_set_base(ViewIface *view, ViewIter *iter);
124 static DirItem *iter_peek(ViewIter *iter);
125 static DirItem *iter_prev(ViewIter *iter);
126 static DirItem *iter_next(ViewIter *iter);
127 static void make_iter(ViewDetails *view_details, ViewIter *iter,
128 IterFlags flags);
129 static void make_item_iter(ViewDetails *view_details, ViewIter *iter, int i);
130 static void view_details_tree_model_init(GtkTreeModelIface *iface);
131 static gboolean details_get_sort_column_id(GtkTreeSortable *sortable,
132 gint *sort_column_id,
133 GtkSortType *order);
134 static void details_set_sort_column_id(GtkTreeSortable *sortable,
135 gint sort_column_id,
136 GtkSortType order);
137 static void details_set_sort_func(GtkTreeSortable *sortable,
138 gint sort_column_id,
139 GtkTreeIterCompareFunc func,
140 gpointer data,
141 GtkDestroyNotify destroy);
142 static void details_set_default_sort_func(GtkTreeSortable *sortable,
143 GtkTreeIterCompareFunc func,
144 gpointer data,
145 GtkDestroyNotify destroy);
146 static gboolean details_has_default_sort_func(GtkTreeSortable *sortable);
147 static void view_details_sortable_init(GtkTreeSortableIface *iface);
148 static void set_selected(ViewDetails *view_details, int i, gboolean selected);
149 static gboolean get_selected(ViewDetails *view_details, int i);
152 /****************************************************************
153 * EXTERNAL INTERFACE *
154 ****************************************************************/
156 GtkWidget *view_details_new(FilerWindow *filer_window)
158 ViewDetails *view_details;
160 view_details = g_object_new(view_details_get_type(), NULL);
161 view_details->filer_window = filer_window;
163 gtk_range_set_adjustment(GTK_RANGE(filer_window->scrollbar),
164 gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(view_details)));
166 return GTK_WIDGET(view_details);
169 GType view_details_get_type(void)
171 static GType type = 0;
173 if (!type)
175 static const GTypeInfo info =
177 sizeof (ViewDetailsClass),
178 NULL, /* base_init */
179 NULL, /* base_finalise */
180 view_details_class_init,
181 NULL, /* class_finalise */
182 NULL, /* class_data */
183 sizeof(ViewDetails),
184 0, /* n_preallocs */
185 view_details_init
187 static const GInterfaceInfo view_iface_info = {
188 view_details_iface_init,
189 NULL, NULL
191 static const GInterfaceInfo tree_model_info = {
192 (GInterfaceInitFunc) view_details_tree_model_init,
193 NULL, NULL
195 static const GInterfaceInfo sortable_info = {
196 (GInterfaceInitFunc) view_details_sortable_init,
197 NULL, NULL
201 type = g_type_register_static(gtk_tree_view_get_type(),
202 "ViewDetails", &info, 0);
204 g_type_add_interface_static(type, VIEW_TYPE_IFACE,
205 &view_iface_info);
206 g_type_add_interface_static(type, GTK_TYPE_TREE_MODEL,
207 &tree_model_info);
208 g_type_add_interface_static(type, GTK_TYPE_TREE_SORTABLE,
209 &sortable_info);
212 return type;
215 /****************************************************************
216 * INTERNAL FUNCTIONS *
217 ****************************************************************/
219 /* Fulfill the GtkTreeModel requirements */
220 static guint details_get_flags(GtkTreeModel *tree_model)
222 return GTK_TREE_MODEL_LIST_ONLY;
225 static gint details_get_n_columns(GtkTreeModel *tree_model)
227 return N_COLUMNS;
230 static GType details_get_column_type(GtkTreeModel *tree_model, gint index)
232 g_return_val_if_fail(index < N_COLUMNS && index >= 0, G_TYPE_INVALID);
234 if (index == COL_COLOUR || index == COL_BG_COLOUR)
235 return GDK_TYPE_COLOR;
236 else if (index == COL_ITEM)
237 return G_TYPE_POINTER;
238 else if (index == COL_ICON)
239 return GDK_TYPE_PIXBUF;
240 return G_TYPE_STRING;
243 static gboolean details_get_iter(GtkTreeModel *tree_model,
244 GtkTreeIter *iter,
245 GtkTreePath *path)
247 ViewDetails *view_details = (ViewDetails *) tree_model;
248 gint i;
250 g_return_val_if_fail(gtk_tree_path_get_depth (path) > 0, FALSE);
252 i = gtk_tree_path_get_indices(path)[0];
254 if (i >= view_details->items->len)
255 return FALSE;
257 iter->user_data = GINT_TO_POINTER(i);
259 return TRUE;
262 static GtkTreePath *details_get_path(GtkTreeModel *tree_model,
263 GtkTreeIter *iter)
265 GtkTreePath *retval;
267 retval = gtk_tree_path_new();
268 gtk_tree_path_append_index(retval, GPOINTER_TO_INT(iter->user_data));
270 return retval;
273 static void details_get_value(GtkTreeModel *tree_model,
274 GtkTreeIter *iter,
275 gint column,
276 GValue *value)
278 ViewDetails *view_details = (ViewDetails *) tree_model;
279 GtkStyle *style = ((GtkWidget *) tree_model)->style;
280 gint i;
281 GPtrArray *items = view_details->items;
282 ViewItem *view_item;
283 DirItem *item;
284 mode_t m;
286 g_return_if_fail(column >= 0 && column < N_COLUMNS);
288 i = GPOINTER_TO_INT(iter->user_data);
289 g_return_if_fail(i >= 0 && i < items->len);
290 view_item = (ViewItem *) items->pdata[i];
291 item = view_item->item;
293 /* g_print("[ get %d ]\n", column); */
295 if (column == COL_LEAF)
297 g_value_init(value, G_TYPE_STRING);
298 g_value_set_string(value, item->leafname);
299 return;
301 else if (column == COL_ITEM)
303 g_value_init(value, G_TYPE_POINTER);
304 g_value_set_pointer(value, item);
305 return;
308 if (item->base_type == TYPE_UNKNOWN)
310 GType type;
311 type = details_get_column_type(tree_model, column);
312 g_value_init(value, type);
313 if (type == G_TYPE_STRING)
314 g_value_set_string(value, "");
315 else if (type == GDK_TYPE_COLOR)
316 g_value_set_boxed(value, NULL);
317 else
318 g_value_set_object(value, NULL);
320 return;
322 m = item->mode;
324 switch (column)
326 case COL_LEAF:
327 g_value_init(value, G_TYPE_STRING);
328 g_value_set_string(value, item->leafname);
329 break;
330 case COL_ICON:
331 g_value_init(value, GDK_TYPE_PIXBUF);
332 if (!item->image->sm_pixbuf)
333 pixmap_make_small(item->image);
334 g_value_set_object(value, item->image->sm_pixbuf);
335 break;
336 case COL_COLOUR:
337 g_value_init(value, GDK_TYPE_COLOR);
338 g_value_set_boxed(value, type_get_colour(item, NULL));
339 break;
340 case COL_BG_COLOUR:
341 g_value_init(value, GDK_TYPE_COLOR);
342 g_value_set_boxed(value, view_item->selected
343 ? &style->base[GTK_STATE_SELECTED]
344 : &style->base[GTK_STATE_NORMAL]);
345 break;
346 case COL_OWNER:
347 g_value_init(value, G_TYPE_STRING);
348 g_value_set_string(value, user_name(item->uid));
349 break;
350 case COL_GROUP:
351 g_value_init(value, G_TYPE_STRING);
352 g_value_set_string(value, group_name(item->gid));
353 break;
354 case COL_MTIME:
356 gchar *time;
357 time = pretty_time(&item->mtime);
358 g_value_init(value, G_TYPE_STRING);
359 g_value_set_string(value, time);
360 g_free(time);
361 break;
363 case COL_PERM:
364 g_value_init(value, G_TYPE_STRING);
365 g_value_set_string(value, pretty_permissions(m));
366 break;
367 case COL_SIZE:
368 g_value_init(value, G_TYPE_STRING);
369 if (item->base_type != TYPE_DIRECTORY)
370 g_value_set_string(value,
371 format_size(item->size));
372 break;
373 case COL_TYPE:
374 g_value_init(value, G_TYPE_STRING);
375 g_value_set_string(value,
376 item->flags & ITEM_FLAG_APPDIR? "App" :
377 S_ISDIR(m) ? "Dir" :
378 S_ISCHR(m) ? "Char" :
379 S_ISBLK(m) ? "Blck" :
380 S_ISLNK(m) ? "Link" :
381 S_ISSOCK(m) ? "Sock" :
382 S_ISFIFO(m) ? "Pipe" :
383 S_ISDOOR(m) ? "Door" :
384 "File");
385 break;
386 default:
387 g_value_init(value, G_TYPE_STRING);
388 g_value_set_string(value, "Hello");
389 break;
393 static gboolean details_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter)
395 ViewDetails *view_details = (ViewDetails *) tree_model;
396 int i;
398 i = GPOINTER_TO_INT(iter->user_data) + 1;
399 iter->user_data = GINT_TO_POINTER(i);
401 return i < view_details->items->len;
404 static gboolean details_iter_children(GtkTreeModel *tree_model,
405 GtkTreeIter *iter,
406 GtkTreeIter *parent)
408 ViewDetails *view_details = (ViewDetails *) tree_model;
410 /* this is a list, nodes have no children */
411 if (parent)
412 return FALSE;
414 /* but if parent == NULL we return the list itself as children of the
415 * "root"
418 if (view_details->items->len)
420 iter->user_data = GINT_TO_POINTER(0);
421 return TRUE;
423 else
424 return FALSE;
427 static gboolean details_iter_has_child(GtkTreeModel *tree_model,
428 GtkTreeIter *iter)
430 return FALSE;
433 static gint details_iter_n_children(GtkTreeModel *tree_model, GtkTreeIter *iter)
435 ViewDetails *view_details = (ViewDetails *) tree_model;
437 if (iter == NULL)
438 return view_details->items->len;
440 return 0;
443 static gboolean details_iter_nth_child(GtkTreeModel *tree_model,
444 GtkTreeIter *iter,
445 GtkTreeIter *parent,
446 gint n)
448 ViewDetails *view_details = (ViewDetails *) tree_model;
450 if (parent)
451 return FALSE;
453 if (n >= 0 && n < view_details->items->len)
455 iter->user_data = GINT_TO_POINTER(n);
456 return TRUE;
458 else
459 return FALSE;
462 static gboolean details_iter_parent(GtkTreeModel *tree_model,
463 GtkTreeIter *iter,
464 GtkTreeIter *child)
466 return FALSE;
469 /* A ViewDetails is both a GtkTreeView and a GtkTreeModel.
470 * The following functions implement the model interface...
473 static void view_details_tree_model_init(GtkTreeModelIface *iface)
475 iface->get_flags = details_get_flags;
476 iface->get_n_columns = details_get_n_columns;
477 iface->get_column_type = details_get_column_type;
478 iface->get_iter = details_get_iter;
479 iface->get_path = details_get_path;
480 iface->get_value = details_get_value;
481 iface->iter_next = details_iter_next;
482 iface->iter_children = details_iter_children;
483 iface->iter_has_child = details_iter_has_child;
484 iface->iter_n_children = details_iter_n_children;
485 iface->iter_nth_child = details_iter_nth_child;
486 iface->iter_parent = details_iter_parent;
489 static void view_details_sortable_init(GtkTreeSortableIface *iface)
491 iface->get_sort_column_id = details_get_sort_column_id;
492 iface->set_sort_column_id = details_set_sort_column_id;
493 iface->set_sort_func = details_set_sort_func;
494 iface->set_default_sort_func = details_set_default_sort_func;
495 iface->has_default_sort_func = details_has_default_sort_func;
498 static gboolean details_get_sort_column_id(GtkTreeSortable *sortable,
499 gint *sort_column_id,
500 GtkSortType *order)
502 ViewDetails *view_details = (ViewDetails *) sortable;
504 if (view_details->sort_column_id == -1)
505 return FALSE;
507 if (sort_column_id)
508 *sort_column_id = view_details->sort_column_id;
509 if (order)
510 *order = view_details->order;
511 return TRUE;
514 static void details_set_sort_column_id(GtkTreeSortable *sortable,
515 gint sort_column_id,
516 GtkSortType order)
518 ViewDetails *view_details = (ViewDetails *) sortable;
520 if (view_details->sort_column_id == sort_column_id &&
521 view_details->order == order)
522 return;
524 view_details->sort_column_id = sort_column_id;
525 view_details->order = order;
527 switch (sort_column_id)
529 case COL_LEAF:
530 view_details->sort_fn = sort_by_name;
531 break;
532 case COL_SIZE:
533 view_details->sort_fn = sort_by_size;
534 break;
535 case COL_MTIME:
536 view_details->sort_fn = sort_by_date;
537 break;
538 case COL_TYPE:
539 view_details->sort_fn = sort_by_type;
540 break;
541 case COL_OWNER:
542 view_details->sort_fn = sort_by_owner;
543 break;
544 case COL_GROUP:
545 view_details->sort_fn = sort_by_group;
546 break;
547 default:
548 g_assert_not_reached();
551 view_details_sort((ViewIface *) view_details);
553 gtk_tree_sortable_sort_column_changed(sortable);
556 static void details_set_sort_func(GtkTreeSortable *sortable,
557 gint sort_column_id,
558 GtkTreeIterCompareFunc func,
559 gpointer data,
560 GtkDestroyNotify destroy)
562 g_assert_not_reached();
565 static void details_set_default_sort_func(GtkTreeSortable *sortable,
566 GtkTreeIterCompareFunc func,
567 gpointer data,
568 GtkDestroyNotify destroy)
570 g_assert_not_reached();
573 static gboolean details_has_default_sort_func(GtkTreeSortable *sortable)
575 return FALSE;
579 /* End of model implementation */
581 static gboolean is_selected(ViewDetails *view_details, int i)
583 ViewIter iter;
584 iter.i = i;
585 return view_details_get_selected((ViewIface *) view_details, &iter);
588 static void perform_action(ViewDetails *view_details, GdkEventButton *event)
590 BindAction action;
591 FilerWindow *filer_window = view_details->filer_window;
592 ViewIface *view = (ViewIface *) view_details;
593 DirItem *item = NULL;
594 GtkTreeView *tree = (GtkTreeView *) view_details;
595 GtkTreePath *path = NULL;
596 GtkTreeIter iter;
597 GtkTreeModel *model;
598 gboolean press = event->type == GDK_BUTTON_PRESS;
599 int i = -1;
600 ViewIter viter;
601 OpenFlags flags = 0;
603 model = gtk_tree_view_get_model(tree);
605 if (gtk_tree_view_get_path_at_pos(tree, event->x, event->y,
606 &path, NULL, NULL, NULL))
608 g_return_if_fail(path != NULL);
610 i = gtk_tree_path_get_indices(path)[0];
611 gtk_tree_path_free(path);
614 if (i != -1)
615 item = ((ViewItem *) view_details->items->pdata[i])->item;
616 make_item_iter(view_details, &viter, i);
617 iter.user_data = GINT_TO_POINTER(i);
619 /* TODO: Cancel slow DnD */
621 if (filer_window->target_cb)
623 dnd_motion_ungrab();
624 if (item && press && event->button == 1)
625 filer_window->target_cb(filer_window, &viter,
626 filer_window->target_data);
628 filer_target_mode(filer_window, NULL, NULL, NULL);
630 return;
633 action = bind_lookup_bev(
634 item ? BIND_DIRECTORY_ICON : BIND_DIRECTORY,
635 event);
637 switch (action)
639 case ACT_CLEAR_SELECTION:
640 view_details_clear_selection(view);
641 break;
642 case ACT_TOGGLE_SELECTED:
643 set_selected(view_details, i,
644 !get_selected(view_details, i));
645 break;
646 case ACT_SELECT_EXCL:
647 view_details_select_only(view, &viter);
648 break;
649 case ACT_EDIT_ITEM:
650 flags |= OPEN_SHIFT;
651 /* (no break) */
652 case ACT_OPEN_ITEM:
653 if (event->button != 1 || event->state & GDK_MOD1_MASK)
654 flags |= OPEN_CLOSE_WINDOW;
655 else
656 flags |= OPEN_SAME_WINDOW;
657 if (o_new_button_1.int_value)
658 flags ^= OPEN_SAME_WINDOW;
659 if (event->type == GDK_2BUTTON_PRESS)
660 view_details_set_selected(view, &viter, FALSE);
661 dnd_motion_ungrab();
663 filer_openitem(filer_window, &viter, flags);
664 break;
665 case ACT_POPUP_MENU:
667 dnd_motion_ungrab();
668 tooltip_show(NULL);
670 show_filer_menu(filer_window,
671 (GdkEvent *) event, &viter);
672 break;
673 case ACT_PRIME_AND_SELECT:
674 if (item && !is_selected(view_details, i))
675 view_details_select_only(view, &viter);
676 dnd_motion_start(MOTION_READY_FOR_DND);
677 break;
678 case ACT_PRIME_AND_TOGGLE:
679 set_selected(view_details, i,
680 !get_selected(view_details, i));
681 dnd_motion_start(MOTION_READY_FOR_DND);
682 break;
683 case ACT_PRIME_FOR_DND:
684 dnd_motion_start(MOTION_READY_FOR_DND);
685 break;
686 case ACT_IGNORE:
687 if (press && event->button < 4)
689 if (item)
690 view_details_wink_item(view, &viter);
691 dnd_motion_start(MOTION_NONE);
693 break;
694 case ACT_LASSO_CLEAR:
695 view_details_clear_selection(view);
696 /* (no break) */
697 case ACT_LASSO_MODIFY:
698 #if 0
699 collection_lasso_box(collection, event->x, event->y);
700 #endif
701 break;
702 case ACT_RESIZE:
703 filer_window_autosize(filer_window);
704 break;
705 default:
706 g_warning("Unsupported action : %d\n", action);
707 break;
711 static gboolean view_details_button_press(GtkWidget *widget,
712 GdkEventButton *bev)
714 if (dnd_motion_press(widget, bev))
715 perform_action((ViewDetails *) widget, bev);
717 return TRUE;
720 static gboolean view_details_button_release(GtkWidget *widget,
721 GdkEventButton *bev)
723 if (!dnd_motion_release(bev))
724 perform_action((ViewDetails *) widget, bev);
726 return TRUE;
729 static gint view_details_key_press_event(GtkWidget *widget, GdkEventKey *event)
731 if (event->keyval == ' ')
733 ViewDetails *view_details = (ViewDetails *) widget;
734 FilerWindow *filer_window = view_details->filer_window;
736 filer_window_toggle_cursor_item_selected(filer_window);
737 return TRUE;
740 return GTK_WIDGET_CLASS(parent_class)->key_press_event(widget, event);
743 static void view_details_destroy(GtkObject *view_details)
745 VIEW_DETAILS(view_details)->filer_window = NULL;
748 static void view_details_finialize(GObject *object)
750 ViewDetails *view_details = (ViewDetails *) object;
752 g_ptr_array_free(view_details->items, TRUE);
753 view_details->items = NULL;
755 G_OBJECT_CLASS(parent_class)->finalize(object);
758 static void view_details_class_init(gpointer gclass, gpointer data)
760 GObjectClass *object = (GObjectClass *) gclass;
761 GtkWidgetClass *widget = (GtkWidgetClass *) gclass;
763 parent_class = g_type_class_peek_parent(gclass);
765 object->finalize = view_details_finialize;
766 GTK_OBJECT_CLASS(object)->destroy = view_details_destroy;
768 widget->button_press_event = view_details_button_press;
769 widget->button_release_event = view_details_button_release;
770 widget->key_press_event = view_details_key_press_event;
773 static void view_details_init(GTypeInstance *object, gpointer gclass)
775 GtkTreeView *treeview = (GtkTreeView *) object;
776 GtkTreeViewColumn *column;
777 GtkCellRenderer *cell;
778 GtkTreeSortable *sortable_list;
779 GtkTreeSelection *selection;
780 ViewDetails *view_details = (ViewDetails *) object;
782 view_details->items = g_ptr_array_new();
783 view_details->cursor_base = -1;
785 selection = gtk_tree_view_get_selection(treeview);
786 gtk_tree_selection_set_mode(selection, GTK_SELECTION_NONE);
788 /* Sorting */
789 view_details->sort_column_id = -1;
790 view_details->sort_fn = NULL;
791 sortable_list = GTK_TREE_SORTABLE(object);
792 gtk_tree_sortable_set_sort_column_id(sortable_list, COL_LEAF,
793 GTK_SORT_ASCENDING);
795 gtk_tree_view_set_model(treeview, GTK_TREE_MODEL(view_details));
797 /* Icon */
798 cell = gtk_cell_renderer_pixbuf_new();
799 column = gtk_tree_view_column_new_with_attributes(NULL, cell,
800 "pixbuf", COL_ICON, NULL);
801 gtk_tree_view_append_column(treeview, column);
803 /* Name */
804 cell = gtk_cell_renderer_text_new();
805 column = gtk_tree_view_column_new_with_attributes(_("Name"), cell,
806 "text", COL_LEAF,
807 "foreground-gdk", COL_COLOUR,
808 "background-gdk", COL_BG_COLOUR,
809 NULL);
810 gtk_tree_view_append_column(treeview, column);
811 gtk_tree_view_column_set_sort_column_id(column, COL_LEAF);
813 /* Type */
814 cell = gtk_cell_renderer_text_new();
815 column = gtk_tree_view_column_new_with_attributes(_("Type"), cell,
816 "text", COL_TYPE,
817 "foreground-gdk", COL_COLOUR,
818 "background-gdk", COL_BG_COLOUR,
819 NULL);
820 gtk_tree_view_append_column(treeview, column);
821 gtk_tree_view_column_set_sort_column_id(column, COL_TYPE);
823 /* Perm */
824 cell = gtk_cell_renderer_text_new();
825 column = gtk_tree_view_column_new_with_attributes(_("Permissions"),
826 cell, "text", COL_PERM,
827 "foreground-gdk", COL_COLOUR,
828 "background-gdk", COL_BG_COLOUR,
829 NULL);
830 gtk_tree_view_append_column(treeview, column);
832 /* Owner */
833 cell = gtk_cell_renderer_text_new();
834 column = gtk_tree_view_column_new_with_attributes(_("Owner"), cell,
835 "text", COL_OWNER,
836 "foreground-gdk", COL_COLOUR,
837 "background-gdk", COL_BG_COLOUR,
838 NULL);
839 gtk_tree_view_append_column(treeview, column);
840 gtk_tree_view_column_set_sort_column_id(column, COL_OWNER);
842 /* Group */
843 cell = gtk_cell_renderer_text_new();
844 column = gtk_tree_view_column_new_with_attributes(_("Group"), cell,
845 "text", COL_GROUP,
846 "foreground-gdk", COL_COLOUR,
847 "background-gdk", COL_BG_COLOUR,
848 NULL);
849 gtk_tree_view_append_column(treeview, column);
850 gtk_tree_view_column_set_sort_column_id(column, COL_GROUP);
852 /* Size */
853 cell = gtk_cell_renderer_text_new();
854 column = gtk_tree_view_column_new_with_attributes(_("Size"), cell,
855 "text", COL_SIZE,
856 "foreground-gdk", COL_COLOUR,
857 "background-gdk", COL_BG_COLOUR,
858 NULL);
859 gtk_tree_view_append_column(treeview, column);
860 gtk_tree_view_column_set_sort_column_id(column, COL_SIZE);
862 /* MTime */
863 cell = gtk_cell_renderer_text_new();
864 column = gtk_tree_view_column_new_with_attributes(_("M-Time"), cell,
865 "text", COL_MTIME,
866 "foreground-gdk", COL_COLOUR,
867 "background-gdk", COL_BG_COLOUR,
868 NULL);
869 gtk_tree_view_append_column(treeview, column);
870 gtk_tree_view_column_set_sort_column_id(column, COL_MTIME);
872 gtk_widget_set_size_request(GTK_WIDGET(treeview), -1, 50);
875 /* Create the handers for the View interface */
876 static void view_details_iface_init(gpointer giface, gpointer iface_data)
878 ViewIfaceClass *iface = giface;
880 g_assert(G_TYPE_FROM_INTERFACE(iface) == VIEW_TYPE_IFACE);
882 /* override stuff */
883 iface->sort = view_details_sort;
884 iface->style_changed = view_details_style_changed;
885 iface->autoselect = view_details_autoselect;
886 iface->add_items = view_details_add_items;
887 iface->update_items = view_details_update_items;
888 iface->delete_if = view_details_delete_if;
889 iface->clear = view_details_clear;
890 iface->select_all = view_details_select_all;
891 iface->clear_selection = view_details_clear_selection;
892 iface->count_items = view_details_count_items;
893 iface->count_selected = view_details_count_selected;
894 iface->show_cursor = view_details_show_cursor;
895 iface->get_iter = view_details_get_iter;
896 iface->cursor_to_iter = view_details_cursor_to_iter;
897 iface->set_selected = view_details_set_selected;
898 iface->get_selected = view_details_get_selected;
899 iface->set_frozen = view_details_set_frozen;
900 iface->select_only = view_details_select_only;
901 iface->wink_item = view_details_wink_item;
902 iface->autosize = view_details_autosize;
903 iface->cursor_visible = view_details_cursor_visible;
904 iface->set_base = view_details_set_base;
907 /* Implementations of the View interface. See view_iface.c for comments. */
909 static void view_details_style_changed(ViewIface *view, int flags)
913 static gint wrap_sort(gconstpointer a, gconstpointer b,
914 ViewDetails *view_details)
916 ViewItem *ia = *(ViewItem **) a;
917 ViewItem *ib = *(ViewItem **) b;
919 if (view_details->order == GTK_SORT_ASCENDING)
920 return view_details->sort_fn(ia->item, ib->item);
921 else
922 return -view_details->sort_fn(ia->item, ib->item);
925 static void view_details_sort(ViewIface *view)
927 ViewDetails *view_details = (ViewDetails *) view;
928 ViewItem **items = (ViewItem **) view_details->items->pdata;
929 GArray *new_order;
930 gint i, len = view_details->items->len;
931 GtkTreePath *path;
933 g_return_if_fail(view_details->sort_fn != NULL);
935 if (!len)
936 return;
938 for (i = len - 1; i >= 0; i--)
939 items[i]->old_pos = i;
941 g_ptr_array_sort_with_data(view_details->items,
942 (GCompareDataFunc) wrap_sort,
943 view_details);
945 new_order = g_array_sized_new(FALSE, FALSE, sizeof(gint), len);
946 g_array_set_size(new_order, len);
948 for (i = len - 1; i >= 0; i--)
949 g_array_insert_val(new_order, items[i]->old_pos, i);
951 path = gtk_tree_path_new();
952 gtk_tree_model_rows_reordered((GtkTreeModel *) view,
953 path, NULL,
954 (gint *) new_order->data);
955 gtk_tree_path_free(path);
956 g_array_free(new_order, TRUE);
959 static gboolean view_details_autoselect(ViewIface *view, const gchar *leaf)
961 return FALSE;
964 static void view_details_add_items(ViewIface *view, GPtrArray *new_items)
966 ViewDetails *view_details = (ViewDetails *) view;
967 FilerWindow *filer_window = view_details->filer_window;
968 gboolean show_hidden = filer_window->show_hidden;
969 GPtrArray *items = view_details->items;
970 GtkTreeIter iter;
971 int i;
972 GtkTreePath *path;
973 GtkTreeModel *model = (GtkTreeModel *) view;
975 iter.user_data = GINT_TO_POINTER(items->len);
976 path = details_get_path(model, &iter);
978 for (i = 0; i < new_items->len; i++)
980 DirItem *item = (DirItem *) new_items->pdata[i];
981 char *leafname = item->leafname;
982 ViewItem *vitem;
984 if (leafname[0] == '.')
986 if (!show_hidden)
987 continue;
989 if (leafname[1] == '\0')
990 continue; /* Never show '.' */
992 if (leafname[1] == '.' &&
993 leafname[2] == '\0')
994 continue; /* Never show '..' */
997 vitem = g_new(ViewItem, 1);
998 vitem->item = item;
999 vitem->image = NULL;
1000 vitem->selected = FALSE;
1002 g_ptr_array_add(items, vitem);
1004 iter.user_data = GINT_TO_POINTER(items->len - 1);
1005 gtk_tree_model_row_inserted(model, path, &iter);
1006 gtk_tree_path_next(path);
1009 gtk_tree_path_free(path);
1011 view_details_sort(view);
1014 /* Find an item in the sorted array.
1015 * Returns the item number, or -1 if not found.
1017 static int details_find_item(ViewDetails *view_details, DirItem *item)
1019 ViewItem **items, tmp, *tmpp;
1020 int lower, upper;
1021 int (*compar)(const void *, const void *);
1023 g_return_val_if_fail(view_details != NULL, -1);
1024 g_return_val_if_fail(item != NULL, -1);
1026 tmp.item = item;
1027 tmpp = &tmp;
1029 items = (ViewItem **) view_details->items->pdata;
1030 compar = view_details->sort_fn;
1032 g_return_val_if_fail(compar != NULL, -1);
1034 /* If item is here, then: lower <= i < upper */
1035 lower = 0;
1036 upper = view_details->items->len;
1038 while (lower < upper)
1040 int i, cmp;
1042 i = (lower + upper) >> 1;
1044 cmp = wrap_sort(&items[i], &tmpp, view_details);
1045 if (cmp == 0)
1046 return i;
1048 if (cmp > 0)
1049 upper = i;
1050 else
1051 lower = i + 1;
1054 return -1;
1057 static void view_details_update_items(ViewIface *view, GPtrArray *items)
1059 ViewDetails *view_details = (ViewDetails *) view;
1060 FilerWindow *filer_window = view_details->filer_window;
1061 int i;
1062 GtkTreeModel *model = (GtkTreeModel *) view_details;
1064 g_return_if_fail(items->len > 0);
1066 /* The item data has already been modified, so this gives the
1067 * final sort order...
1069 view_details_sort(view);
1071 for (i = 0; i < items->len; i++)
1073 DirItem *item = (DirItem *) items->pdata[i];
1074 const gchar *leafname = item->leafname;
1075 int j;
1077 if (leafname[0] == '.' && filer_window->show_hidden == FALSE)
1078 continue;
1080 j = details_find_item(view_details, item);
1082 if (j < 0)
1083 g_warning("Failed to find '%s'\n", leafname);
1084 else
1086 GtkTreePath *path;
1087 GtkTreeIter iter;
1088 path = gtk_tree_path_new();
1089 gtk_tree_path_append_index(path, j);
1090 iter.user_data = GINT_TO_POINTER(j);
1091 gtk_tree_model_row_changed(model, path, &iter);
1096 static void view_details_delete_if(ViewIface *view,
1097 gboolean (*test)(gpointer item, gpointer data),
1098 gpointer data)
1100 GtkTreePath *path;
1101 ViewDetails *view_details = (ViewDetails *) view;
1102 int i = 0;
1103 GPtrArray *items = view_details->items;
1104 GtkTreeModel *model = (GtkTreeModel *) view;
1106 path = gtk_tree_path_new();
1108 gtk_tree_path_append_index(path, i);
1110 while (i < items->len)
1112 ViewItem *item = items->pdata[i];
1114 if (test(item->item, data))
1116 g_free(items->pdata[i]);
1117 g_ptr_array_remove_index(items, i);
1118 gtk_tree_model_row_deleted(model, path);
1120 else
1122 i++;
1123 gtk_tree_path_next(path);
1127 gtk_tree_path_free(path);
1130 static void view_details_clear(ViewIface *view)
1132 GtkTreePath *path;
1133 GPtrArray *items = ((ViewDetails *) view)->items;
1134 GtkTreeModel *model = (GtkTreeModel *) view;
1136 path = gtk_tree_path_new();
1137 gtk_tree_path_append_index(path, items->len);
1139 while (gtk_tree_path_prev(path))
1140 gtk_tree_model_row_deleted(model, path);
1142 g_ptr_array_set_size(items, 0);
1143 gtk_tree_path_free(path);
1146 static void view_details_select_all(ViewIface *view)
1148 int i;
1149 int n = ((ViewDetails *) view)->items->len;
1151 for (i = 0; i < n; i++)
1153 ViewIter iter;
1154 iter.i = i;
1155 view_details_set_selected(view, &iter, TRUE);
1159 static void view_details_clear_selection(ViewIface *view)
1161 int i;
1162 int n = ((ViewDetails *) view)->items->len;
1164 for (i = 0; i < n; i++)
1166 ViewIter iter;
1167 iter.i = i;
1168 view_details_set_selected(view, &iter, FALSE);
1172 static int view_details_count_items(ViewIface *view)
1174 ViewDetails *view_details = (ViewDetails *) view;
1176 return view_details->items->len;
1179 static int view_details_count_selected(ViewIface *view)
1181 ViewDetails *view_details = (ViewDetails *) view;
1182 int n = view_details->items->len;
1183 ViewItem **items = (ViewItem **) view_details->items->pdata;
1184 int count = 0;
1185 int i;
1187 for (i = 0; i < n; i++)
1188 if (items[i]->selected)
1189 count++;
1191 return count;
1194 static void view_details_show_cursor(ViewIface *view)
1198 static void view_details_get_iter(ViewIface *view,
1199 ViewIter *iter, IterFlags flags)
1201 make_iter((ViewDetails *) view, iter, flags);
1204 static void view_details_cursor_to_iter(ViewIface *view, ViewIter *iter)
1206 GtkTreePath *path;
1208 if (!iter)
1210 /* XXX: How do we get rid of the cursor? */
1211 g_print("FIXME: Remove cursor\n");
1212 return;
1215 path = gtk_tree_path_new();
1217 gtk_tree_path_append_index(path, iter->i);
1218 gtk_tree_view_set_cursor((GtkTreeView *) view, path, NULL, FALSE);
1219 gtk_tree_path_free(path);
1222 static void set_selected(ViewDetails *view_details, int i, gboolean selected)
1224 GtkTreeModel *model = (GtkTreeModel *) view_details;
1225 GtkTreeIter t_iter;
1226 GtkTreePath *path;
1227 GPtrArray *items = view_details->items;
1228 ViewItem *view_item;
1230 g_return_if_fail(i >= 0 && i < items->len);
1231 view_item = (ViewItem *) items->pdata[i];
1233 if (view_item->selected == selected)
1234 return;
1236 view_item->selected = selected;
1238 path = gtk_tree_path_new();
1239 gtk_tree_path_append_index(path, i);
1240 t_iter.user_data = GINT_TO_POINTER(i);
1241 gtk_tree_model_row_changed(model, path, &t_iter);
1242 gtk_tree_path_free(path);
1245 static void view_details_set_selected(ViewIface *view,
1246 ViewIter *iter,
1247 gboolean selected)
1249 set_selected((ViewDetails *) view, iter->i, selected);
1252 static gboolean get_selected(ViewDetails *view_details, int i)
1254 GPtrArray *items = view_details->items;
1256 g_return_val_if_fail(i >= 0 && i < items->len, FALSE);
1258 return ((ViewItem *) items->pdata[i])->selected;
1261 static gboolean view_details_get_selected(ViewIface *view, ViewIter *iter)
1263 return get_selected((ViewDetails *) view, iter->i);
1266 static void view_details_select_only(ViewIface *view, ViewIter *iter)
1268 ViewDetails *view_details = (ViewDetails *) view;
1269 int i = iter->i;
1270 int n = view_details->items->len;
1272 set_selected(view_details, i, TRUE);
1274 while (n > 0)
1276 n--;
1278 if (n != i)
1279 set_selected(view_details, n, FALSE);
1283 static void view_details_set_frozen(ViewIface *view, gboolean frozen)
1287 static void view_details_wink_item(ViewIface *view, ViewIter *iter)
1291 static void view_details_autosize(ViewIface *view)
1295 static gboolean view_details_cursor_visible(ViewIface *view)
1297 return FALSE;
1300 static void view_details_set_base(ViewIface *view, ViewIter *iter)
1302 ViewDetails *view_details = (ViewDetails *) view;
1304 view_details->cursor_base = iter->i;
1307 static DirItem *iter_init(ViewIter *iter)
1309 ViewDetails *view_details = (ViewDetails *) iter->view;
1310 int i = -1;
1311 int n = view_details->items->len;
1312 int flags = iter->flags;
1314 iter->peek = iter_peek;
1316 if (iter->n_remaining == 0)
1317 return NULL;
1319 if (flags & VIEW_ITER_FROM_CURSOR)
1321 GtkTreePath *path;
1322 gtk_tree_view_get_cursor((GtkTreeView *) view_details,
1323 &path, NULL);
1324 if (!path)
1325 return NULL; /* No cursor */
1326 i = gtk_tree_path_get_indices(path)[0];
1327 gtk_tree_path_free(path);
1329 else if (flags & VIEW_ITER_FROM_BASE)
1330 i = view_details->cursor_base;
1332 if (i < 0 || i >= n)
1334 /* Either a normal iteration, or an iteration from an
1335 * invalid starting point.
1337 if (flags & VIEW_ITER_BACKWARDS)
1338 i = n - 1;
1339 else
1340 i = 0;
1343 if (i < 0 || i >= n)
1344 return NULL; /* No items at all! */
1346 iter->next = flags & VIEW_ITER_BACKWARDS ? iter_prev : iter_next;
1347 iter->n_remaining--;
1348 iter->i = i;
1350 if (flags & VIEW_ITER_SELECTED && !is_selected(view_details, i))
1351 return iter->next(iter);
1352 return iter->peek(iter);
1355 static DirItem *iter_prev(ViewIter *iter)
1357 ViewDetails *view_details = (ViewDetails *) iter->view;
1358 int n = view_details->items->len;
1359 int i = iter->i;
1361 g_return_val_if_fail(iter->n_remaining >= 0, NULL);
1363 /* i is the last item returned (always valid) */
1365 g_return_val_if_fail(i >= 0 && i < n, NULL);
1367 while (iter->n_remaining)
1369 i--;
1370 iter->n_remaining--;
1372 if (i == -1)
1373 i = n - 1;
1375 g_return_val_if_fail(i >= 0 && i < n, NULL);
1377 if (iter->flags & VIEW_ITER_SELECTED &&
1378 !is_selected(view_details, i))
1379 continue;
1381 iter->i = i;
1382 return ((ViewItem *) view_details->items->pdata[i])->item;
1385 iter->i = -1;
1386 return NULL;
1389 static DirItem *iter_next(ViewIter *iter)
1391 ViewDetails *view_details = (ViewDetails *) iter->view;
1392 int n = view_details->items->len;
1393 int i = iter->i;
1395 g_return_val_if_fail(iter->n_remaining >= 0, NULL);
1397 /* i is the last item returned (always valid) */
1399 g_return_val_if_fail(i >= 0 && i < n, NULL);
1401 while (iter->n_remaining)
1403 i++;
1404 iter->n_remaining--;
1406 if (i == n)
1407 i = 0;
1409 g_return_val_if_fail(i >= 0 && i < n, NULL);
1411 if (iter->flags & VIEW_ITER_SELECTED &&
1412 !is_selected(view_details, i))
1413 continue;
1415 iter->i = i;
1416 return ((ViewItem *) view_details->items->pdata[i])->item;
1419 iter->i = -1;
1420 return NULL;
1423 static DirItem *iter_peek(ViewIter *iter)
1425 ViewDetails *view_details = (ViewDetails *) iter->view;
1426 int n = view_details->items->len;
1427 int i = iter->i;
1429 if (i == -1)
1430 return NULL;
1432 g_return_val_if_fail(i >= 0 && i < n, NULL);
1434 return ((ViewItem *) view_details->items->pdata[i])->item;
1437 /* Set the iterator to return 'i' on the next peek() */
1438 static void make_item_iter(ViewDetails *view_details, ViewIter *iter, int i)
1440 make_iter(view_details, iter, 0);
1442 g_return_if_fail(i >= -1 && i < (int) view_details->items->len);
1444 iter->i = i;
1445 iter->next = iter_next;
1446 iter->peek = iter_peek;
1447 iter->n_remaining = 0;
1450 static void make_iter(ViewDetails *view_details, ViewIter *iter,
1451 IterFlags flags)
1453 iter->view = (ViewIface *) view_details;
1454 iter->next = iter_init;
1455 iter->peek = NULL;
1456 iter->i = -1;
1458 iter->flags = flags;
1460 if (flags & VIEW_ITER_ONE_ONLY)
1462 iter->n_remaining = 1;
1463 iter->next(iter);
1465 else
1466 iter->n_remaining = view_details->items->len;