r2229: Right-clicking the Size button switches to automatic mode and resizes the
[rox-filer.git] / ROX-Filer / src / view_details.c
blobdf79b52679132f3a9cb4a45a0ca6b1bb8bf0d1b2
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2002, the ROX-Filer team.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* view_details.c - display a list of files in a TreeView */
24 #include "config.h"
26 #include <gtk/gtk.h>
27 #include <gdk/gdkkeysyms.h>
29 #include "global.h"
31 #include "view_iface.h"
32 #include "view_details.h"
33 #include "dir.h"
34 #include "diritem.h"
35 #include "support.h"
36 #include "type.h"
37 #include "filer.h"
38 #include "display.h"
39 #include "pixmaps.h"
40 #include "dnd.h"
41 #include "bind.h"
42 #include "gui_support.h"
43 #include "menu.h"
44 #include "options.h"
45 #include "cell_icon.h"
47 /* These are the column numbers in the ListStore */
48 #define COL_LEAF 0
49 #define COL_TYPE 1
50 #define COL_PERM 2
51 #define COL_OWNER 3
52 #define COL_GROUP 4
53 #define COL_SIZE 5
54 #define COL_MTIME 6
55 #define COL_ITEM 7
56 #define COL_COLOUR 8
57 #define COL_ICON 9
58 #define COL_BG_COLOUR 10
59 #define N_COLUMNS 11
61 static gpointer parent_class = NULL;
63 struct _ViewDetailsClass {
64 GtkTreeViewClass parent;
67 typedef struct _ViewItem ViewItem;
69 struct _ViewItem {
70 DirItem *item;
71 GdkPixbuf *image;
72 int old_pos; /* Used while sorting */
73 gboolean selected;
74 gchar *utf8_name; /* NULL => leafname is valid */
77 typedef struct _ViewDetails ViewDetails;
79 struct _ViewDetails {
80 GtkTreeView treeview;
82 FilerWindow *filer_window; /* Used for styles, etc */
84 GPtrArray *items; /* ViewItem */
86 gint sort_column_id;
87 GtkSortType order;
88 int (*sort_fn)(const void *, const void *);
90 int cursor_base; /* Cursor when minibuffer opened */
93 /* Static prototypes */
94 static void view_details_finialize(GObject *object);
95 static void view_details_class_init(gpointer gclass, gpointer data);
96 static void view_details_init(GTypeInstance *object, gpointer gclass);
98 static void view_details_iface_init(gpointer giface, gpointer iface_data);
100 static void view_details_sort(ViewIface *view);
101 static void view_details_style_changed(ViewIface *view, int flags);
102 static gboolean view_details_autoselect(ViewIface *view, const gchar *leaf);
103 static void view_details_add_items(ViewIface *view, GPtrArray *items);
104 static void view_details_update_items(ViewIface *view, GPtrArray *items);
105 static void view_details_delete_if(ViewIface *view,
106 gboolean (*test)(gpointer item, gpointer data),
107 gpointer data);
108 static void view_details_clear(ViewIface *view);
109 static void view_details_select_all(ViewIface *view);
110 static void view_details_clear_selection(ViewIface *view);
111 static int view_details_count_items(ViewIface *view);
112 static int view_details_count_selected(ViewIface *view);
113 static void view_details_show_cursor(ViewIface *view);
114 static void view_details_get_iter(ViewIface *view,
115 ViewIter *iter, IterFlags flags);
116 static void view_details_get_iter_at_point(ViewIface *view, ViewIter *iter,
117 int x, int y);
118 static void view_details_cursor_to_iter(ViewIface *view, ViewIter *iter);
119 static void view_details_set_selected(ViewIface *view,
120 ViewIter *iter,
121 gboolean selected);
122 static gboolean view_details_get_selected(ViewIface *view, ViewIter *iter);
123 static void view_details_select_only(ViewIface *view, ViewIter *iter);
124 static void view_details_set_frozen(ViewIface *view, gboolean frozen);
125 static void view_details_wink_item(ViewIface *view, ViewIter *iter);
126 static void view_details_autosize(ViewIface *view);
127 static gboolean view_details_cursor_visible(ViewIface *view);
128 static void view_details_set_base(ViewIface *view, ViewIter *iter);
129 static void view_details_start_lasso_box(ViewIface *view,
130 GdkEventButton *event);
131 static void view_details_extend_tip(ViewIface *view,
132 ViewIter *iter, GString *tip);
134 static DirItem *iter_peek(ViewIter *iter);
135 static DirItem *iter_prev(ViewIter *iter);
136 static DirItem *iter_next(ViewIter *iter);
137 static void make_iter(ViewDetails *view_details, ViewIter *iter,
138 IterFlags flags);
139 static void make_item_iter(ViewDetails *view_details, ViewIter *iter, int i);
140 static void view_details_tree_model_init(GtkTreeModelIface *iface);
141 static gboolean details_get_sort_column_id(GtkTreeSortable *sortable,
142 gint *sort_column_id,
143 GtkSortType *order);
144 static void details_set_sort_column_id(GtkTreeSortable *sortable,
145 gint sort_column_id,
146 GtkSortType order);
147 static void details_set_sort_func(GtkTreeSortable *sortable,
148 gint sort_column_id,
149 GtkTreeIterCompareFunc func,
150 gpointer data,
151 GtkDestroyNotify destroy);
152 static void details_set_default_sort_func(GtkTreeSortable *sortable,
153 GtkTreeIterCompareFunc func,
154 gpointer data,
155 GtkDestroyNotify destroy);
156 static gboolean details_has_default_sort_func(GtkTreeSortable *sortable);
157 static void view_details_sortable_init(GtkTreeSortableIface *iface);
158 static void set_selected(ViewDetails *view_details, int i, gboolean selected);
159 static gboolean get_selected(ViewDetails *view_details, int i);
160 static void free_view_item(ViewItem *view_item);
163 /****************************************************************
164 * EXTERNAL INTERFACE *
165 ****************************************************************/
167 GtkWidget *view_details_new(FilerWindow *filer_window)
169 ViewDetails *view_details;
171 view_details = g_object_new(view_details_get_type(), NULL);
172 view_details->filer_window = filer_window;
174 gtk_range_set_adjustment(GTK_RANGE(filer_window->scrollbar),
175 gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(view_details)));
177 return GTK_WIDGET(view_details);
180 GType view_details_get_type(void)
182 static GType type = 0;
184 if (!type)
186 static const GTypeInfo info =
188 sizeof (ViewDetailsClass),
189 NULL, /* base_init */
190 NULL, /* base_finalise */
191 view_details_class_init,
192 NULL, /* class_finalise */
193 NULL, /* class_data */
194 sizeof(ViewDetails),
195 0, /* n_preallocs */
196 view_details_init
198 static const GInterfaceInfo view_iface_info = {
199 view_details_iface_init,
200 NULL, NULL
202 static const GInterfaceInfo tree_model_info = {
203 (GInterfaceInitFunc) view_details_tree_model_init,
204 NULL, NULL
206 static const GInterfaceInfo sortable_info = {
207 (GInterfaceInitFunc) view_details_sortable_init,
208 NULL, NULL
212 type = g_type_register_static(gtk_tree_view_get_type(),
213 "ViewDetails", &info, 0);
215 g_type_add_interface_static(type, VIEW_TYPE_IFACE,
216 &view_iface_info);
217 g_type_add_interface_static(type, GTK_TYPE_TREE_MODEL,
218 &tree_model_info);
219 g_type_add_interface_static(type, GTK_TYPE_TREE_SORTABLE,
220 &sortable_info);
223 return type;
226 /****************************************************************
227 * INTERNAL FUNCTIONS *
228 ****************************************************************/
230 /* Fulfill the GtkTreeModel requirements */
231 static guint details_get_flags(GtkTreeModel *tree_model)
233 return GTK_TREE_MODEL_LIST_ONLY;
236 static gint details_get_n_columns(GtkTreeModel *tree_model)
238 return N_COLUMNS;
241 static GType details_get_column_type(GtkTreeModel *tree_model, gint index)
243 g_return_val_if_fail(index < N_COLUMNS && index >= 0, G_TYPE_INVALID);
245 if (index == COL_COLOUR || index == COL_BG_COLOUR)
246 return GDK_TYPE_COLOR;
247 else if (index == COL_ITEM)
248 return G_TYPE_POINTER;
249 else if (index == COL_ICON)
250 return GDK_TYPE_PIXBUF;
251 return G_TYPE_STRING;
254 static gboolean details_get_iter(GtkTreeModel *tree_model,
255 GtkTreeIter *iter,
256 GtkTreePath *path)
258 ViewDetails *view_details = (ViewDetails *) tree_model;
259 gint i;
261 g_return_val_if_fail(gtk_tree_path_get_depth (path) > 0, FALSE);
263 i = gtk_tree_path_get_indices(path)[0];
265 if (i >= view_details->items->len)
266 return FALSE;
268 iter->user_data = GINT_TO_POINTER(i);
270 return TRUE;
273 static GtkTreePath *details_get_path(GtkTreeModel *tree_model,
274 GtkTreeIter *iter)
276 GtkTreePath *retval;
278 retval = gtk_tree_path_new();
279 gtk_tree_path_append_index(retval, GPOINTER_TO_INT(iter->user_data));
281 return retval;
284 static void details_get_value(GtkTreeModel *tree_model,
285 GtkTreeIter *iter,
286 gint column,
287 GValue *value)
289 ViewDetails *view_details = (ViewDetails *) tree_model;
290 GtkStyle *style = ((GtkWidget *) tree_model)->style;
291 gint i;
292 GPtrArray *items = view_details->items;
293 ViewItem *view_item;
294 DirItem *item;
295 mode_t m;
297 g_return_if_fail(column >= 0 && column < N_COLUMNS);
299 i = GPOINTER_TO_INT(iter->user_data);
300 g_return_if_fail(i >= 0 && i < items->len);
301 view_item = (ViewItem *) items->pdata[i];
302 item = view_item->item;
304 if (column == COL_LEAF)
306 g_value_init(value, G_TYPE_STRING);
307 g_value_set_string(value,
308 view_item->utf8_name ? view_item->utf8_name
309 : item->leafname);
310 return;
312 else if (column == COL_ITEM)
314 g_value_init(value, G_TYPE_POINTER);
315 g_value_set_pointer(value, item);
316 return;
319 if (item->base_type == TYPE_UNKNOWN)
321 GType type;
322 type = details_get_column_type(tree_model, column);
323 g_value_init(value, type);
324 if (type == G_TYPE_STRING)
325 g_value_set_string(value, "");
326 else if (type == GDK_TYPE_COLOR)
327 g_value_set_boxed(value, NULL);
328 else
329 g_value_set_object(value, NULL);
331 return;
333 m = item->mode;
335 switch (column)
337 case COL_LEAF:
338 g_value_init(value, G_TYPE_STRING);
339 g_value_set_string(value, item->leafname);
340 break;
341 case COL_ICON:
342 g_value_init(value, GDK_TYPE_PIXBUF);
343 if (!item->image->sm_pixbuf)
344 pixmap_make_small(item->image);
345 g_value_set_object(value, item->image->sm_pixbuf);
346 break;
347 case COL_COLOUR:
348 g_value_init(value, GDK_TYPE_COLOR);
349 if (view_item->utf8_name)
351 GdkColor red;
352 red.red = 0xffff;
353 red.green = 0;
354 red.blue = 0;
355 g_value_set_boxed(value, &red);
357 else
358 g_value_set_boxed(value,
359 type_get_colour(item, NULL));
360 break;
361 case COL_BG_COLOUR:
362 g_value_init(value, GDK_TYPE_COLOR);
363 g_value_set_boxed(value, view_item->selected
364 ? &style->base[GTK_STATE_SELECTED]
365 : NULL);
366 break;
367 case COL_OWNER:
368 g_value_init(value, G_TYPE_STRING);
369 g_value_set_string(value, user_name(item->uid));
370 break;
371 case COL_GROUP:
372 g_value_init(value, G_TYPE_STRING);
373 g_value_set_string(value, group_name(item->gid));
374 break;
375 case COL_MTIME:
377 gchar *time;
378 time = pretty_time(&item->mtime);
379 g_value_init(value, G_TYPE_STRING);
380 g_value_set_string(value, time);
381 g_free(time);
382 break;
384 case COL_PERM:
385 g_value_init(value, G_TYPE_STRING);
386 g_value_set_string(value, pretty_permissions(m));
387 break;
388 case COL_SIZE:
389 g_value_init(value, G_TYPE_STRING);
390 if (item->base_type != TYPE_DIRECTORY)
391 g_value_set_string(value,
392 format_size(item->size));
393 break;
394 case COL_TYPE:
395 g_value_init(value, G_TYPE_STRING);
396 g_value_set_string(value,
397 item->flags & ITEM_FLAG_APPDIR? "App" :
398 S_ISDIR(m) ? "Dir" :
399 S_ISCHR(m) ? "Char" :
400 S_ISBLK(m) ? "Blck" :
401 S_ISLNK(m) ? "Link" :
402 S_ISSOCK(m) ? "Sock" :
403 S_ISFIFO(m) ? "Pipe" :
404 S_ISDOOR(m) ? "Door" :
405 "File");
406 break;
407 default:
408 g_value_init(value, G_TYPE_STRING);
409 g_value_set_string(value, "Hello");
410 break;
414 static gboolean details_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter)
416 ViewDetails *view_details = (ViewDetails *) tree_model;
417 int i;
419 i = GPOINTER_TO_INT(iter->user_data) + 1;
420 iter->user_data = GINT_TO_POINTER(i);
422 return i < view_details->items->len;
425 static gboolean details_iter_children(GtkTreeModel *tree_model,
426 GtkTreeIter *iter,
427 GtkTreeIter *parent)
429 ViewDetails *view_details = (ViewDetails *) tree_model;
431 /* this is a list, nodes have no children */
432 if (parent)
433 return FALSE;
435 /* but if parent == NULL we return the list itself as children of the
436 * "root"
439 if (view_details->items->len)
441 iter->user_data = GINT_TO_POINTER(0);
442 return TRUE;
444 else
445 return FALSE;
448 static gboolean details_iter_has_child(GtkTreeModel *tree_model,
449 GtkTreeIter *iter)
451 return FALSE;
454 static gint details_iter_n_children(GtkTreeModel *tree_model, GtkTreeIter *iter)
456 ViewDetails *view_details = (ViewDetails *) tree_model;
458 if (iter == NULL)
459 return view_details->items->len;
461 return 0;
464 static gboolean details_iter_nth_child(GtkTreeModel *tree_model,
465 GtkTreeIter *iter,
466 GtkTreeIter *parent,
467 gint n)
469 ViewDetails *view_details = (ViewDetails *) tree_model;
471 if (parent)
472 return FALSE;
474 if (n >= 0 && n < view_details->items->len)
476 iter->user_data = GINT_TO_POINTER(n);
477 return TRUE;
479 else
480 return FALSE;
483 static gboolean details_iter_parent(GtkTreeModel *tree_model,
484 GtkTreeIter *iter,
485 GtkTreeIter *child)
487 return FALSE;
490 /* A ViewDetails is both a GtkTreeView and a GtkTreeModel.
491 * The following functions implement the model interface...
494 static void view_details_tree_model_init(GtkTreeModelIface *iface)
496 iface->get_flags = details_get_flags;
497 iface->get_n_columns = details_get_n_columns;
498 iface->get_column_type = details_get_column_type;
499 iface->get_iter = details_get_iter;
500 iface->get_path = details_get_path;
501 iface->get_value = details_get_value;
502 iface->iter_next = details_iter_next;
503 iface->iter_children = details_iter_children;
504 iface->iter_has_child = details_iter_has_child;
505 iface->iter_n_children = details_iter_n_children;
506 iface->iter_nth_child = details_iter_nth_child;
507 iface->iter_parent = details_iter_parent;
510 static void view_details_sortable_init(GtkTreeSortableIface *iface)
512 iface->get_sort_column_id = details_get_sort_column_id;
513 iface->set_sort_column_id = details_set_sort_column_id;
514 iface->set_sort_func = details_set_sort_func;
515 iface->set_default_sort_func = details_set_default_sort_func;
516 iface->has_default_sort_func = details_has_default_sort_func;
519 static gboolean details_get_sort_column_id(GtkTreeSortable *sortable,
520 gint *sort_column_id,
521 GtkSortType *order)
523 ViewDetails *view_details = (ViewDetails *) sortable;
525 if (view_details->sort_column_id == -1)
526 return FALSE;
528 if (sort_column_id)
529 *sort_column_id = view_details->sort_column_id;
530 if (order)
531 *order = view_details->order;
532 return TRUE;
535 static void details_set_sort_column_id(GtkTreeSortable *sortable,
536 gint sort_column_id,
537 GtkSortType order)
539 ViewDetails *view_details = (ViewDetails *) sortable;
541 if (view_details->sort_column_id == sort_column_id &&
542 view_details->order == order)
543 return;
545 view_details->sort_column_id = sort_column_id;
546 view_details->order = order;
548 switch (sort_column_id)
550 case COL_LEAF:
551 view_details->sort_fn = sort_by_name;
552 break;
553 case COL_SIZE:
554 view_details->sort_fn = sort_by_size;
555 break;
556 case COL_MTIME:
557 view_details->sort_fn = sort_by_date;
558 break;
559 case COL_TYPE:
560 view_details->sort_fn = sort_by_type;
561 break;
562 case COL_OWNER:
563 view_details->sort_fn = sort_by_owner;
564 break;
565 case COL_GROUP:
566 view_details->sort_fn = sort_by_group;
567 break;
568 default:
569 g_assert_not_reached();
572 view_details_sort((ViewIface *) view_details);
574 gtk_tree_sortable_sort_column_changed(sortable);
577 static void details_set_sort_func(GtkTreeSortable *sortable,
578 gint sort_column_id,
579 GtkTreeIterCompareFunc func,
580 gpointer data,
581 GtkDestroyNotify destroy)
583 g_assert_not_reached();
586 static void details_set_default_sort_func(GtkTreeSortable *sortable,
587 GtkTreeIterCompareFunc func,
588 gpointer data,
589 GtkDestroyNotify destroy)
591 g_assert_not_reached();
594 static gboolean details_has_default_sort_func(GtkTreeSortable *sortable)
596 return FALSE;
600 /* End of model implementation */
602 static gboolean is_selected(ViewDetails *view_details, int i)
604 ViewIter iter;
605 iter.i = i;
606 return view_details_get_selected((ViewIface *) view_details, &iter);
609 static gboolean view_details_button_press(GtkWidget *widget,
610 GdkEventButton *bev)
612 FilerWindow *filer_window = ((ViewDetails *) widget)->filer_window;
614 if (dnd_motion_press(widget, bev))
615 filer_perform_action(filer_window, bev);
617 return TRUE;
620 static gboolean view_details_button_release(GtkWidget *widget,
621 GdkEventButton *bev)
623 FilerWindow *filer_window = ((ViewDetails *) widget)->filer_window;
625 if (!dnd_motion_release(bev))
626 filer_perform_action(filer_window, bev);
628 return TRUE;
631 static gint view_details_key_press_event(GtkWidget *widget, GdkEventKey *event)
633 ViewDetails *view_details = (ViewDetails *) widget;
634 FilerWindow *filer_window = view_details->filer_window;
636 switch (event->keyval)
638 case ' ':
639 filer_window_toggle_cursor_item_selected(filer_window);
640 return TRUE;
641 case GDK_BackSpace:
642 change_to_parent(filer_window);
643 return TRUE;
646 return GTK_WIDGET_CLASS(parent_class)->key_press_event(widget, event);
649 static gint view_details_motion_notify(GtkWidget *widget, GdkEventMotion *event)
651 ViewDetails *view_details = (ViewDetails *) widget;
653 return filer_motion_notify(view_details->filer_window, event);
656 static gboolean view_details_expose(GtkWidget *widget, GdkEventExpose *event)
658 GtkTreeView *tree = (GtkTreeView *) widget;
659 GtkTreePath *path = NULL;
660 GdkRectangle focus_rectangle;
661 gboolean had_cursor;
663 had_cursor = (GTK_WIDGET_FLAGS(widget) & GTK_HAS_FOCUS) != 0;
665 if (had_cursor)
666 GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS);
667 GTK_WIDGET_CLASS(parent_class)->expose_event(widget, event);
668 if (had_cursor)
669 GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
671 if (event->window != gtk_tree_view_get_bin_window(tree))
672 return FALSE; /* Not the main area */
674 gtk_tree_view_get_cursor(tree, &path, NULL);
675 if (!path)
676 return FALSE; /* No cursor */
677 gtk_tree_view_get_background_area(tree, path, NULL, &focus_rectangle);
678 gtk_tree_path_free(path);
680 if (!focus_rectangle.height)
681 return FALSE; /* Off screen */
683 focus_rectangle.width = widget->allocation.width;
685 gtk_paint_focus(widget->style,
686 event->window,
687 GTK_STATE_NORMAL,
688 NULL,
689 widget,
690 "treeview",
691 focus_rectangle.x,
692 focus_rectangle.y,
693 focus_rectangle.width,
694 focus_rectangle.height);
696 return FALSE;
699 static void view_details_destroy(GtkObject *view_details)
701 VIEW_DETAILS(view_details)->filer_window = NULL;
704 static void view_details_finialize(GObject *object)
706 ViewDetails *view_details = (ViewDetails *) object;
708 g_ptr_array_free(view_details->items, TRUE);
709 view_details->items = NULL;
711 G_OBJECT_CLASS(parent_class)->finalize(object);
714 static void view_details_class_init(gpointer gclass, gpointer data)
716 GObjectClass *object = (GObjectClass *) gclass;
717 GtkWidgetClass *widget = (GtkWidgetClass *) gclass;
719 parent_class = g_type_class_peek_parent(gclass);
721 object->finalize = view_details_finialize;
722 GTK_OBJECT_CLASS(object)->destroy = view_details_destroy;
724 widget->button_press_event = view_details_button_press;
725 widget->button_release_event = view_details_button_release;
726 widget->key_press_event = view_details_key_press_event;
727 widget->motion_notify_event = view_details_motion_notify;
728 widget->expose_event = view_details_expose;
731 static gboolean block_focus(GtkWidget *button, GtkDirectionType dir,
732 ViewDetails *view_details)
734 GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
735 return FALSE;
738 #define ADD_TEXT_COLUMN(name, model_column) \
739 cell = gtk_cell_renderer_text_new(); \
740 column = gtk_tree_view_column_new_with_attributes(name, cell, \
741 "text", model_column, \
742 "foreground-gdk", COL_COLOUR, \
743 "background-gdk", COL_BG_COLOUR, \
744 NULL); \
745 gtk_tree_view_append_column(treeview, column); \
746 g_signal_connect(column->button, "grab-focus", \
747 G_CALLBACK(block_focus), view_details);
749 static void view_details_init(GTypeInstance *object, gpointer gclass)
751 GtkTreeView *treeview = (GtkTreeView *) object;
752 GtkTreeViewColumn *column;
753 GtkCellRenderer *cell;
754 GtkTreeSortable *sortable_list;
755 GtkTreeSelection *selection;
756 ViewDetails *view_details = (ViewDetails *) object;
758 view_details->items = g_ptr_array_new();
759 view_details->cursor_base = -1;
761 selection = gtk_tree_view_get_selection(treeview);
762 gtk_tree_selection_set_mode(selection, GTK_SELECTION_NONE);
764 /* Sorting */
765 view_details->sort_column_id = -1;
766 view_details->sort_fn = NULL;
767 sortable_list = GTK_TREE_SORTABLE(object);
768 gtk_tree_sortable_set_sort_column_id(sortable_list, COL_LEAF,
769 GTK_SORT_ASCENDING);
771 gtk_tree_view_set_model(treeview, GTK_TREE_MODEL(view_details));
773 /* Icon */
774 cell = cell_icon_new();
775 column = gtk_tree_view_column_new_with_attributes(NULL, cell,
776 "item", COL_ITEM,
777 "background-gdk", COL_BG_COLOUR,
778 NULL);
779 gtk_tree_view_append_column(treeview, column);
781 ADD_TEXT_COLUMN(_("_Name"), COL_LEAF);
782 gtk_tree_view_column_set_sort_column_id(column, COL_LEAF);
783 ADD_TEXT_COLUMN(_("_Type"), COL_TYPE);
784 gtk_tree_view_column_set_sort_column_id(column, COL_TYPE);
785 ADD_TEXT_COLUMN(_("_Permissions"), COL_PERM);
786 ADD_TEXT_COLUMN(_("_Owner"), COL_OWNER);
787 gtk_tree_view_column_set_sort_column_id(column, COL_OWNER);
788 ADD_TEXT_COLUMN(_("_Group"), COL_GROUP);
789 gtk_tree_view_column_set_sort_column_id(column, COL_GROUP);
790 ADD_TEXT_COLUMN(_("_Size"), COL_SIZE);
791 gtk_tree_view_column_set_sort_column_id(column, COL_SIZE);
792 ADD_TEXT_COLUMN(_("Last _Modified"), COL_MTIME);
793 gtk_tree_view_column_set_sort_column_id(column, COL_MTIME);
795 gtk_widget_set_size_request(GTK_WIDGET(treeview), -1, 50);
798 /* Create the handers for the View interface */
799 static void view_details_iface_init(gpointer giface, gpointer iface_data)
801 ViewIfaceClass *iface = giface;
803 g_assert(G_TYPE_FROM_INTERFACE(iface) == VIEW_TYPE_IFACE);
805 /* override stuff */
806 iface->sort = view_details_sort;
807 iface->style_changed = view_details_style_changed;
808 iface->autoselect = view_details_autoselect;
809 iface->add_items = view_details_add_items;
810 iface->update_items = view_details_update_items;
811 iface->delete_if = view_details_delete_if;
812 iface->clear = view_details_clear;
813 iface->select_all = view_details_select_all;
814 iface->clear_selection = view_details_clear_selection;
815 iface->count_items = view_details_count_items;
816 iface->count_selected = view_details_count_selected;
817 iface->show_cursor = view_details_show_cursor;
818 iface->get_iter = view_details_get_iter;
819 iface->get_iter_at_point = view_details_get_iter_at_point;
820 iface->cursor_to_iter = view_details_cursor_to_iter;
821 iface->set_selected = view_details_set_selected;
822 iface->get_selected = view_details_get_selected;
823 iface->set_frozen = view_details_set_frozen;
824 iface->select_only = view_details_select_only;
825 iface->wink_item = view_details_wink_item;
826 iface->autosize = view_details_autosize;
827 iface->cursor_visible = view_details_cursor_visible;
828 iface->set_base = view_details_set_base;
829 iface->start_lasso_box = view_details_start_lasso_box;
830 iface->extend_tip = view_details_extend_tip;
833 /* Implementations of the View interface. See view_iface.c for comments. */
835 static void view_details_style_changed(ViewIface *view, int flags)
839 static gint wrap_sort(gconstpointer a, gconstpointer b,
840 ViewDetails *view_details)
842 ViewItem *ia = *(ViewItem **) a;
843 ViewItem *ib = *(ViewItem **) b;
845 if (view_details->order == GTK_SORT_ASCENDING)
846 return view_details->sort_fn(ia->item, ib->item);
847 else
848 return -view_details->sort_fn(ia->item, ib->item);
851 static void view_details_sort(ViewIface *view)
853 ViewDetails *view_details = (ViewDetails *) view;
854 ViewItem **items = (ViewItem **) view_details->items->pdata;
855 GArray *new_order;
856 gint i, len = view_details->items->len;
857 GtkTreePath *path;
859 g_return_if_fail(view_details->sort_fn != NULL);
861 if (!len)
862 return;
864 for (i = len - 1; i >= 0; i--)
865 items[i]->old_pos = i;
867 g_ptr_array_sort_with_data(view_details->items,
868 (GCompareDataFunc) wrap_sort,
869 view_details);
871 new_order = g_array_sized_new(FALSE, FALSE, sizeof(gint), len);
872 g_array_set_size(new_order, len);
874 for (i = len - 1; i >= 0; i--)
875 g_array_insert_val(new_order, items[i]->old_pos, i);
877 path = gtk_tree_path_new();
878 gtk_tree_model_rows_reordered((GtkTreeModel *) view,
879 path, NULL,
880 (gint *) new_order->data);
881 gtk_tree_path_free(path);
882 g_array_free(new_order, TRUE);
885 static gboolean view_details_autoselect(ViewIface *view, const gchar *leaf)
887 return FALSE;
890 static void view_details_add_items(ViewIface *view, GPtrArray *new_items)
892 ViewDetails *view_details = (ViewDetails *) view;
893 FilerWindow *filer_window = view_details->filer_window;
894 gboolean show_hidden = filer_window->show_hidden;
895 GPtrArray *items = view_details->items;
896 GtkTreeIter iter;
897 int i;
898 GtkTreePath *path;
899 GtkTreeModel *model = (GtkTreeModel *) view;
901 iter.user_data = GINT_TO_POINTER(items->len);
902 path = details_get_path(model, &iter);
904 for (i = 0; i < new_items->len; i++)
906 DirItem *item = (DirItem *) new_items->pdata[i];
907 char *leafname = item->leafname;
908 ViewItem *vitem;
910 if (leafname[0] == '.')
912 if (!show_hidden)
913 continue;
915 if (leafname[1] == '\0')
916 continue; /* Never show '.' */
918 if (leafname[1] == '.' &&
919 leafname[2] == '\0')
920 continue; /* Never show '..' */
923 vitem = g_new(ViewItem, 1);
924 vitem->item = item;
925 vitem->image = NULL;
926 vitem->selected = FALSE;
927 if (!g_utf8_validate(leafname, -1, NULL))
928 vitem->utf8_name = to_utf8(leafname);
929 else
930 vitem->utf8_name = NULL;
932 g_ptr_array_add(items, vitem);
934 iter.user_data = GINT_TO_POINTER(items->len - 1);
935 gtk_tree_model_row_inserted(model, path, &iter);
936 gtk_tree_path_next(path);
939 gtk_tree_path_free(path);
941 view_details_sort(view);
944 /* Find an item in the sorted array.
945 * Returns the item number, or -1 if not found.
947 static int details_find_item(ViewDetails *view_details, DirItem *item)
949 ViewItem **items, tmp, *tmpp;
950 int lower, upper;
951 int (*compar)(const void *, const void *);
953 g_return_val_if_fail(view_details != NULL, -1);
954 g_return_val_if_fail(item != NULL, -1);
956 tmp.item = item;
957 tmpp = &tmp;
959 items = (ViewItem **) view_details->items->pdata;
960 compar = view_details->sort_fn;
962 g_return_val_if_fail(compar != NULL, -1);
964 /* If item is here, then: lower <= i < upper */
965 lower = 0;
966 upper = view_details->items->len;
968 while (lower < upper)
970 int i, cmp;
972 i = (lower + upper) >> 1;
974 cmp = wrap_sort(&items[i], &tmpp, view_details);
975 if (cmp == 0)
976 return i;
978 if (cmp > 0)
979 upper = i;
980 else
981 lower = i + 1;
984 return -1;
987 static void view_details_update_items(ViewIface *view, GPtrArray *items)
989 ViewDetails *view_details = (ViewDetails *) view;
990 FilerWindow *filer_window = view_details->filer_window;
991 int i;
992 GtkTreeModel *model = (GtkTreeModel *) view_details;
994 g_return_if_fail(items->len > 0);
996 /* The item data has already been modified, so this gives the
997 * final sort order...
999 view_details_sort(view);
1001 for (i = 0; i < items->len; i++)
1003 DirItem *item = (DirItem *) items->pdata[i];
1004 const gchar *leafname = item->leafname;
1005 int j;
1007 if (leafname[0] == '.' && filer_window->show_hidden == FALSE)
1008 continue;
1010 j = details_find_item(view_details, item);
1012 if (j < 0)
1013 g_warning("Failed to find '%s'\n", leafname);
1014 else
1016 GtkTreePath *path;
1017 GtkTreeIter iter;
1018 path = gtk_tree_path_new();
1019 gtk_tree_path_append_index(path, j);
1020 iter.user_data = GINT_TO_POINTER(j);
1021 gtk_tree_model_row_changed(model, path, &iter);
1026 static void view_details_delete_if(ViewIface *view,
1027 gboolean (*test)(gpointer item, gpointer data),
1028 gpointer data)
1030 GtkTreePath *path;
1031 ViewDetails *view_details = (ViewDetails *) view;
1032 int i = 0;
1033 GPtrArray *items = view_details->items;
1034 GtkTreeModel *model = (GtkTreeModel *) view;
1036 path = gtk_tree_path_new();
1038 gtk_tree_path_append_index(path, i);
1040 while (i < items->len)
1042 ViewItem *item = items->pdata[i];
1044 if (test(item->item, data))
1046 free_view_item(items->pdata[i]);
1047 g_ptr_array_remove_index(items, i);
1048 gtk_tree_model_row_deleted(model, path);
1050 else
1052 i++;
1053 gtk_tree_path_next(path);
1057 gtk_tree_path_free(path);
1060 static void view_details_clear(ViewIface *view)
1062 GtkTreePath *path;
1063 GPtrArray *items = ((ViewDetails *) view)->items;
1064 GtkTreeModel *model = (GtkTreeModel *) view;
1066 path = gtk_tree_path_new();
1067 gtk_tree_path_append_index(path, items->len);
1069 while (gtk_tree_path_prev(path))
1070 gtk_tree_model_row_deleted(model, path);
1072 g_ptr_array_set_size(items, 0);
1073 gtk_tree_path_free(path);
1076 static void view_details_select_all(ViewIface *view)
1078 int i;
1079 int n = ((ViewDetails *) view)->items->len;
1081 for (i = 0; i < n; i++)
1083 ViewIter iter;
1084 iter.i = i;
1085 view_details_set_selected(view, &iter, TRUE);
1089 static void view_details_clear_selection(ViewIface *view)
1091 int i;
1092 int n = ((ViewDetails *) view)->items->len;
1094 for (i = 0; i < n; i++)
1096 ViewIter iter;
1097 iter.i = i;
1098 view_details_set_selected(view, &iter, FALSE);
1102 static int view_details_count_items(ViewIface *view)
1104 ViewDetails *view_details = (ViewDetails *) view;
1106 return view_details->items->len;
1109 static int view_details_count_selected(ViewIface *view)
1111 ViewDetails *view_details = (ViewDetails *) view;
1112 int n = view_details->items->len;
1113 ViewItem **items = (ViewItem **) view_details->items->pdata;
1114 int count = 0;
1115 int i;
1117 for (i = 0; i < n; i++)
1118 if (items[i]->selected)
1119 count++;
1121 return count;
1124 static void view_details_show_cursor(ViewIface *view)
1128 static void view_details_get_iter(ViewIface *view,
1129 ViewIter *iter, IterFlags flags)
1131 make_iter((ViewDetails *) view, iter, flags);
1134 static void view_details_get_iter_at_point(ViewIface *view, ViewIter *iter,
1135 int x, int y)
1137 ViewDetails *view_details = (ViewDetails *) view;
1138 GtkTreeModel *model;
1139 GtkTreeView *tree = (GtkTreeView *) view;
1140 GtkTreePath *path = NULL;
1141 int i = -1;
1143 model = gtk_tree_view_get_model(tree);
1145 if (gtk_tree_view_get_path_at_pos(tree, x, y, &path, NULL, NULL, NULL))
1147 g_return_if_fail(path != NULL);
1149 i = gtk_tree_path_get_indices(path)[0];
1150 gtk_tree_path_free(path);
1153 make_item_iter(view_details, iter, i);
1156 static void view_details_cursor_to_iter(ViewIface *view, ViewIter *iter)
1158 GtkTreePath *path;
1159 ViewDetails *view_details = (ViewDetails *) view;
1161 path = gtk_tree_path_new();
1163 if (iter)
1164 gtk_tree_path_append_index(path, iter->i);
1165 else
1167 /* Using depth zero or index -1 gives an error, but this
1168 * is OK!
1170 gtk_tree_path_append_index(path, view_details->items->len);
1173 gtk_tree_view_set_cursor((GtkTreeView *) view, path, NULL, FALSE);
1174 gtk_tree_path_free(path);
1177 static void set_selected(ViewDetails *view_details, int i, gboolean selected)
1179 GtkTreeModel *model = (GtkTreeModel *) view_details;
1180 GtkTreeIter t_iter;
1181 GtkTreePath *path;
1182 GPtrArray *items = view_details->items;
1183 ViewItem *view_item;
1185 g_return_if_fail(i >= 0 && i < items->len);
1186 view_item = (ViewItem *) items->pdata[i];
1188 if (view_item->selected == selected)
1189 return;
1191 view_item->selected = selected;
1193 path = gtk_tree_path_new();
1194 gtk_tree_path_append_index(path, i);
1195 t_iter.user_data = GINT_TO_POINTER(i);
1196 gtk_tree_model_row_changed(model, path, &t_iter);
1197 gtk_tree_path_free(path);
1200 static void view_details_set_selected(ViewIface *view,
1201 ViewIter *iter,
1202 gboolean selected)
1204 set_selected((ViewDetails *) view, iter->i, selected);
1207 static gboolean get_selected(ViewDetails *view_details, int i)
1209 GPtrArray *items = view_details->items;
1211 g_return_val_if_fail(i >= 0 && i < items->len, FALSE);
1213 return ((ViewItem *) items->pdata[i])->selected;
1216 static gboolean view_details_get_selected(ViewIface *view, ViewIter *iter)
1218 return get_selected((ViewDetails *) view, iter->i);
1221 static void view_details_select_only(ViewIface *view, ViewIter *iter)
1223 ViewDetails *view_details = (ViewDetails *) view;
1224 int i = iter->i;
1225 int n = view_details->items->len;
1227 set_selected(view_details, i, TRUE);
1229 while (n > 0)
1231 n--;
1233 if (n != i)
1234 set_selected(view_details, n, FALSE);
1238 static void view_details_set_frozen(ViewIface *view, gboolean frozen)
1242 static void view_details_wink_item(ViewIface *view, ViewIter *iter)
1246 static void view_details_autosize(ViewIface *view)
1248 ViewDetails *view_details = (ViewDetails *) view;
1249 FilerWindow *filer_window = view_details->filer_window;
1250 GdkWindow *bin;
1251 int max_height = (o_filer_size_limit.int_value * screen_height) / 100;
1252 int h, y;
1253 GtkTreeView *tree = (GtkTreeView *) view;
1254 GtkTreeViewColumn *column;
1255 GtkRequisition req;
1257 gtk_widget_size_request(GTK_WIDGET(view), &req);
1258 column = gtk_tree_view_get_column(tree, 1);
1259 gtk_tree_view_column_cell_get_size(column, NULL, NULL, NULL, NULL, &h);
1261 bin = gtk_tree_view_get_bin_window(GTK_TREE_VIEW(view));
1263 gdk_window_get_position(bin, NULL, &y);
1265 h = MAX(h, SMALL_HEIGHT);
1267 h = (view_details->items->len + 2) * h + y;
1269 h = MIN(h, max_height);
1271 filer_window_set_size(filer_window, 5, h);
1274 static gboolean view_details_cursor_visible(ViewIface *view)
1276 return FALSE;
1279 static void view_details_set_base(ViewIface *view, ViewIter *iter)
1281 ViewDetails *view_details = (ViewDetails *) view;
1283 view_details->cursor_base = iter->i;
1286 static void view_details_start_lasso_box(ViewIface *view, GdkEventButton *event)
1290 static void view_details_extend_tip(ViewIface *view,
1291 ViewIter *iter, GString *tip)
1295 static DirItem *iter_init(ViewIter *iter)
1297 ViewDetails *view_details = (ViewDetails *) iter->view;
1298 int i = -1;
1299 int n = view_details->items->len;
1300 int flags = iter->flags;
1302 iter->peek = iter_peek;
1304 if (iter->n_remaining == 0)
1305 return NULL;
1307 if (flags & VIEW_ITER_FROM_CURSOR)
1309 GtkTreePath *path;
1310 gtk_tree_view_get_cursor((GtkTreeView *) view_details,
1311 &path, NULL);
1312 if (!path)
1313 return NULL; /* No cursor */
1314 i = gtk_tree_path_get_indices(path)[0];
1315 gtk_tree_path_free(path);
1317 else if (flags & VIEW_ITER_FROM_BASE)
1318 i = view_details->cursor_base;
1320 if (i < 0 || i >= n)
1322 /* Either a normal iteration, or an iteration from an
1323 * invalid starting point.
1325 if (flags & VIEW_ITER_BACKWARDS)
1326 i = n - 1;
1327 else
1328 i = 0;
1331 if (i < 0 || i >= n)
1332 return NULL; /* No items at all! */
1334 iter->next = flags & VIEW_ITER_BACKWARDS ? iter_prev : iter_next;
1335 iter->n_remaining--;
1336 iter->i = i;
1338 if (flags & VIEW_ITER_SELECTED && !is_selected(view_details, i))
1339 return iter->next(iter);
1340 return iter->peek(iter);
1343 static DirItem *iter_prev(ViewIter *iter)
1345 ViewDetails *view_details = (ViewDetails *) iter->view;
1346 int n = view_details->items->len;
1347 int i = iter->i;
1349 g_return_val_if_fail(iter->n_remaining >= 0, NULL);
1351 /* i is the last item returned (always valid) */
1353 g_return_val_if_fail(i >= 0 && i < n, NULL);
1355 while (iter->n_remaining)
1357 i--;
1358 iter->n_remaining--;
1360 if (i == -1)
1361 i = n - 1;
1363 g_return_val_if_fail(i >= 0 && i < n, NULL);
1365 if (iter->flags & VIEW_ITER_SELECTED &&
1366 !is_selected(view_details, i))
1367 continue;
1369 iter->i = i;
1370 return ((ViewItem *) view_details->items->pdata[i])->item;
1373 iter->i = -1;
1374 return NULL;
1377 static DirItem *iter_next(ViewIter *iter)
1379 ViewDetails *view_details = (ViewDetails *) iter->view;
1380 int n = view_details->items->len;
1381 int i = iter->i;
1383 g_return_val_if_fail(iter->n_remaining >= 0, NULL);
1385 /* i is the last item returned (always valid) */
1387 g_return_val_if_fail(i >= 0 && i < n, NULL);
1389 while (iter->n_remaining)
1391 i++;
1392 iter->n_remaining--;
1394 if (i == n)
1395 i = 0;
1397 g_return_val_if_fail(i >= 0 && i < n, NULL);
1399 if (iter->flags & VIEW_ITER_SELECTED &&
1400 !is_selected(view_details, i))
1401 continue;
1403 iter->i = i;
1404 return ((ViewItem *) view_details->items->pdata[i])->item;
1407 iter->i = -1;
1408 return NULL;
1411 static DirItem *iter_peek(ViewIter *iter)
1413 ViewDetails *view_details = (ViewDetails *) iter->view;
1414 int n = view_details->items->len;
1415 int i = iter->i;
1417 if (i == -1)
1418 return NULL;
1420 g_return_val_if_fail(i >= 0 && i < n, NULL);
1422 return ((ViewItem *) view_details->items->pdata[i])->item;
1425 /* Set the iterator to return 'i' on the next peek().
1426 * If i is -1, returns NULL on next peek().
1428 static void make_item_iter(ViewDetails *view_details, ViewIter *iter, int i)
1430 make_iter(view_details, iter, 0);
1432 g_return_if_fail(i >= -1 && i < (int) view_details->items->len);
1434 iter->i = i;
1435 iter->next = iter_next;
1436 iter->peek = iter_peek;
1437 iter->n_remaining = 0;
1440 static void make_iter(ViewDetails *view_details, ViewIter *iter,
1441 IterFlags flags)
1443 iter->view = (ViewIface *) view_details;
1444 iter->next = iter_init;
1445 iter->peek = NULL;
1446 iter->i = -1;
1448 iter->flags = flags;
1450 if (flags & VIEW_ITER_ONE_ONLY)
1452 iter->n_remaining = 1;
1453 iter->next(iter);
1455 else
1456 iter->n_remaining = view_details->items->len;
1459 static void free_view_item(ViewItem *view_item)
1461 g_free(view_item->utf8_name);
1462 g_free(view_item);