r4351: The monospace font for the list view is now configurable via
[rox-filer.git] / ROX-Filer / src / view_details.c
blob02d9d872c756be2035f42a2dc92b017a4b4f30b7
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2005, 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_WEIGHT 9
58 #define COL_VIEW_ITEM 10
59 #define N_COLUMNS 11
61 static gpointer parent_class = NULL;
63 struct _ViewDetailsClass {
64 GtkTreeViewClass parent;
67 /* Static prototypes */
68 static void view_details_finialize(GObject *object);
69 static void view_details_class_init(gpointer gclass, gpointer data);
70 static void view_details_init(GTypeInstance *object, gpointer gclass);
72 static void view_details_iface_init(gpointer giface, gpointer iface_data);
74 static void view_details_sort(ViewIface *view);
75 static void view_details_style_changed(ViewIface *view, int flags);
76 static void view_details_add_items(ViewIface *view, GPtrArray *items);
77 static void view_details_update_items(ViewIface *view, GPtrArray *items);
78 static void view_details_delete_if(ViewIface *view,
79 gboolean (*test)(gpointer item, gpointer data),
80 gpointer data);
81 static void view_details_clear(ViewIface *view);
82 static void view_details_select_all(ViewIface *view);
83 static void view_details_clear_selection(ViewIface *view);
84 static int view_details_count_items(ViewIface *view);
85 static int view_details_count_selected(ViewIface *view);
86 static void view_details_show_cursor(ViewIface *view);
87 static void view_details_get_iter(ViewIface *view,
88 ViewIter *iter, IterFlags flags);
89 static void view_details_get_iter_at_point(ViewIface *view, ViewIter *iter,
90 GdkWindow *src, int x, int y);
91 static void view_details_cursor_to_iter(ViewIface *view, ViewIter *iter);
92 static void view_details_set_selected(ViewIface *view,
93 ViewIter *iter,
94 gboolean selected);
95 static gboolean view_details_get_selected(ViewIface *view, ViewIter *iter);
96 static void view_details_select_only(ViewIface *view, ViewIter *iter);
97 static void view_details_set_frozen(ViewIface *view, gboolean frozen);
98 static void view_details_wink_item(ViewIface *view, ViewIter *iter);
99 static void view_details_autosize(ViewIface *view);
100 static gboolean view_details_cursor_visible(ViewIface *view);
101 static void view_details_set_base(ViewIface *view, ViewIter *iter);
102 static void view_details_start_lasso_box(ViewIface *view,
103 GdkEventButton *event);
104 static void view_details_extend_tip(ViewIface *view,
105 ViewIter *iter, GString *tip);
106 static gboolean view_details_auto_scroll_callback(ViewIface *view);
108 static DirItem *iter_peek(ViewIter *iter);
109 static DirItem *iter_prev(ViewIter *iter);
110 static DirItem *iter_next(ViewIter *iter);
111 static void make_iter(ViewDetails *view_details, ViewIter *iter,
112 IterFlags flags);
113 static void make_item_iter(ViewDetails *view_details, ViewIter *iter, int i);
114 static void view_details_tree_model_init(GtkTreeModelIface *iface);
115 static gboolean details_get_sort_column_id(GtkTreeSortable *sortable,
116 gint *sort_column_id,
117 GtkSortType *order);
118 static void details_set_sort_column_id(GtkTreeSortable *sortable,
119 gint sort_column_id,
120 GtkSortType order);
121 static void details_set_sort_func(GtkTreeSortable *sortable,
122 gint sort_column_id,
123 GtkTreeIterCompareFunc func,
124 gpointer data,
125 GtkDestroyNotify destroy);
126 static void details_set_default_sort_func(GtkTreeSortable *sortable,
127 GtkTreeIterCompareFunc func,
128 gpointer data,
129 GtkDestroyNotify destroy);
130 static gboolean details_has_default_sort_func(GtkTreeSortable *sortable);
131 static void view_details_sortable_init(GtkTreeSortableIface *iface);
132 static void set_selected(ViewDetails *view_details, int i, gboolean selected);
133 static gboolean get_selected(ViewDetails *view_details, int i);
134 static void free_view_item(ViewItem *view_item);
135 static void details_update_header_visibility(ViewDetails *view_details);
136 static void set_lasso(ViewDetails *view_details, int x, int y);
137 static void cancel_wink(ViewDetails *view_details);
140 /****************************************************************
141 * EXTERNAL INTERFACE *
142 ****************************************************************/
144 GtkWidget *view_details_new(FilerWindow *filer_window)
146 ViewDetails *view_details;
148 view_details = g_object_new(view_details_get_type(), NULL);
149 view_details->filer_window = filer_window;
151 gtk_range_set_adjustment(GTK_RANGE(filer_window->scrollbar),
152 gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(view_details)));
154 if (filer_window->sort_type != -1)
155 view_details_sort((ViewIface *) view_details);
157 details_update_header_visibility(view_details);
159 return GTK_WIDGET(view_details);
162 GType view_details_get_type(void)
164 static GType type = 0;
166 if (!type)
168 static const GTypeInfo info =
170 sizeof (ViewDetailsClass),
171 NULL, /* base_init */
172 NULL, /* base_finalise */
173 view_details_class_init,
174 NULL, /* class_finalise */
175 NULL, /* class_data */
176 sizeof(ViewDetails),
177 0, /* n_preallocs */
178 view_details_init
180 static const GInterfaceInfo view_iface_info = {
181 view_details_iface_init,
182 NULL, NULL
184 static const GInterfaceInfo tree_model_info = {
185 (GInterfaceInitFunc) view_details_tree_model_init,
186 NULL, NULL
188 static const GInterfaceInfo sortable_info = {
189 (GInterfaceInitFunc) view_details_sortable_init,
190 NULL, NULL
194 type = g_type_register_static(gtk_tree_view_get_type(),
195 "ViewDetails", &info, 0);
197 g_type_add_interface_static(type, VIEW_TYPE_IFACE,
198 &view_iface_info);
199 g_type_add_interface_static(type, GTK_TYPE_TREE_MODEL,
200 &tree_model_info);
201 g_type_add_interface_static(type, GTK_TYPE_TREE_SORTABLE,
202 &sortable_info);
205 return type;
208 /****************************************************************
209 * INTERNAL FUNCTIONS *
210 ****************************************************************/
212 /* Update the visibility of the list headers */
213 static void details_update_header_visibility(ViewDetails *view_details)
215 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view_details),
216 o_display_show_headers.int_value);
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)
235 return GDK_TYPE_COLOR;
236 else if (index == COL_ITEM || index == COL_VIEW_ITEM)
237 return G_TYPE_POINTER;
238 else if (index == COL_WEIGHT)
239 return G_TYPE_INT;
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 gint i;
280 GPtrArray *items = view_details->items;
281 ViewItem *view_item;
282 DirItem *item;
283 mode_t m;
285 g_return_if_fail(column >= 0 && column < N_COLUMNS);
287 i = GPOINTER_TO_INT(iter->user_data);
288 g_return_if_fail(i >= 0 && i < items->len);
289 view_item = (ViewItem *) items->pdata[i];
290 item = view_item->item;
292 if (column == COL_LEAF)
294 g_value_init(value, G_TYPE_STRING);
295 g_value_set_string(value,
296 view_item->utf8_name ? view_item->utf8_name
297 : item->leafname);
298 return;
300 else if (column == COL_VIEW_ITEM)
302 g_value_init(value, G_TYPE_POINTER);
303 g_value_set_pointer(value, view_item);
304 return;
306 else if (column == COL_ITEM)
308 g_value_init(value, G_TYPE_POINTER);
309 g_value_set_pointer(value, item);
310 return;
313 if (item->base_type == TYPE_UNKNOWN)
315 GType type;
316 type = details_get_column_type(tree_model, column);
317 g_value_init(value, type);
318 if (type == G_TYPE_STRING)
319 g_value_set_string(value, "");
320 else if (type == GDK_TYPE_COLOR)
321 g_value_set_boxed(value, NULL);
322 else if (type == G_TYPE_INT)
323 g_value_set_int(value, PANGO_WEIGHT_NORMAL);
324 else
325 g_value_set_object(value, NULL);
327 return;
329 m = item->mode;
331 switch (column)
333 case COL_LEAF:
334 g_value_init(value, G_TYPE_STRING);
335 g_value_set_string(value, item->leafname);
336 break;
337 case COL_COLOUR:
338 g_value_init(value, GDK_TYPE_COLOR);
339 if (view_item->utf8_name)
341 GdkColor red;
342 red.red = 0xffff;
343 red.green = 0;
344 red.blue = 0;
345 g_value_set_boxed(value, &red);
347 else
348 g_value_set_boxed(value,
349 type_get_colour(item, NULL));
350 break;
351 case COL_OWNER:
352 g_value_init(value, G_TYPE_STRING);
353 g_value_set_string(value, user_name(item->uid));
354 break;
355 case COL_GROUP:
356 g_value_init(value, G_TYPE_STRING);
357 g_value_set_string(value, group_name(item->gid));
358 break;
359 case COL_MTIME:
361 gchar *time;
362 time = pretty_time(&item->mtime);
363 g_value_init(value, G_TYPE_STRING);
364 g_value_set_string(value, time);
365 g_free(time);
366 break;
368 case COL_PERM:
369 g_value_init(value, G_TYPE_STRING);
370 g_value_set_string(value, pretty_permissions(m));
371 break;
372 case COL_SIZE:
373 g_value_init(value, G_TYPE_STRING);
374 if (item->base_type != TYPE_DIRECTORY)
375 g_value_set_string(value,
376 format_size(item->size));
377 break;
378 case COL_TYPE:
379 g_value_init(value, G_TYPE_STRING);
380 if(o_display_show_full_type.int_value)
381 g_value_set_string(value,
382 item->flags & ITEM_FLAG_APPDIR? "Application" :
383 mime_type_comment(item->mime_type));
384 else
385 g_value_set_string(value,
386 item->flags & ITEM_FLAG_APPDIR? "App" :
387 S_ISDIR(m) ? "Dir" :
388 S_ISCHR(m) ? "Char" :
389 S_ISBLK(m) ? "Blck" :
390 S_ISLNK(m) ? "Link" :
391 S_ISSOCK(m) ? "Sock" :
392 S_ISFIFO(m) ? "Pipe" :
393 S_ISDOOR(m) ? "Door" :
394 "File");
396 break;
397 case COL_WEIGHT:
398 g_value_init(value, G_TYPE_INT);
399 if (item->flags & ITEM_FLAG_RECENT)
400 g_value_set_int(value, PANGO_WEIGHT_BOLD);
401 else
402 g_value_set_int(value, PANGO_WEIGHT_NORMAL);
403 break;
404 default:
405 g_value_init(value, G_TYPE_STRING);
406 g_value_set_string(value, "Hello");
407 break;
411 static gboolean details_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter)
413 ViewDetails *view_details = (ViewDetails *) tree_model;
414 int i;
416 i = GPOINTER_TO_INT(iter->user_data) + 1;
417 iter->user_data = GINT_TO_POINTER(i);
419 return i < view_details->items->len;
422 static gboolean details_iter_children(GtkTreeModel *tree_model,
423 GtkTreeIter *iter,
424 GtkTreeIter *parent)
426 ViewDetails *view_details = (ViewDetails *) tree_model;
428 /* this is a list, nodes have no children */
429 if (parent)
430 return FALSE;
432 /* but if parent == NULL we return the list itself as children of the
433 * "root"
436 if (view_details->items->len)
438 iter->user_data = GINT_TO_POINTER(0);
439 return TRUE;
441 else
442 return FALSE;
445 static gboolean details_iter_has_child(GtkTreeModel *tree_model,
446 GtkTreeIter *iter)
448 return FALSE;
451 static gint details_iter_n_children(GtkTreeModel *tree_model, GtkTreeIter *iter)
453 ViewDetails *view_details = (ViewDetails *) tree_model;
455 if (iter == NULL)
456 return view_details->items->len;
458 return 0;
461 static gboolean details_iter_nth_child(GtkTreeModel *tree_model,
462 GtkTreeIter *iter,
463 GtkTreeIter *parent,
464 gint n)
466 ViewDetails *view_details = (ViewDetails *) tree_model;
468 if (parent)
469 return FALSE;
471 if (n >= 0 && n < view_details->items->len)
473 iter->user_data = GINT_TO_POINTER(n);
474 return TRUE;
476 else
477 return FALSE;
480 static gboolean details_iter_parent(GtkTreeModel *tree_model,
481 GtkTreeIter *iter,
482 GtkTreeIter *child)
484 return FALSE;
487 /* A ViewDetails is both a GtkTreeView and a GtkTreeModel.
488 * The following functions implement the model interface...
491 static void view_details_tree_model_init(GtkTreeModelIface *iface)
493 iface->get_flags = details_get_flags;
494 iface->get_n_columns = details_get_n_columns;
495 iface->get_column_type = details_get_column_type;
496 iface->get_iter = details_get_iter;
497 iface->get_path = details_get_path;
498 iface->get_value = details_get_value;
499 iface->iter_next = details_iter_next;
500 iface->iter_children = details_iter_children;
501 iface->iter_has_child = details_iter_has_child;
502 iface->iter_n_children = details_iter_n_children;
503 iface->iter_nth_child = details_iter_nth_child;
504 iface->iter_parent = details_iter_parent;
507 static void view_details_sortable_init(GtkTreeSortableIface *iface)
509 iface->get_sort_column_id = details_get_sort_column_id;
510 iface->set_sort_column_id = details_set_sort_column_id;
511 iface->set_sort_func = details_set_sort_func;
512 iface->set_default_sort_func = details_set_default_sort_func;
513 iface->has_default_sort_func = details_has_default_sort_func;
516 static gboolean details_get_sort_column_id(GtkTreeSortable *sortable,
517 gint *sort_column_id,
518 GtkSortType *order)
520 ViewDetails *view_details = (ViewDetails *) sortable;
521 FilerWindow *filer_window = view_details->filer_window;
522 int col;
524 if (!filer_window)
525 return FALSE; /* Not yet initialised */
527 switch (filer_window->sort_type)
529 case SORT_NAME: col = COL_LEAF; break;
530 case SORT_TYPE: col = COL_TYPE; break;
531 case SORT_DATE: col = COL_MTIME; break;
532 case SORT_SIZE: col = COL_SIZE; break;
533 case SORT_OWNER: col = COL_OWNER; break;
534 case SORT_GROUP: col = COL_GROUP; break;
535 default:
536 g_warning("details_get_sort_column_id(): error!");
537 return FALSE;
539 if (sort_column_id)
540 *sort_column_id = col;
541 if (order)
542 *order = filer_window->sort_order;
543 return TRUE;
546 static void details_set_sort_column_id(GtkTreeSortable *sortable,
547 gint sort_column_id,
548 GtkSortType order)
550 ViewDetails *view_details = (ViewDetails *) sortable;
551 FilerWindow *filer_window = view_details->filer_window;
553 if (!filer_window)
554 return; /* Not yet initialised */
556 switch (sort_column_id)
558 case COL_LEAF:
559 display_set_sort_type(filer_window, SORT_NAME, order);
560 break;
561 case COL_SIZE:
562 display_set_sort_type(filer_window, SORT_SIZE, order);
563 break;
564 case COL_MTIME:
565 display_set_sort_type(filer_window, SORT_DATE, order);
566 break;
567 case COL_TYPE:
568 display_set_sort_type(filer_window, SORT_TYPE, order);
569 break;
570 case COL_OWNER:
571 display_set_sort_type(filer_window, SORT_OWNER, order);
572 break;
573 case COL_GROUP:
574 display_set_sort_type(filer_window, SORT_GROUP, order);
575 break;
576 default:
577 g_assert_not_reached();
581 static void details_set_sort_func(GtkTreeSortable *sortable,
582 gint sort_column_id,
583 GtkTreeIterCompareFunc func,
584 gpointer data,
585 GtkDestroyNotify destroy)
587 g_assert_not_reached();
590 static void details_set_default_sort_func(GtkTreeSortable *sortable,
591 GtkTreeIterCompareFunc func,
592 gpointer data,
593 GtkDestroyNotify destroy)
595 g_assert_not_reached();
598 static gboolean details_has_default_sort_func(GtkTreeSortable *sortable)
600 return FALSE;
604 /* End of model implementation */
606 static gboolean is_selected(ViewDetails *view_details, int i)
608 ViewIter iter;
609 iter.i = i;
610 return view_details_get_selected((ViewIface *) view_details, &iter);
613 static gboolean view_details_scroll(GtkWidget *widget, GdkEventScroll *event)
615 GtkTreeView *tree = (GtkTreeView *) widget;
616 GtkTreePath *path = NULL;
618 if (!gtk_tree_view_get_path_at_pos(tree, 0, 1, &path, NULL, NULL, NULL))
619 return TRUE; /* Empty? */
621 if (event->direction == GDK_SCROLL_UP)
622 gtk_tree_path_prev(path);
623 else if (event->direction == GDK_SCROLL_DOWN)
624 gtk_tree_path_next(path);
625 else
626 goto out;
628 gtk_tree_view_scroll_to_cell(tree, path, NULL, TRUE, 0, 0);
629 out:
630 gtk_tree_path_free(path);
631 return TRUE;
634 static gint view_details_key_press(GtkWidget *widget, GdkEventKey *event)
636 if (event->keyval == GDK_Up || event->keyval == GDK_Down ||
637 event->keyval == GDK_Prior || event->keyval == GDK_Next ||
638 event->keyval == GDK_Home || event->keyval == GDK_End)
639 return GTK_WIDGET_CLASS(parent_class)->key_press_event(widget,
640 event);
641 return FALSE;
644 static gboolean view_details_button_press(GtkWidget *widget,
645 GdkEventButton *bev)
647 FilerWindow *filer_window = ((ViewDetails *) widget)->filer_window;
648 GtkTreeView *tree = (GtkTreeView *) widget;
650 if (bev->window != gtk_tree_view_get_bin_window(tree))
651 return GTK_WIDGET_CLASS(parent_class)->button_press_event(
652 widget, bev);
654 if (dnd_motion_press(widget, bev))
655 filer_perform_action(filer_window, bev);
657 return TRUE;
660 static int get_lasso_index(ViewDetails *view_details, int y)
662 GtkTreeViewColumn *column = NULL;
663 GtkTreePath *path = NULL;
664 GtkTreeView *tree = (GtkTreeView *) view_details;
665 gint cell_y;
666 int i;
668 if (y < 0)
669 y = 0; /* gtk_tree_view_get_path_at_pos can't handle negatives */
671 if (gtk_tree_view_get_path_at_pos(tree, 4, y, &path,
672 &column, NULL, &cell_y))
674 GdkRectangle rect;
675 i = gtk_tree_path_get_indices(path)[0];
676 gtk_tree_view_get_cell_area(tree, path, column, &rect);
677 gtk_tree_path_free(path);
679 if (2 * cell_y > rect.height)
680 i += 1;
682 else
683 i = view_details->items->len;
685 return i;
688 static gboolean select_lasso_cb(ViewIter *iter, gpointer data)
690 int start = ((int *) data)[0];
691 int end = ((int *) data)[1];
692 GdkFunction fn = ((int *) data)[2];
693 ViewDetails *view_details = (ViewDetails *) iter->view;
694 GtkTreeIter titer;
696 titer.user_data = GINT_TO_POINTER(iter->i);
698 if (iter->i < start || iter->i >= end)
699 return gtk_tree_selection_iter_is_selected(
700 view_details->selection, &titer);
702 if (fn == GDK_SET)
703 return TRUE;
705 return !gtk_tree_selection_iter_is_selected(view_details->selection,
706 &titer);
709 static void select_lasso(ViewDetails *view_details, GdkFunction fn)
711 GtkAdjustment *adj;
712 int range[3];
714 adj = gtk_tree_view_get_vadjustment((GtkTreeView *) view_details);
716 range[0] = view_details->lasso_start_index;
717 range[1] = get_lasso_index(view_details,
718 view_details->drag_box_y[1] - adj->value);
719 range[2] = fn;
721 if (range[0] == range[1])
722 return;
723 if (range[0] > range[1])
725 int tmp = range[0];
726 range[0] = range[1];
727 range[1] = tmp;
730 view_select_if((ViewIface *) view_details, select_lasso_cb, &range);
733 static gboolean view_details_button_release(GtkWidget *widget,
734 GdkEventButton *bev)
736 ViewDetails *view_details = (ViewDetails *) widget;
737 FilerWindow *filer_window = view_details->filer_window;
738 GtkTreeView *tree = (GtkTreeView *) widget;
740 if (bev->window != gtk_tree_view_get_bin_window(tree))
741 return GTK_WIDGET_CLASS(parent_class)->button_release_event(
742 widget, bev);
744 if (!dnd_motion_release(bev))
745 filer_perform_action(filer_window, bev);
747 if (motion_buttons_pressed == 0 && view_details->lasso_box)
749 select_lasso(view_details,
750 bev->button == 1 ? GDK_SET : GDK_INVERT);
751 filer_set_autoscroll(filer_window, FALSE);
752 set_lasso(view_details,
753 view_details->drag_box_x[0],
754 view_details->drag_box_y[0]);
755 view_details->lasso_box = FALSE;
758 return TRUE;
761 static gint view_details_motion_notify(GtkWidget *widget, GdkEventMotion *event)
763 ViewDetails *view_details = (ViewDetails *) widget;
764 GtkTreeView *tree = (GtkTreeView *) widget;
766 if (event->window != gtk_tree_view_get_bin_window(tree))
767 return GTK_WIDGET_CLASS(parent_class)->motion_notify_event(
768 widget, event);
770 if (view_details->lasso_box)
772 GtkAdjustment *adj;
773 adj = gtk_tree_view_get_vadjustment(tree);
774 set_lasso(view_details, event->x, event->y + adj->value);
775 return TRUE;
778 return filer_motion_notify(view_details->filer_window, event);
781 static gboolean view_details_expose(GtkWidget *widget, GdkEventExpose *event)
783 GtkTreeView *tree = (GtkTreeView *) widget;
784 GtkTreePath *path = NULL;
785 GdkRectangle focus_rectangle;
786 ViewDetails *view_details = (ViewDetails *) widget;
787 gboolean had_cursor;
789 had_cursor = (GTK_WIDGET_FLAGS(widget) & GTK_HAS_FOCUS) != 0;
791 if (view_details->filer_window->selection_state == GTK_STATE_SELECTED)
792 GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
793 else
794 GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS);
795 GTK_WIDGET_CLASS(parent_class)->expose_event(widget, event);
796 if (had_cursor)
797 GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
799 if (event->window != gtk_tree_view_get_bin_window(tree))
800 return FALSE; /* Not the main area */
802 if (view_details->lasso_box)
804 int x, y, width, height;
805 GtkAdjustment *adj;
807 adj = gtk_tree_view_get_vadjustment(tree);
808 x = MIN(view_details->drag_box_x[0],
809 view_details->drag_box_x[1]);
810 y = MIN(view_details->drag_box_y[0],
811 view_details->drag_box_y[1]);
812 width = abs(view_details->drag_box_x[1] -
813 view_details->drag_box_x[0]);
814 height = abs(view_details->drag_box_y[1] -
815 view_details->drag_box_y[0]);
816 y -= adj->value;
818 if (width && height)
819 gdk_draw_rectangle(event->window,
820 widget->style->fg_gc[GTK_STATE_NORMAL],
821 FALSE, x, y, width - 1, height - 1);
824 if (view_details->wink_item != -1 && view_details->wink_step & 1)
826 GtkTreePath *wink_path;
827 GdkRectangle wink_area;
829 wink_path = gtk_tree_path_new();
830 gtk_tree_path_append_index(wink_path, view_details->wink_item);
831 gtk_tree_view_get_background_area(tree, wink_path,
832 NULL, &wink_area);
833 gtk_tree_path_free(wink_path);
835 if (wink_area.height)
837 /* (visible) */
838 wink_area.width = widget->allocation.width;
839 gdk_draw_rectangle(event->window,
840 widget->style->fg_gc[GTK_STATE_NORMAL],
841 FALSE,
842 wink_area.x + 1,
843 wink_area.y + 1,
844 wink_area.width - 3,
845 wink_area.height - 3);
849 gtk_tree_view_get_cursor(tree, &path, NULL);
850 if (!path)
851 return FALSE; /* No cursor */
852 gtk_tree_view_get_background_area(tree, path, NULL, &focus_rectangle);
853 gtk_tree_path_free(path);
855 if (!focus_rectangle.height)
856 return FALSE; /* Off screen */
858 focus_rectangle.width = widget->allocation.width;
860 gtk_paint_focus(widget->style,
861 event->window,
862 GTK_STATE_NORMAL,
863 NULL,
864 widget,
865 "treeview",
866 focus_rectangle.x,
867 focus_rectangle.y,
868 focus_rectangle.width,
869 focus_rectangle.height);
871 return FALSE;
874 static void view_details_size_request(GtkWidget *widget,
875 GtkRequisition *requisition)
877 ViewDetails *view_details = (ViewDetails *) widget;
879 (*GTK_WIDGET_CLASS(parent_class)->size_request)(widget, requisition);
881 view_details->desired_size = *requisition;
883 requisition->height = 50;
884 requisition->width = 50;
887 static void view_details_drag_data_received(GtkWidget *widget,
888 GdkDragContext *drag_context,
889 gint x, gint y, GtkSelectionData *data, guint info, guint time)
891 /* Just here to override annoying default handler */
894 static void view_details_destroy(GtkObject *obj)
896 ViewDetails *view_details = VIEW_DETAILS(obj);
898 view_details->filer_window = NULL;
899 cancel_wink(view_details);
902 static void view_details_finialize(GObject *object)
904 ViewDetails *view_details = (ViewDetails *) object;
906 g_ptr_array_free(view_details->items, TRUE);
907 view_details->items = NULL;
909 G_OBJECT_CLASS(parent_class)->finalize(object);
912 static void view_details_class_init(gpointer gclass, gpointer data)
914 GObjectClass *object = (GObjectClass *) gclass;
915 GtkWidgetClass *widget = (GtkWidgetClass *) gclass;
917 parent_class = g_type_class_peek_parent(gclass);
919 object->finalize = view_details_finialize;
920 GTK_OBJECT_CLASS(object)->destroy = view_details_destroy;
922 widget->scroll_event = view_details_scroll;
923 widget->key_press_event = view_details_key_press;
924 widget->button_press_event = view_details_button_press;
925 widget->button_release_event = view_details_button_release;
926 widget->motion_notify_event = view_details_motion_notify;
927 widget->expose_event = view_details_expose;
928 widget->size_request = view_details_size_request;
929 widget->drag_data_received = view_details_drag_data_received;
932 * Add the ViewDetails::mono-font style property.
933 * To use this add something like
935 * style "details" {
936 * ViewDetails::mono-font = "Courier 8"
938 * class "ViewDetails" style "details"
940 * to your ~/.gtkrc-2.0
942 gtk_widget_class_install_style_property(widget,
943 g_param_spec_string("mono-font",
944 _("Mono font"),
945 _("Font for displaying mono-spaced text"),
946 "monospace",
947 G_PARAM_READABLE));
951 static gboolean block_focus(GtkWidget *button, GtkDirectionType dir,
952 ViewDetails *view_details)
954 GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
955 return FALSE;
958 static gboolean test_can_change_selection(GtkTreeSelection *sel,
959 GtkTreeModel *model,
960 GtkTreePath *path,
961 gboolean path_currently_selected,
962 gpointer data)
964 ViewDetails *view_details;
966 view_details = VIEW_DETAILS(gtk_tree_selection_get_tree_view(sel));
968 return view_details->can_change_selection != 0;
971 static void selection_changed(GtkTreeSelection *selection,
972 gpointer user_data)
974 ViewDetails *view_details = VIEW_DETAILS(user_data);
976 filer_selection_changed(view_details->filer_window,
977 gtk_get_current_event_time());
981 * Set the font used for a treeview column to that given for the
982 * "mono-font" style property of a widget. This has to be done _after_ the
983 * widget has been realized, because that is when the style information is
984 * set up. This is done by connecting this function to run after the
985 * "realize" signal.
987 static void set_column_mono_font(GtkWidget *widget, GObject *object)
989 const gchar *font_name;
991 gtk_widget_style_get(widget, "mono-font", &font_name, NULL);
992 g_object_set(object, "font", font_name, NULL);
995 #define ADD_TEXT_COLUMN(name, model_column) \
996 cell = gtk_cell_renderer_text_new(); \
997 column = gtk_tree_view_column_new_with_attributes(name, cell, \
998 "text", model_column, \
999 "foreground-gdk", COL_COLOUR, \
1000 "weight", COL_WEIGHT, \
1001 NULL); \
1002 gtk_tree_view_append_column(treeview, column); \
1003 g_signal_connect(column->button, "grab-focus", \
1004 G_CALLBACK(block_focus), view_details);
1006 static void view_details_init(GTypeInstance *object, gpointer gclass)
1008 GtkTreeView *treeview = (GtkTreeView *) object;
1009 GtkTreeViewColumn *column;
1010 GtkCellRenderer *cell;
1011 GtkTreeSortable *sortable_list;
1012 ViewDetails *view_details = (ViewDetails *) object;
1014 view_details->items = g_ptr_array_new();
1015 view_details->cursor_base = -1;
1016 view_details->wink_item = -1;
1017 view_details->desired_size.width = -1;
1018 view_details->desired_size.height = -1;
1019 view_details->can_change_selection = 0;
1020 view_details->lasso_box = FALSE;
1022 view_details->selection = gtk_tree_view_get_selection(treeview);
1023 gtk_tree_selection_set_mode(view_details->selection,
1024 GTK_SELECTION_MULTIPLE);
1025 gtk_tree_selection_set_select_function(view_details->selection,
1026 test_can_change_selection, view_details, NULL);
1028 /* Sorting */
1029 view_details->sort_fn = NULL;
1030 sortable_list = GTK_TREE_SORTABLE(object);
1032 gtk_tree_view_set_model(treeview, GTK_TREE_MODEL(view_details));
1033 /* Do this after set_model, because that can generate this
1034 * signal...
1036 g_signal_connect(view_details->selection, "changed",
1037 G_CALLBACK(selection_changed), view_details);
1039 /* Icon */
1040 cell = cell_icon_new(view_details);
1041 column = gtk_tree_view_column_new_with_attributes(NULL, cell,
1042 "item", COL_VIEW_ITEM,
1043 NULL);
1044 gtk_tree_view_append_column(treeview, column);
1046 ADD_TEXT_COLUMN(_("_Name"), COL_LEAF);
1047 gtk_tree_view_column_set_sort_column_id(column, COL_LEAF);
1048 gtk_tree_view_column_set_resizable(column, TRUE);
1049 ADD_TEXT_COLUMN(_("_Type"), COL_TYPE);
1050 gtk_tree_view_column_set_sort_column_id(column, COL_TYPE);
1051 gtk_tree_view_column_set_resizable(column, TRUE);
1052 ADD_TEXT_COLUMN(_("_Permissions"), COL_PERM);
1053 g_object_set(G_OBJECT(cell), "font", "monospace", NULL);
1054 g_signal_connect_after(object, "realize",
1055 G_CALLBACK(set_column_mono_font),
1056 G_OBJECT(cell));
1057 ADD_TEXT_COLUMN(_("_Owner"), COL_OWNER);
1058 gtk_tree_view_column_set_sort_column_id(column, COL_OWNER);
1059 ADD_TEXT_COLUMN(_("_Group"), COL_GROUP);
1060 gtk_tree_view_column_set_sort_column_id(column, COL_GROUP);
1061 ADD_TEXT_COLUMN(_("_Size"), COL_SIZE);
1062 g_object_set(G_OBJECT(cell), "xalign", 1.0, "font", "monospace", NULL);
1063 g_signal_connect_after(object, "realize",
1064 G_CALLBACK(set_column_mono_font),
1065 G_OBJECT(cell));
1066 gtk_tree_view_column_set_sort_column_id(column, COL_SIZE);
1067 ADD_TEXT_COLUMN(_("Last _Modified"), COL_MTIME);
1068 gtk_tree_view_column_set_sort_column_id(column, COL_MTIME);
1071 /* Create the handers for the View interface */
1072 static void view_details_iface_init(gpointer giface, gpointer iface_data)
1074 ViewIfaceClass *iface = giface;
1076 g_assert(G_TYPE_FROM_INTERFACE(iface) == VIEW_TYPE_IFACE);
1078 /* override stuff */
1079 iface->sort = view_details_sort;
1080 iface->style_changed = view_details_style_changed;
1081 iface->add_items = view_details_add_items;
1082 iface->update_items = view_details_update_items;
1083 iface->delete_if = view_details_delete_if;
1084 iface->clear = view_details_clear;
1085 iface->select_all = view_details_select_all;
1086 iface->clear_selection = view_details_clear_selection;
1087 iface->count_items = view_details_count_items;
1088 iface->count_selected = view_details_count_selected;
1089 iface->show_cursor = view_details_show_cursor;
1090 iface->get_iter = view_details_get_iter;
1091 iface->get_iter_at_point = view_details_get_iter_at_point;
1092 iface->cursor_to_iter = view_details_cursor_to_iter;
1093 iface->set_selected = view_details_set_selected;
1094 iface->get_selected = view_details_get_selected;
1095 iface->set_frozen = view_details_set_frozen;
1096 iface->select_only = view_details_select_only;
1097 iface->wink_item = view_details_wink_item;
1098 iface->autosize = view_details_autosize;
1099 iface->cursor_visible = view_details_cursor_visible;
1100 iface->set_base = view_details_set_base;
1101 iface->start_lasso_box = view_details_start_lasso_box;
1102 iface->extend_tip = view_details_extend_tip;
1103 iface->auto_scroll_callback = view_details_auto_scroll_callback;
1106 /* Implementations of the View interface. See view_iface.c for comments. */
1108 static void view_details_style_changed(ViewIface *view, int flags)
1110 ViewDetails *view_details = (ViewDetails *) view;
1111 GtkTreeModel *model = (GtkTreeModel *) view;
1112 GtkTreePath *path;
1113 ViewItem **items = (ViewItem **) view_details->items->pdata;
1114 int i;
1115 int n = view_details->items->len;
1117 path = gtk_tree_path_new();
1118 gtk_tree_path_append_index(path, 0);
1120 for (i = 0; i < n; i++)
1122 GtkTreeIter iter;
1123 ViewItem *item = items[i];
1125 iter.user_data = GINT_TO_POINTER(i);
1126 if (item->image)
1128 g_object_unref(G_OBJECT(item->image));
1129 item->image = NULL;
1131 gtk_tree_model_row_changed(model, path, &iter);
1132 gtk_tree_path_next(path);
1135 gtk_tree_path_free(path);
1137 gtk_tree_view_columns_autosize((GtkTreeView *) view);
1139 if (flags & VIEW_UPDATE_HEADERS)
1140 details_update_header_visibility(view_details);
1143 static gint wrap_sort(gconstpointer a, gconstpointer b,
1144 ViewDetails *view_details)
1146 ViewItem *ia = *(ViewItem **) a;
1147 ViewItem *ib = *(ViewItem **) b;
1149 if (view_details->filer_window->sort_order == GTK_SORT_ASCENDING)
1150 return view_details->sort_fn(ia->item, ib->item);
1151 else
1152 return -view_details->sort_fn(ia->item, ib->item);
1155 static void resort(ViewDetails *view_details)
1157 ViewItem **items = (ViewItem **) view_details->items->pdata;
1158 gint i, len = view_details->items->len;
1159 guint *new_order;
1160 GtkTreePath *path;
1161 int wink_item = view_details->wink_item;
1163 if (!len)
1164 return;
1166 for (i = len - 1; i >= 0; i--)
1167 items[i]->old_pos = i;
1169 switch (view_details->filer_window->sort_type)
1171 case SORT_NAME: view_details->sort_fn = sort_by_name; break;
1172 case SORT_TYPE: view_details->sort_fn = sort_by_type; break;
1173 case SORT_DATE: view_details->sort_fn = sort_by_date; break;
1174 case SORT_SIZE: view_details->sort_fn = sort_by_size; break;
1175 case SORT_OWNER: view_details->sort_fn = sort_by_owner; break;
1176 case SORT_GROUP: view_details->sort_fn = sort_by_group; break;
1177 default:
1178 g_assert_not_reached();
1181 g_ptr_array_sort_with_data(view_details->items,
1182 (GCompareDataFunc) wrap_sort,
1183 view_details);
1185 new_order = g_new(guint, len);
1186 for (i = len - 1; i >= 0; i--)
1188 new_order[i] = items[i]->old_pos;
1189 if (wink_item == items[i]->old_pos)
1190 wink_item = i;
1193 view_details->wink_item = wink_item;
1195 path = gtk_tree_path_new();
1196 gtk_tree_model_rows_reordered((GtkTreeModel *) view_details,
1197 path, NULL, new_order);
1198 gtk_tree_path_free(path);
1199 g_free(new_order);
1202 static void view_details_sort(ViewIface *view)
1204 resort((ViewDetails *) view);
1205 gtk_tree_sortable_sort_column_changed((GtkTreeSortable *) view);
1208 static void view_details_add_items(ViewIface *view, GPtrArray *new_items)
1210 ViewDetails *view_details = (ViewDetails *) view;
1211 FilerWindow *filer_window = view_details->filer_window;
1212 GPtrArray *items = view_details->items;
1213 GtkTreeIter iter;
1214 int i;
1215 GtkTreePath *path;
1216 GtkTreeModel *model = (GtkTreeModel *) view;
1218 iter.user_data = GINT_TO_POINTER(items->len);
1219 path = details_get_path(model, &iter);
1221 for (i = 0; i < new_items->len; i++)
1223 DirItem *item = (DirItem *) new_items->pdata[i];
1224 char *leafname = item->leafname;
1225 ViewItem *vitem;
1227 if(!filer_match_filter(filer_window, leafname))
1228 continue;
1229 if (leafname[0] == '.')
1231 if (leafname[1] == '\0')
1232 continue; /* Never show '.' */
1234 if (leafname[1] == '.' &&
1235 leafname[2] == '\0')
1236 continue; /* Never show '..' */
1239 vitem = g_new(ViewItem, 1);
1240 vitem->item = item;
1241 vitem->image = NULL;
1242 if (!g_utf8_validate(leafname, -1, NULL))
1243 vitem->utf8_name = to_utf8(leafname);
1244 else
1245 vitem->utf8_name = NULL;
1247 g_ptr_array_add(items, vitem);
1249 iter.user_data = GINT_TO_POINTER(items->len - 1);
1250 gtk_tree_model_row_inserted(model, path, &iter);
1251 gtk_tree_path_next(path);
1254 gtk_tree_path_free(path);
1256 resort(view_details);
1259 /* Find an item in the sorted array.
1260 * Returns the item number, or -1 if not found.
1262 static int details_find_item(ViewDetails *view_details, DirItem *item)
1264 ViewItem **items, tmp, *tmpp;
1265 int lower, upper;
1267 g_return_val_if_fail(view_details != NULL, -1);
1268 g_return_val_if_fail(item != NULL, -1);
1270 tmp.item = item;
1271 tmpp = &tmp;
1273 items = (ViewItem **) view_details->items->pdata;
1275 /* If item is here, then: lower <= i < upper */
1276 lower = 0;
1277 upper = view_details->items->len;
1279 while (lower < upper)
1281 int i, cmp;
1283 i = (lower + upper) >> 1;
1285 cmp = wrap_sort(&items[i], &tmpp, view_details);
1286 if (cmp == 0)
1287 return i;
1289 if (cmp > 0)
1290 upper = i;
1291 else
1292 lower = i + 1;
1295 return -1;
1298 static void view_details_update_items(ViewIface *view, GPtrArray *items)
1300 ViewDetails *view_details = (ViewDetails *) view;
1301 FilerWindow *filer_window = view_details->filer_window;
1302 int i;
1303 GtkTreeModel *model = (GtkTreeModel *) view_details;
1305 g_return_if_fail(items->len > 0);
1307 /* The item data has already been modified, so this gives the
1308 * final sort order...
1310 resort(view_details);
1312 for (i = 0; i < items->len; i++)
1314 DirItem *item = (DirItem *) items->pdata[i];
1315 const gchar *leafname = item->leafname;
1316 int j;
1318 if (!filer_match_filter(filer_window, leafname))
1319 continue;
1321 j = details_find_item(view_details, item);
1323 if (j < 0)
1324 g_warning("Failed to find '%s'\n", leafname);
1325 else
1327 GtkTreePath *path;
1328 GtkTreeIter iter;
1329 ViewItem *view_item = view_details->items->pdata[j];
1330 if (view_item->image)
1332 g_object_unref(G_OBJECT(view_item->image));
1333 view_item->image = NULL;
1335 path = gtk_tree_path_new();
1336 gtk_tree_path_append_index(path, j);
1337 iter.user_data = GINT_TO_POINTER(j);
1338 gtk_tree_model_row_changed(model, path, &iter);
1343 static void view_details_delete_if(ViewIface *view,
1344 gboolean (*test)(gpointer item, gpointer data),
1345 gpointer data)
1347 GtkTreePath *path;
1348 ViewDetails *view_details = (ViewDetails *) view;
1349 int i = 0;
1350 GPtrArray *items = view_details->items;
1351 GtkTreeModel *model = (GtkTreeModel *) view;
1353 path = gtk_tree_path_new();
1355 gtk_tree_path_append_index(path, i);
1357 while (i < items->len)
1359 ViewItem *item = items->pdata[i];
1361 if (test(item->item, data))
1363 free_view_item(items->pdata[i]);
1364 g_ptr_array_remove_index(items, i);
1365 gtk_tree_model_row_deleted(model, path);
1367 else
1369 i++;
1370 gtk_tree_path_next(path);
1374 gtk_tree_path_free(path);
1377 static void view_details_clear(ViewIface *view)
1379 GtkTreePath *path;
1380 GPtrArray *items = ((ViewDetails *) view)->items;
1381 GtkTreeModel *model = (GtkTreeModel *) view;
1383 path = gtk_tree_path_new();
1384 gtk_tree_path_append_index(path, items->len);
1386 while (gtk_tree_path_prev(path))
1387 gtk_tree_model_row_deleted(model, path);
1389 g_ptr_array_set_size(items, 0);
1390 gtk_tree_path_free(path);
1393 static void view_details_select_all(ViewIface *view)
1395 ViewDetails *view_details = (ViewDetails *) view;
1397 view_details->can_change_selection++;
1398 gtk_tree_selection_select_all(view_details->selection);
1399 view_details->can_change_selection--;
1402 static void view_details_clear_selection(ViewIface *view)
1404 ViewDetails *view_details = (ViewDetails *) view;
1406 view_details->can_change_selection++;
1407 gtk_tree_selection_unselect_all(view_details->selection);
1408 view_details->can_change_selection--;
1411 static int view_details_count_items(ViewIface *view)
1413 ViewDetails *view_details = (ViewDetails *) view;
1415 return view_details->items->len;
1418 #if GTK_MINOR_VERSION < 2
1419 static void view_details_count_inc(GtkTreeModel *model, GtkTreePath *path,
1420 GtkTreeIter *iter, gpointer data)
1422 int *count = (int *) data;
1423 (*count) += 1;
1425 #endif
1427 static int view_details_count_selected(ViewIface *view)
1429 ViewDetails *view_details = (ViewDetails *) view;
1431 #if GTK_MINOR_VERSION >= 2
1432 return gtk_tree_selection_count_selected_rows(view_details->selection);
1433 #else
1434 int count = 0;
1436 gtk_tree_selection_selected_foreach(view_details->selection,
1437 view_details_count_inc, &count);
1438 return count;
1439 #endif
1442 static void view_details_show_cursor(ViewIface *view)
1446 static void view_details_get_iter(ViewIface *view,
1447 ViewIter *iter, IterFlags flags)
1449 make_iter((ViewDetails *) view, iter, flags);
1452 static void view_details_get_iter_at_point(ViewIface *view, ViewIter *iter,
1453 GdkWindow *src, int x, int y)
1455 ViewDetails *view_details = (ViewDetails *) view;
1456 GtkTreeModel *model;
1457 GtkTreeView *tree = (GtkTreeView *) view;
1458 GtkTreePath *path = NULL;
1459 int i = -1;
1460 gint cell_y;
1462 model = gtk_tree_view_get_model(tree);
1464 if (gtk_tree_view_get_path_at_pos(tree, x, y + 4, &path, NULL,
1465 NULL, &cell_y))
1467 g_return_if_fail(path != NULL);
1469 if (cell_y > 8)
1470 i = gtk_tree_path_get_indices(path)[0];
1471 gtk_tree_path_free(path);
1474 make_item_iter(view_details, iter, i);
1477 static void view_details_cursor_to_iter(ViewIface *view, ViewIter *iter)
1479 GtkTreePath *path;
1480 ViewDetails *view_details = (ViewDetails *) view;
1482 path = gtk_tree_path_new();
1484 if (iter)
1485 gtk_tree_path_append_index(path, iter->i);
1486 else
1488 /* Using depth zero or index -1 gives an error, but this
1489 * is OK!
1491 gtk_tree_path_append_index(path, view_details->items->len);
1494 gtk_tree_view_set_cursor((GtkTreeView *) view, path, NULL, FALSE);
1495 gtk_tree_path_free(path);
1498 static void set_selected(ViewDetails *view_details, int i, gboolean selected)
1500 GtkTreeIter iter;
1502 iter.user_data = GINT_TO_POINTER(i);
1503 view_details->can_change_selection++;
1504 if (selected)
1505 gtk_tree_selection_select_iter(view_details->selection, &iter);
1506 else
1507 gtk_tree_selection_unselect_iter(view_details->selection,
1508 &iter);
1509 view_details->can_change_selection--;
1512 static void view_details_set_selected(ViewIface *view,
1513 ViewIter *iter,
1514 gboolean selected)
1516 set_selected((ViewDetails *) view, iter->i, selected);
1519 static gboolean get_selected(ViewDetails *view_details, int i)
1521 GtkTreeIter iter;
1523 iter.user_data = GINT_TO_POINTER(i);
1525 return gtk_tree_selection_iter_is_selected(view_details->selection,
1526 &iter);
1529 static gboolean view_details_get_selected(ViewIface *view, ViewIter *iter)
1531 return get_selected((ViewDetails *) view, iter->i);
1534 static void view_details_select_only(ViewIface *view, ViewIter *iter)
1536 ViewDetails *view_details = (ViewDetails *) view;
1537 GtkTreePath *path;
1539 path = gtk_tree_path_new();
1540 gtk_tree_path_append_index(path, iter->i);
1541 view_details->can_change_selection++;
1542 gtk_tree_selection_unselect_all(view_details->selection);
1543 gtk_tree_selection_select_range(view_details->selection, path, path);
1544 view_details->can_change_selection--;
1545 gtk_tree_path_free(path);
1548 static void view_details_set_frozen(ViewIface *view, gboolean frozen)
1552 static void redraw_wink_area(ViewDetails *view_details)
1554 GtkTreePath *wink_path;
1555 GdkRectangle wink_area;
1556 GtkTreeView *tree = (GtkTreeView *) view_details;
1558 g_return_if_fail(view_details->wink_item >= 0);
1560 wink_path = gtk_tree_path_new();
1561 gtk_tree_path_append_index(wink_path, view_details->wink_item);
1562 gtk_tree_view_get_background_area(tree, wink_path, NULL, &wink_area);
1563 gtk_tree_path_free(wink_path);
1565 if (wink_area.height)
1567 GdkWindow *window;
1568 window = gtk_tree_view_get_bin_window(tree);
1570 wink_area.width = GTK_WIDGET(tree)->allocation.width;
1571 gdk_window_invalidate_rect(window, &wink_area, FALSE);
1575 static void cancel_wink(ViewDetails *view_details)
1577 if (view_details->wink_item == -1)
1578 return;
1580 if (view_details->filer_window)
1581 redraw_wink_area(view_details);
1583 view_details->wink_item = -1;
1584 g_source_remove(view_details->wink_timeout);
1587 static gboolean wink_timeout(ViewDetails *view_details)
1589 view_details->wink_step--;
1590 if (view_details->wink_step < 1)
1592 cancel_wink(view_details);
1593 return FALSE;
1596 redraw_wink_area(view_details);
1598 return TRUE;
1601 static void view_details_wink_item(ViewIface *view, ViewIter *iter)
1603 ViewDetails *view_details = (ViewDetails *) view;
1604 GtkTreePath *path;
1606 cancel_wink(view_details);
1607 if (!iter)
1608 return;
1610 view_details->wink_item = iter->i;
1611 view_details->wink_timeout = g_timeout_add(70,
1612 (GSourceFunc) wink_timeout, view_details);
1613 view_details->wink_step = 7;
1614 redraw_wink_area(view_details);
1616 path = gtk_tree_path_new();
1617 gtk_tree_path_append_index(path, iter->i);
1618 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(view),
1619 path, NULL, FALSE, 0, 0);
1620 gtk_tree_path_free(path);
1623 static void view_details_autosize(ViewIface *view)
1625 ViewDetails *view_details = (ViewDetails *) view;
1626 FilerWindow *filer_window = view_details->filer_window;
1627 int max_width = (o_filer_size_limit.int_value * monitor_width) / 100;
1628 int max_height = (o_filer_size_limit.int_value * monitor_height) / 100;
1629 int h;
1630 GtkRequisition req;
1632 gtk_widget_queue_resize(GTK_WIDGET(view));
1633 gtk_widget_size_request(GTK_WIDGET(view), &req);
1635 h = MAX(view_details->desired_size.height, SMALL_HEIGHT);
1637 filer_window_set_size(filer_window,
1638 MIN(view_details->desired_size.width, max_width),
1639 MIN(h, max_height));
1642 static gboolean view_details_cursor_visible(ViewIface *view)
1644 GtkTreePath *path = NULL;
1646 gtk_tree_view_get_cursor((GtkTreeView *) view, &path, NULL);
1648 if (path)
1649 gtk_tree_path_free(path);
1651 return path != NULL;
1654 static void view_details_set_base(ViewIface *view, ViewIter *iter)
1656 ViewDetails *view_details = (ViewDetails *) view;
1658 view_details->cursor_base = iter->i;
1661 /* Change the dynamic corner of the lasso box and trigger a redraw */
1662 static void set_lasso(ViewDetails *view_details, int x, int y)
1664 GdkWindow *window;
1665 GdkRectangle area;
1666 int minx, miny, maxx, maxy;
1668 if (x == view_details->drag_box_x[1] &&
1669 y == view_details->drag_box_y[1])
1670 return;
1672 /* Get old region */
1673 minx = MIN(view_details->drag_box_x[0], view_details->drag_box_x[1]);
1674 miny = MIN(view_details->drag_box_y[0], view_details->drag_box_y[1]);
1675 maxx = MAX(view_details->drag_box_x[0], view_details->drag_box_x[1]);
1676 maxy = MAX(view_details->drag_box_y[0], view_details->drag_box_y[1]);
1678 /* Enlarge to cover new point */
1679 minx = MIN(minx, x);
1680 miny = MIN(miny, y);
1681 maxx = MAX(maxx, x);
1682 maxy = MAX(maxy, y);
1684 area.x = minx;
1685 area.y = miny;
1686 area.width = maxx - minx;
1687 area.height = maxy - miny;
1689 view_details->drag_box_x[1] = x;
1690 view_details->drag_box_y[1] = y;
1692 window = gtk_tree_view_get_bin_window((GtkTreeView *) view_details);
1693 if (area.width && area.height)
1695 GtkAdjustment *adj;
1697 adj = gtk_tree_view_get_vadjustment((GtkTreeView *)
1698 view_details);
1699 area.y -= adj->value;
1700 gdk_window_invalidate_rect(window, &area, FALSE);
1704 static void view_details_start_lasso_box(ViewIface *view, GdkEventButton *event)
1706 ViewDetails *view_details = (ViewDetails *) view;
1707 GtkAdjustment *adj;
1709 adj = gtk_tree_view_get_vadjustment((GtkTreeView *) view_details);
1711 view_details->lasso_start_index = get_lasso_index(view_details,
1712 event->y);
1714 filer_set_autoscroll(view_details->filer_window, TRUE);
1716 view_details->drag_box_x[0] = view_details->drag_box_x[1] = event->x;
1717 view_details->drag_box_y[0] = view_details->drag_box_y[1] = event->y +
1718 adj->value;
1719 view_details->lasso_box = TRUE;
1722 static void view_details_extend_tip(ViewIface *view,
1723 ViewIter *iter, GString *tip)
1727 static DirItem *iter_init(ViewIter *iter)
1729 ViewDetails *view_details = (ViewDetails *) iter->view;
1730 int i = -1;
1731 int n = view_details->items->len;
1732 int flags = iter->flags;
1734 iter->peek = iter_peek;
1736 if (iter->n_remaining == 0)
1737 return NULL;
1739 if (flags & VIEW_ITER_FROM_CURSOR)
1741 GtkTreePath *path;
1742 gtk_tree_view_get_cursor((GtkTreeView *) view_details,
1743 &path, NULL);
1744 if (!path)
1745 return NULL; /* No cursor */
1746 i = gtk_tree_path_get_indices(path)[0];
1747 gtk_tree_path_free(path);
1749 else if (flags & VIEW_ITER_FROM_BASE)
1750 i = view_details->cursor_base;
1752 if (i < 0 || i >= n)
1754 /* Either a normal iteration, or an iteration from an
1755 * invalid starting point.
1757 if (flags & VIEW_ITER_BACKWARDS)
1758 i = n - 1;
1759 else
1760 i = 0;
1763 if (i < 0 || i >= n)
1764 return NULL; /* No items at all! */
1766 iter->next = flags & VIEW_ITER_BACKWARDS ? iter_prev : iter_next;
1767 iter->n_remaining--;
1768 iter->i = i;
1770 if (flags & VIEW_ITER_SELECTED && !is_selected(view_details, i))
1771 return iter->next(iter);
1772 return iter->peek(iter);
1775 static DirItem *iter_prev(ViewIter *iter)
1777 ViewDetails *view_details = (ViewDetails *) iter->view;
1778 int n = view_details->items->len;
1779 int i = iter->i;
1781 g_return_val_if_fail(iter->n_remaining >= 0, NULL);
1783 /* i is the last item returned (always valid) */
1785 g_return_val_if_fail(i >= 0 && i < n, NULL);
1787 while (iter->n_remaining)
1789 i--;
1790 iter->n_remaining--;
1792 if (i == -1)
1793 i = n - 1;
1795 g_return_val_if_fail(i >= 0 && i < n, NULL);
1797 if (iter->flags & VIEW_ITER_SELECTED &&
1798 !is_selected(view_details, i))
1799 continue;
1801 iter->i = i;
1802 return ((ViewItem *) view_details->items->pdata[i])->item;
1805 iter->i = -1;
1806 return NULL;
1809 static DirItem *iter_next(ViewIter *iter)
1811 ViewDetails *view_details = (ViewDetails *) iter->view;
1812 int n = view_details->items->len;
1813 int i = iter->i;
1815 g_return_val_if_fail(iter->n_remaining >= 0, NULL);
1817 /* i is the last item returned (always valid) */
1819 g_return_val_if_fail(i >= 0 && i < n, NULL);
1821 while (iter->n_remaining)
1823 i++;
1824 iter->n_remaining--;
1826 if (i == n)
1827 i = 0;
1829 g_return_val_if_fail(i >= 0 && i < n, NULL);
1831 if (iter->flags & VIEW_ITER_SELECTED &&
1832 !is_selected(view_details, i))
1833 continue;
1835 iter->i = i;
1836 return ((ViewItem *) view_details->items->pdata[i])->item;
1839 iter->i = -1;
1840 return NULL;
1843 static DirItem *iter_peek(ViewIter *iter)
1845 ViewDetails *view_details = (ViewDetails *) iter->view;
1846 int n = view_details->items->len;
1847 int i = iter->i;
1849 if (i == -1)
1850 return NULL;
1852 g_return_val_if_fail(i >= 0 && i < n, NULL);
1854 return ((ViewItem *) view_details->items->pdata[i])->item;
1857 /* Set the iterator to return 'i' on the next peek().
1858 * If i is -1, returns NULL on next peek().
1860 static void make_item_iter(ViewDetails *view_details, ViewIter *iter, int i)
1862 make_iter(view_details, iter, 0);
1864 g_return_if_fail(i >= -1 && i < (int) view_details->items->len);
1866 iter->i = i;
1867 iter->next = iter_next;
1868 iter->peek = iter_peek;
1869 iter->n_remaining = 0;
1872 static void make_iter(ViewDetails *view_details, ViewIter *iter,
1873 IterFlags flags)
1875 iter->view = (ViewIface *) view_details;
1876 iter->next = iter_init;
1877 iter->peek = NULL;
1878 iter->i = -1;
1880 iter->flags = flags;
1882 if (flags & VIEW_ITER_ONE_ONLY)
1884 iter->n_remaining = 1;
1885 iter->next(iter);
1887 else
1888 iter->n_remaining = view_details->items->len;
1891 static void free_view_item(ViewItem *view_item)
1893 if (view_item->image)
1894 g_object_unref(G_OBJECT(view_item->image));
1895 g_free(view_item->utf8_name);
1896 g_free(view_item);
1899 static gboolean view_details_auto_scroll_callback(ViewIface *view)
1901 GtkTreeView *tree = (GtkTreeView *) view;
1902 ViewDetails *view_details = (ViewDetails *) view;
1903 FilerWindow *filer_window = view_details->filer_window;
1904 GtkRange *scrollbar = (GtkRange *) filer_window->scrollbar;
1905 GtkAdjustment *adj;
1906 GdkWindow *window;
1907 gint x, y, w, h;
1908 GdkModifierType mask;
1909 int diff = 0;
1911 window = gtk_tree_view_get_bin_window(tree);
1913 gdk_window_get_pointer(window, &x, &y, &mask);
1914 gdk_drawable_get_size(window, &w, NULL);
1916 adj = gtk_range_get_adjustment(scrollbar);
1917 h = adj->page_size;
1919 if ((x < 0 || x > w || y < 0 || y > h) && !view_details->lasso_box)
1920 return FALSE; /* Out of window - stop */
1922 if (y < AUTOSCROLL_STEP)
1923 diff = y - AUTOSCROLL_STEP;
1924 else if (y > h - AUTOSCROLL_STEP)
1925 diff = AUTOSCROLL_STEP + y - h;
1927 if (diff)
1929 int old = adj->value;
1930 int value = old + diff;
1932 value = CLAMP(value, 0, adj->upper - adj->page_size);
1933 gtk_adjustment_set_value(adj, value);
1935 if (adj->value != old)
1936 dnd_spring_abort();
1939 return TRUE;