Converted README to markdown
[rox-filer.git] / ROX-Filer / src / view_details.c
blob3b0de90387bd04be00fae027ca1ff9e696aeb06e
1 /*
2 * ROX-Filer, filer for the ROX desktop project
3 * Copyright (C) 2006, Thomas Leonard and others (see changelog for details).
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
17 * Place, Suite 330, Boston, MA 02111-1307 USA
20 /* view_details.c - display a list of files in a TreeView */
22 #include "config.h"
24 #include <gtk/gtk.h>
25 #include <gdk/gdkkeysyms.h>
27 #include "global.h"
29 #include "view_iface.h"
30 #include "view_details.h"
31 #include "dir.h"
32 #include "diritem.h"
33 #include "support.h"
34 #include "type.h"
35 #include "filer.h"
36 #include "display.h"
37 #include "pixmaps.h"
38 #include "dnd.h"
39 #include "bind.h"
40 #include "gui_support.h"
41 #include "menu.h"
42 #include "options.h"
43 #include "cell_icon.h"
45 /* These are the column numbers in the ListStore */
46 #define COL_LEAF 0
47 #define COL_TYPE 1
48 #define COL_PERM 2
49 #define COL_OWNER 3
50 #define COL_GROUP 4
51 #define COL_SIZE 5
52 #define COL_MTIME 6
53 #define COL_ITEM 7
54 #define COL_COLOUR 8
55 #define COL_WEIGHT 9
56 #define COL_VIEW_ITEM 10
57 #define N_COLUMNS 11
59 static gpointer parent_class = NULL;
61 struct _ViewDetailsClass {
62 GtkTreeViewClass parent;
65 /* Static prototypes */
66 static void view_details_finialize(GObject *object);
67 static void view_details_class_init(gpointer gclass, gpointer data);
68 static void view_details_init(GTypeInstance *object, gpointer gclass);
70 static void view_details_iface_init(gpointer giface, gpointer iface_data);
72 static void view_details_sort(ViewIface *view);
73 static void view_details_style_changed(ViewIface *view, int flags);
74 static void view_details_add_items(ViewIface *view, GPtrArray *items);
75 static void view_details_update_items(ViewIface *view, GPtrArray *items);
76 static void view_details_delete_if(ViewIface *view,
77 gboolean (*test)(gpointer item, gpointer data),
78 gpointer data);
79 static void view_details_clear(ViewIface *view);
80 static void view_details_select_all(ViewIface *view);
81 static void view_details_clear_selection(ViewIface *view);
82 static int view_details_count_items(ViewIface *view);
83 static int view_details_count_selected(ViewIface *view);
84 static void view_details_show_cursor(ViewIface *view);
85 static void view_details_get_iter(ViewIface *view,
86 ViewIter *iter, IterFlags flags);
87 static void view_details_get_iter_at_point(ViewIface *view, ViewIter *iter,
88 GdkWindow *src, int x, int y);
89 static void view_details_cursor_to_iter(ViewIface *view, ViewIter *iter);
90 static void view_details_set_selected(ViewIface *view,
91 ViewIter *iter,
92 gboolean selected);
93 static gboolean view_details_get_selected(ViewIface *view, ViewIter *iter);
94 static void view_details_select_only(ViewIface *view, ViewIter *iter);
95 static void view_details_set_frozen(ViewIface *view, gboolean frozen);
96 static void view_details_wink_item(ViewIface *view, ViewIter *iter);
97 static void view_details_autosize(ViewIface *view);
98 static gboolean view_details_cursor_visible(ViewIface *view);
99 static void view_details_set_base(ViewIface *view, ViewIter *iter);
100 static void view_details_start_lasso_box(ViewIface *view,
101 GdkEventButton *event);
102 static void view_details_extend_tip(ViewIface *view,
103 ViewIter *iter, GString *tip);
104 static gboolean view_details_auto_scroll_callback(ViewIface *view);
106 static DirItem *iter_peek(ViewIter *iter);
107 static DirItem *iter_prev(ViewIter *iter);
108 static DirItem *iter_next(ViewIter *iter);
109 static void make_iter(ViewDetails *view_details, ViewIter *iter,
110 IterFlags flags);
111 static void make_item_iter(ViewDetails *view_details, ViewIter *iter, int i);
112 static void view_details_tree_model_init(GtkTreeModelIface *iface);
113 static gboolean details_get_sort_column_id(GtkTreeSortable *sortable,
114 gint *sort_column_id,
115 GtkSortType *order);
116 static void details_set_sort_column_id(GtkTreeSortable *sortable,
117 gint sort_column_id,
118 GtkSortType order);
119 static void details_set_sort_func(GtkTreeSortable *sortable,
120 gint sort_column_id,
121 GtkTreeIterCompareFunc func,
122 gpointer data,
123 GtkDestroyNotify destroy);
124 static void details_set_default_sort_func(GtkTreeSortable *sortable,
125 GtkTreeIterCompareFunc func,
126 gpointer data,
127 GtkDestroyNotify destroy);
128 static gboolean details_has_default_sort_func(GtkTreeSortable *sortable);
129 static void view_details_sortable_init(GtkTreeSortableIface *iface);
130 static void set_selected(ViewDetails *view_details, int i, gboolean selected);
131 static gboolean get_selected(ViewDetails *view_details, int i);
132 static void free_view_item(ViewItem *view_item);
133 static void details_update_header_visibility(ViewDetails *view_details);
134 static void set_lasso(ViewDetails *view_details, int x, int y);
135 static void cancel_wink(ViewDetails *view_details);
138 /****************************************************************
139 * EXTERNAL INTERFACE *
140 ****************************************************************/
142 GtkWidget *view_details_new(FilerWindow *filer_window)
144 ViewDetails *view_details;
146 view_details = g_object_new(view_details_get_type(), NULL);
147 view_details->filer_window = filer_window;
149 gtk_range_set_adjustment(GTK_RANGE(filer_window->scrollbar),
150 gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(view_details)));
152 if (filer_window->sort_type != -1)
153 view_details_sort((ViewIface *) view_details);
155 details_update_header_visibility(view_details);
157 return GTK_WIDGET(view_details);
160 GType view_details_get_type(void)
162 static GType type = 0;
164 if (!type)
166 static const GTypeInfo info =
168 sizeof (ViewDetailsClass),
169 NULL, /* base_init */
170 NULL, /* base_finalise */
171 view_details_class_init,
172 NULL, /* class_finalise */
173 NULL, /* class_data */
174 sizeof(ViewDetails),
175 0, /* n_preallocs */
176 view_details_init
178 static const GInterfaceInfo view_iface_info = {
179 view_details_iface_init,
180 NULL, NULL
182 static const GInterfaceInfo tree_model_info = {
183 (GInterfaceInitFunc) view_details_tree_model_init,
184 NULL, NULL
186 static const GInterfaceInfo sortable_info = {
187 (GInterfaceInitFunc) view_details_sortable_init,
188 NULL, NULL
192 type = g_type_register_static(gtk_tree_view_get_type(),
193 "ViewDetails", &info, 0);
195 g_type_add_interface_static(type, VIEW_TYPE_IFACE,
196 &view_iface_info);
197 g_type_add_interface_static(type, GTK_TYPE_TREE_MODEL,
198 &tree_model_info);
199 g_type_add_interface_static(type, GTK_TYPE_TREE_SORTABLE,
200 &sortable_info);
203 return type;
206 /****************************************************************
207 * INTERNAL FUNCTIONS *
208 ****************************************************************/
210 /* Update the visibility of the list headers */
211 static void details_update_header_visibility(ViewDetails *view_details)
213 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view_details),
214 o_display_show_headers.int_value);
217 /* Fulfill the GtkTreeModel requirements */
218 static guint details_get_flags(GtkTreeModel *tree_model)
220 return GTK_TREE_MODEL_LIST_ONLY;
223 static gint details_get_n_columns(GtkTreeModel *tree_model)
225 return N_COLUMNS;
228 static GType details_get_column_type(GtkTreeModel *tree_model, gint index)
230 g_return_val_if_fail(index < N_COLUMNS && index >= 0, G_TYPE_INVALID);
232 if (index == COL_COLOUR)
233 return GDK_TYPE_COLOR;
234 else if (index == COL_ITEM || index == COL_VIEW_ITEM)
235 return G_TYPE_POINTER;
236 else if (index == COL_WEIGHT)
237 return G_TYPE_INT;
238 return G_TYPE_STRING;
241 static gboolean details_get_iter(GtkTreeModel *tree_model,
242 GtkTreeIter *iter,
243 GtkTreePath *path)
245 ViewDetails *view_details = (ViewDetails *) tree_model;
246 gint i;
248 g_return_val_if_fail(gtk_tree_path_get_depth (path) > 0, FALSE);
250 i = gtk_tree_path_get_indices(path)[0];
252 if (i >= view_details->items->len)
253 return FALSE;
255 iter->user_data = GINT_TO_POINTER(i);
257 return TRUE;
260 static GtkTreePath *details_get_path(GtkTreeModel *tree_model,
261 GtkTreeIter *iter)
263 GtkTreePath *retval;
265 retval = gtk_tree_path_new();
266 gtk_tree_path_append_index(retval, GPOINTER_TO_INT(iter->user_data));
268 return retval;
271 static void details_get_value(GtkTreeModel *tree_model,
272 GtkTreeIter *iter,
273 gint column,
274 GValue *value)
276 ViewDetails *view_details = (ViewDetails *) tree_model;
277 gint i;
278 GPtrArray *items = view_details->items;
279 ViewItem *view_item;
280 DirItem *item;
281 mode_t m;
283 g_return_if_fail(column >= 0 && column < N_COLUMNS);
285 i = GPOINTER_TO_INT(iter->user_data);
286 g_return_if_fail(i >= 0 && i < items->len);
287 view_item = (ViewItem *) items->pdata[i];
288 item = view_item->item;
290 if (column == COL_LEAF)
292 g_value_init(value, G_TYPE_STRING);
293 g_value_set_string(value,
294 view_item->utf8_name ? view_item->utf8_name
295 : item->leafname);
296 return;
298 else if (column == COL_VIEW_ITEM)
300 g_value_init(value, G_TYPE_POINTER);
301 g_value_set_pointer(value, view_item);
302 return;
304 else if (column == COL_ITEM)
306 g_value_init(value, G_TYPE_POINTER);
307 g_value_set_pointer(value, item);
308 return;
311 if (item->base_type == TYPE_UNKNOWN)
313 GType type;
314 type = details_get_column_type(tree_model, column);
315 g_value_init(value, type);
316 if (type == G_TYPE_STRING)
317 g_value_set_string(value, "");
318 else if (type == GDK_TYPE_COLOR)
319 g_value_set_boxed(value, NULL);
320 else if (type == G_TYPE_INT)
321 g_value_set_int(value, PANGO_WEIGHT_NORMAL);
322 else
323 g_value_set_object(value, NULL);
325 return;
327 m = item->mode;
329 switch (column)
331 case COL_LEAF:
332 g_value_init(value, G_TYPE_STRING);
333 g_value_set_string(value, item->leafname);
334 break;
335 case COL_COLOUR:
336 g_value_init(value, GDK_TYPE_COLOR);
337 if (view_item->utf8_name)
339 GdkColor red;
340 red.red = 0xffff;
341 red.green = 0;
342 red.blue = 0;
343 g_value_set_boxed(value, &red);
345 else
346 g_value_set_boxed(value,
347 type_get_colour(item, NULL));
348 break;
349 case COL_OWNER:
350 g_value_init(value, G_TYPE_STRING);
351 g_value_set_string(value, user_name(item->uid));
352 break;
353 case COL_GROUP:
354 g_value_init(value, G_TYPE_STRING);
355 g_value_set_string(value, group_name(item->gid));
356 break;
357 case COL_MTIME:
359 gchar *time;
360 time = pretty_time(&item->mtime);
361 g_value_init(value, G_TYPE_STRING);
362 g_value_set_string(value, time);
363 g_free(time);
364 break;
366 case COL_PERM:
367 g_value_init(value, G_TYPE_STRING);
368 g_value_set_string(value, pretty_permissions(m));
369 break;
370 case COL_SIZE:
371 g_value_init(value, G_TYPE_STRING);
372 if (item->base_type != TYPE_DIRECTORY)
373 g_value_set_string(value,
374 format_size(item->size));
375 break;
376 case COL_TYPE:
377 g_value_init(value, G_TYPE_STRING);
378 if(o_display_show_full_type.int_value)
379 g_value_set_string(value,
380 item->flags & ITEM_FLAG_APPDIR? "Application" :
381 mime_type_comment(item->mime_type));
382 else
383 g_value_set_string(value,
384 item->flags & ITEM_FLAG_APPDIR? "App" :
385 S_ISDIR(m) ? "Dir" :
386 S_ISCHR(m) ? "Char" :
387 S_ISBLK(m) ? "Blck" :
388 S_ISLNK(m) ? "Link" :
389 S_ISSOCK(m) ? "Sock" :
390 S_ISFIFO(m) ? "Pipe" :
391 S_ISDOOR(m) ? "Door" :
392 "File");
394 break;
395 case COL_WEIGHT:
396 g_value_init(value, G_TYPE_INT);
397 if (item->flags & ITEM_FLAG_RECENT)
398 g_value_set_int(value, PANGO_WEIGHT_BOLD);
399 else
400 g_value_set_int(value, PANGO_WEIGHT_NORMAL);
401 break;
402 default:
403 g_value_init(value, G_TYPE_STRING);
404 g_value_set_string(value, "Hello");
405 break;
409 static gboolean details_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter)
411 ViewDetails *view_details = (ViewDetails *) tree_model;
412 int i;
414 i = GPOINTER_TO_INT(iter->user_data) + 1;
415 iter->user_data = GINT_TO_POINTER(i);
417 return i < view_details->items->len;
420 static gboolean details_iter_children(GtkTreeModel *tree_model,
421 GtkTreeIter *iter,
422 GtkTreeIter *parent)
424 ViewDetails *view_details = (ViewDetails *) tree_model;
426 /* this is a list, nodes have no children */
427 if (parent)
428 return FALSE;
430 /* but if parent == NULL we return the list itself as children of the
431 * "root"
434 if (view_details->items->len)
436 iter->user_data = GINT_TO_POINTER(0);
437 return TRUE;
439 else
440 return FALSE;
443 static gboolean details_iter_has_child(GtkTreeModel *tree_model,
444 GtkTreeIter *iter)
446 return FALSE;
449 static gint details_iter_n_children(GtkTreeModel *tree_model, GtkTreeIter *iter)
451 ViewDetails *view_details = (ViewDetails *) tree_model;
453 if (iter == NULL)
454 return view_details->items->len;
456 return 0;
459 static gboolean details_iter_nth_child(GtkTreeModel *tree_model,
460 GtkTreeIter *iter,
461 GtkTreeIter *parent,
462 gint n)
464 ViewDetails *view_details = (ViewDetails *) tree_model;
466 if (parent)
467 return FALSE;
469 if (n >= 0 && n < view_details->items->len)
471 iter->user_data = GINT_TO_POINTER(n);
472 return TRUE;
474 else
475 return FALSE;
478 static gboolean details_iter_parent(GtkTreeModel *tree_model,
479 GtkTreeIter *iter,
480 GtkTreeIter *child)
482 return FALSE;
485 /* A ViewDetails is both a GtkTreeView and a GtkTreeModel.
486 * The following functions implement the model interface...
489 static void view_details_tree_model_init(GtkTreeModelIface *iface)
491 iface->get_flags = details_get_flags;
492 iface->get_n_columns = details_get_n_columns;
493 iface->get_column_type = details_get_column_type;
494 iface->get_iter = details_get_iter;
495 iface->get_path = details_get_path;
496 iface->get_value = details_get_value;
497 iface->iter_next = details_iter_next;
498 iface->iter_children = details_iter_children;
499 iface->iter_has_child = details_iter_has_child;
500 iface->iter_n_children = details_iter_n_children;
501 iface->iter_nth_child = details_iter_nth_child;
502 iface->iter_parent = details_iter_parent;
505 static void view_details_sortable_init(GtkTreeSortableIface *iface)
507 iface->get_sort_column_id = details_get_sort_column_id;
508 iface->set_sort_column_id = details_set_sort_column_id;
509 iface->set_sort_func = details_set_sort_func;
510 iface->set_default_sort_func = details_set_default_sort_func;
511 iface->has_default_sort_func = details_has_default_sort_func;
514 static gboolean details_get_sort_column_id(GtkTreeSortable *sortable,
515 gint *sort_column_id,
516 GtkSortType *order)
518 ViewDetails *view_details = (ViewDetails *) sortable;
519 FilerWindow *filer_window = view_details->filer_window;
520 int col;
522 if (!filer_window)
523 return FALSE; /* Not yet initialised */
525 switch (filer_window->sort_type)
527 case SORT_NAME: col = COL_LEAF; break;
528 case SORT_TYPE: col = COL_TYPE; break;
529 case SORT_DATE: col = COL_MTIME; break;
530 case SORT_SIZE: col = COL_SIZE; break;
531 case SORT_OWNER: col = COL_OWNER; break;
532 case SORT_GROUP: col = COL_GROUP; break;
533 default:
534 g_warning("details_get_sort_column_id(): error!");
535 return FALSE;
537 if (sort_column_id)
538 *sort_column_id = col;
539 if (order)
540 *order = filer_window->sort_order;
541 return TRUE;
544 static void details_set_sort_column_id(GtkTreeSortable *sortable,
545 gint sort_column_id,
546 GtkSortType order)
548 ViewDetails *view_details = (ViewDetails *) sortable;
549 FilerWindow *filer_window = view_details->filer_window;
551 if (!filer_window)
552 return; /* Not yet initialised */
554 switch (sort_column_id)
556 case COL_LEAF:
557 display_set_sort_type(filer_window, SORT_NAME, order);
558 break;
559 case COL_SIZE:
560 display_set_sort_type(filer_window, SORT_SIZE, order);
561 break;
562 case COL_MTIME:
563 display_set_sort_type(filer_window, SORT_DATE, order);
564 break;
565 case COL_TYPE:
566 display_set_sort_type(filer_window, SORT_TYPE, order);
567 break;
568 case COL_OWNER:
569 display_set_sort_type(filer_window, SORT_OWNER, order);
570 break;
571 case COL_GROUP:
572 display_set_sort_type(filer_window, SORT_GROUP, order);
573 break;
574 default:
575 g_assert_not_reached();
579 static void details_set_sort_func(GtkTreeSortable *sortable,
580 gint sort_column_id,
581 GtkTreeIterCompareFunc func,
582 gpointer data,
583 GtkDestroyNotify destroy)
585 g_assert_not_reached();
588 static void details_set_default_sort_func(GtkTreeSortable *sortable,
589 GtkTreeIterCompareFunc func,
590 gpointer data,
591 GtkDestroyNotify destroy)
593 g_assert_not_reached();
596 static gboolean details_has_default_sort_func(GtkTreeSortable *sortable)
598 return FALSE;
602 /* End of model implementation */
604 static gboolean is_selected(ViewDetails *view_details, int i)
606 ViewIter iter;
607 iter.i = i;
608 return view_details_get_selected((ViewIface *) view_details, &iter);
611 static gboolean view_details_scroll(GtkWidget *widget, GdkEventScroll *event)
613 GtkTreeView *tree = (GtkTreeView *) widget;
614 GtkTreePath *path = NULL;
616 if (!gtk_tree_view_get_path_at_pos(tree, 0, 1, &path, NULL, NULL, NULL))
617 return TRUE; /* Empty? */
619 if (event->direction == GDK_SCROLL_UP)
620 gtk_tree_path_prev(path);
621 else if (event->direction == GDK_SCROLL_DOWN)
622 gtk_tree_path_next(path);
623 else
624 goto out;
626 gtk_tree_view_scroll_to_cell(tree, path, NULL, TRUE, 0, 0);
627 out:
628 gtk_tree_path_free(path);
629 return TRUE;
632 static gint view_details_key_press(GtkWidget *widget, GdkEventKey *event)
634 if (event->keyval == GDK_Up || event->keyval == GDK_Down ||
635 event->keyval == GDK_Prior || event->keyval == GDK_Next ||
636 event->keyval == GDK_Home || event->keyval == GDK_End) {
637 /* Work around a strange GTK bug that prevents you from moving the cursor
638 * if nothing is selected.
640 if (event->state & GDK_CONTROL_MASK)
642 return GTK_WIDGET_CLASS(parent_class)->key_press_event(widget, event);
644 else
646 /* GTK hard-codes the test for CTRL, and won't move the cursor
647 * if it isn't set.
649 event->state |= GDK_CONTROL_MASK;
650 gtk_propagate_event(widget, (GdkEvent *) event);
651 return TRUE;
654 return FALSE;
657 static gboolean view_details_button_press(GtkWidget *widget,
658 GdkEventButton *bev)
660 FilerWindow *filer_window = ((ViewDetails *) widget)->filer_window;
661 GtkTreeView *tree = (GtkTreeView *) widget;
663 if (bev->window != gtk_tree_view_get_bin_window(tree))
664 return GTK_WIDGET_CLASS(parent_class)->button_press_event(
665 widget, bev);
667 if (dnd_motion_press(widget, bev))
668 filer_perform_action(filer_window, bev);
670 return TRUE;
673 static int get_lasso_index(ViewDetails *view_details, int y)
675 GtkTreeViewColumn *column = NULL;
676 GtkTreePath *path = NULL;
677 GtkTreeView *tree = (GtkTreeView *) view_details;
678 gint cell_y;
679 int i;
681 if (y < 0)
682 y = 0; /* gtk_tree_view_get_path_at_pos can't handle negatives */
684 if (gtk_tree_view_get_path_at_pos(tree, 4, y, &path,
685 &column, NULL, &cell_y))
687 GdkRectangle rect;
688 i = gtk_tree_path_get_indices(path)[0];
689 gtk_tree_view_get_cell_area(tree, path, column, &rect);
690 gtk_tree_path_free(path);
692 if (2 * cell_y > rect.height)
693 i += 1;
695 else
696 i = view_details->items->len;
698 return i;
701 static gboolean select_lasso_cb(ViewIter *iter, gpointer data)
703 int start = ((int *) data)[0];
704 int end = ((int *) data)[1];
705 GdkFunction fn = ((int *) data)[2];
706 ViewDetails *view_details = (ViewDetails *) iter->view;
707 GtkTreeIter titer;
709 titer.user_data = GINT_TO_POINTER(iter->i);
711 if (iter->i < start || iter->i >= end)
712 return gtk_tree_selection_iter_is_selected(
713 view_details->selection, &titer);
715 if (fn == GDK_SET)
716 return TRUE;
718 return !gtk_tree_selection_iter_is_selected(view_details->selection,
719 &titer);
722 static void select_lasso(ViewDetails *view_details, GdkFunction fn)
724 GtkAdjustment *adj;
725 int range[3];
727 adj = gtk_tree_view_get_vadjustment((GtkTreeView *) view_details);
729 range[0] = view_details->lasso_start_index;
730 range[1] = get_lasso_index(view_details,
731 view_details->drag_box_y[1] - adj->value);
732 range[2] = fn;
734 if (range[0] == range[1])
735 return;
736 if (range[0] > range[1])
738 int tmp = range[0];
739 range[0] = range[1];
740 range[1] = tmp;
743 view_select_if((ViewIface *) view_details, select_lasso_cb, &range);
746 static gboolean view_details_button_release(GtkWidget *widget,
747 GdkEventButton *bev)
749 ViewDetails *view_details = (ViewDetails *) widget;
750 FilerWindow *filer_window = view_details->filer_window;
751 GtkTreeView *tree = (GtkTreeView *) widget;
753 if (bev->window != gtk_tree_view_get_bin_window(tree))
754 return GTK_WIDGET_CLASS(parent_class)->button_release_event(
755 widget, bev);
757 if (!dnd_motion_release(bev))
758 filer_perform_action(filer_window, bev);
760 if (motion_buttons_pressed == 0 && view_details->lasso_box)
762 select_lasso(view_details,
763 bev->button == 1 ? GDK_SET : GDK_INVERT);
764 filer_set_autoscroll(filer_window, FALSE);
765 set_lasso(view_details,
766 view_details->drag_box_x[0],
767 view_details->drag_box_y[0]);
768 view_details->lasso_box = FALSE;
771 return TRUE;
774 static gint view_details_motion_notify(GtkWidget *widget, GdkEventMotion *event)
776 ViewDetails *view_details = (ViewDetails *) widget;
777 GtkTreeView *tree = (GtkTreeView *) widget;
779 if (event->window != gtk_tree_view_get_bin_window(tree))
780 return GTK_WIDGET_CLASS(parent_class)->motion_notify_event(
781 widget, event);
783 if (view_details->lasso_box)
785 GtkAdjustment *adj;
786 adj = gtk_tree_view_get_vadjustment(tree);
787 set_lasso(view_details, event->x, event->y + adj->value);
788 return TRUE;
791 return filer_motion_notify(view_details->filer_window, event);
794 static gboolean view_details_expose(GtkWidget *widget, GdkEventExpose *event)
796 GtkTreeView *tree = (GtkTreeView *) widget;
797 GtkTreePath *path = NULL;
798 GdkRectangle focus_rectangle;
799 ViewDetails *view_details = (ViewDetails *) widget;
800 gboolean had_cursor;
802 had_cursor = (GTK_WIDGET_FLAGS(widget) & GTK_HAS_FOCUS) != 0;
804 if (view_details->filer_window->selection_state == GTK_STATE_SELECTED)
805 GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
806 else
807 GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS);
808 GTK_WIDGET_CLASS(parent_class)->expose_event(widget, event);
809 if (had_cursor)
810 GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
812 if (event->window != gtk_tree_view_get_bin_window(tree))
813 return FALSE; /* Not the main area */
815 if (view_details->lasso_box)
817 int x, y, width, height;
818 GtkAdjustment *adj;
820 adj = gtk_tree_view_get_vadjustment(tree);
821 x = MIN(view_details->drag_box_x[0],
822 view_details->drag_box_x[1]);
823 y = MIN(view_details->drag_box_y[0],
824 view_details->drag_box_y[1]);
825 width = abs(view_details->drag_box_x[1] -
826 view_details->drag_box_x[0]);
827 height = abs(view_details->drag_box_y[1] -
828 view_details->drag_box_y[0]);
829 y -= adj->value;
831 if (width && height)
832 gdk_draw_rectangle(event->window,
833 widget->style->fg_gc[GTK_STATE_NORMAL],
834 FALSE, x, y, width - 1, height - 1);
837 if (view_details->wink_item != -1 && view_details->wink_step & 1)
839 GtkTreePath *wink_path;
840 GdkRectangle wink_area;
842 wink_path = gtk_tree_path_new();
843 gtk_tree_path_append_index(wink_path, view_details->wink_item);
844 gtk_tree_view_get_background_area(tree, wink_path,
845 NULL, &wink_area);
846 gtk_tree_path_free(wink_path);
848 if (wink_area.height)
850 /* (visible) */
851 wink_area.width = widget->allocation.width;
852 gdk_draw_rectangle(event->window,
853 widget->style->fg_gc[GTK_STATE_NORMAL],
854 FALSE,
855 wink_area.x + 1,
856 wink_area.y + 1,
857 wink_area.width - 3,
858 wink_area.height - 3);
862 gtk_tree_view_get_cursor(tree, &path, NULL);
863 if (!path)
864 return FALSE; /* No cursor */
865 gtk_tree_view_get_background_area(tree, path, NULL, &focus_rectangle);
866 gtk_tree_path_free(path);
868 if (!focus_rectangle.height)
869 return FALSE; /* Off screen */
871 focus_rectangle.width = widget->allocation.width;
873 gtk_paint_focus(widget->style,
874 event->window,
875 GTK_STATE_NORMAL,
876 NULL,
877 widget,
878 "treeview",
879 focus_rectangle.x,
880 focus_rectangle.y,
881 focus_rectangle.width,
882 focus_rectangle.height);
884 return FALSE;
887 static void view_details_size_request(GtkWidget *widget,
888 GtkRequisition *requisition)
890 ViewDetails *view_details = (ViewDetails *) widget;
892 (*GTK_WIDGET_CLASS(parent_class)->size_request)(widget, requisition);
894 view_details->desired_size = *requisition;
896 requisition->height = 50;
897 requisition->width = 50;
900 static void view_details_drag_data_received(GtkWidget *widget,
901 GdkDragContext *drag_context,
902 gint x, gint y, GtkSelectionData *data, guint info, guint time)
904 /* Just here to override annoying default handler */
907 static void view_details_destroy(GtkObject *obj)
909 ViewDetails *view_details = VIEW_DETAILS(obj);
911 view_details->filer_window = NULL;
912 cancel_wink(view_details);
915 static void view_details_finialize(GObject *object)
917 ViewDetails *view_details = (ViewDetails *) object;
919 g_ptr_array_free(view_details->items, TRUE);
920 view_details->items = NULL;
922 G_OBJECT_CLASS(parent_class)->finalize(object);
925 static void view_details_class_init(gpointer gclass, gpointer data)
927 GObjectClass *object = (GObjectClass *) gclass;
928 GtkWidgetClass *widget = (GtkWidgetClass *) gclass;
930 parent_class = g_type_class_peek_parent(gclass);
932 object->finalize = view_details_finialize;
933 GTK_OBJECT_CLASS(object)->destroy = view_details_destroy;
935 widget->scroll_event = view_details_scroll;
936 widget->key_press_event = view_details_key_press;
937 widget->button_press_event = view_details_button_press;
938 widget->button_release_event = view_details_button_release;
939 widget->motion_notify_event = view_details_motion_notify;
940 widget->expose_event = view_details_expose;
941 widget->size_request = view_details_size_request;
942 widget->drag_data_received = view_details_drag_data_received;
945 * Add the ViewDetails::mono-font style property.
946 * To use this add something like
948 * style "details" {
949 * ViewDetails::mono-font = "Courier 8"
951 * class "ViewDetails" style "details"
953 * to your ~/.gtkrc-2.0
955 gtk_widget_class_install_style_property(widget,
956 g_param_spec_string("mono-font",
957 _("Mono font"),
958 _("Font for displaying mono-spaced text"),
959 "monospace",
960 G_PARAM_READABLE));
964 static gboolean block_focus(GtkWidget *button, GtkDirectionType dir,
965 ViewDetails *view_details)
967 GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
968 return FALSE;
971 static gboolean test_can_change_selection(GtkTreeSelection *sel,
972 GtkTreeModel *model,
973 GtkTreePath *path,
974 gboolean path_currently_selected,
975 gpointer data)
977 ViewDetails *view_details;
979 view_details = VIEW_DETAILS(gtk_tree_selection_get_tree_view(sel));
981 return view_details->can_change_selection != 0;
984 static void selection_changed(GtkTreeSelection *selection,
985 gpointer user_data)
987 ViewDetails *view_details = VIEW_DETAILS(user_data);
989 filer_selection_changed(view_details->filer_window,
990 gtk_get_current_event_time());
994 * Set the font used for a treeview column to that given for the
995 * "mono-font" style property of a widget. This has to be done _after_ the
996 * widget has been realized, because that is when the style information is
997 * set up. This is done by connecting this function to run after the
998 * "realize" signal.
1000 static void set_column_mono_font(GtkWidget *widget, GObject *object)
1002 const gchar *font_name;
1004 gtk_widget_style_get(widget, "mono-font", &font_name, NULL);
1005 g_object_set(object, "font", font_name, NULL);
1008 #define ADD_TEXT_COLUMN(name, model_column) \
1009 cell = gtk_cell_renderer_text_new(); \
1010 column = gtk_tree_view_column_new_with_attributes(name, cell, \
1011 "text", model_column, \
1012 "foreground-gdk", COL_COLOUR, \
1013 "weight", COL_WEIGHT, \
1014 NULL); \
1015 gtk_tree_view_append_column(treeview, column); \
1016 g_signal_connect(column->button, "grab-focus", \
1017 G_CALLBACK(block_focus), view_details);
1019 static void view_details_init(GTypeInstance *object, gpointer gclass)
1021 GtkTreeView *treeview = (GtkTreeView *) object;
1022 GtkTreeViewColumn *column;
1023 GtkCellRenderer *cell;
1024 ViewDetails *view_details = (ViewDetails *) object;
1026 view_details->items = g_ptr_array_new();
1027 view_details->cursor_base = -1;
1028 view_details->wink_item = -1;
1029 view_details->desired_size.width = -1;
1030 view_details->desired_size.height = -1;
1031 view_details->can_change_selection = 0;
1032 view_details->lasso_box = FALSE;
1034 view_details->selection = gtk_tree_view_get_selection(treeview);
1035 gtk_tree_selection_set_mode(view_details->selection,
1036 GTK_SELECTION_MULTIPLE);
1037 gtk_tree_selection_set_select_function(view_details->selection,
1038 test_can_change_selection, view_details, NULL);
1040 /* Sorting */
1041 view_details->sort_fn = NULL;
1043 gtk_tree_view_set_model(treeview, GTK_TREE_MODEL(view_details));
1044 /* Do this after set_model, because that can generate this
1045 * signal...
1047 g_signal_connect(view_details->selection, "changed",
1048 G_CALLBACK(selection_changed), view_details);
1050 /* Icon */
1051 cell = cell_icon_new(view_details);
1052 column = gtk_tree_view_column_new_with_attributes(NULL, cell,
1053 "item", COL_VIEW_ITEM,
1054 NULL);
1055 gtk_tree_view_append_column(treeview, column);
1057 ADD_TEXT_COLUMN(_("_Name"), COL_LEAF);
1058 gtk_tree_view_column_set_sort_column_id(column, COL_LEAF);
1059 gtk_tree_view_column_set_resizable(column, TRUE);
1060 ADD_TEXT_COLUMN(_("_Type"), COL_TYPE);
1061 gtk_tree_view_column_set_sort_column_id(column, COL_TYPE);
1062 gtk_tree_view_column_set_resizable(column, TRUE);
1063 ADD_TEXT_COLUMN(_("_Permissions"), COL_PERM);
1064 g_object_set(G_OBJECT(cell), "font", "monospace", NULL);
1065 g_signal_connect_after(object, "realize",
1066 G_CALLBACK(set_column_mono_font),
1067 G_OBJECT(cell));
1068 ADD_TEXT_COLUMN(_("_Owner"), COL_OWNER);
1069 gtk_tree_view_column_set_sort_column_id(column, COL_OWNER);
1070 ADD_TEXT_COLUMN(_("_Group"), COL_GROUP);
1071 gtk_tree_view_column_set_sort_column_id(column, COL_GROUP);
1072 ADD_TEXT_COLUMN(_("_Size"), COL_SIZE);
1073 g_object_set(G_OBJECT(cell), "xalign", 1.0, "font", "monospace", NULL);
1074 g_signal_connect_after(object, "realize",
1075 G_CALLBACK(set_column_mono_font),
1076 G_OBJECT(cell));
1077 gtk_tree_view_column_set_sort_column_id(column, COL_SIZE);
1078 ADD_TEXT_COLUMN(_("Last _Modified"), COL_MTIME);
1079 gtk_tree_view_column_set_sort_column_id(column, COL_MTIME);
1082 /* Create the handers for the View interface */
1083 static void view_details_iface_init(gpointer giface, gpointer iface_data)
1085 ViewIfaceClass *iface = giface;
1087 g_assert(G_TYPE_FROM_INTERFACE(iface) == VIEW_TYPE_IFACE);
1089 /* override stuff */
1090 iface->sort = view_details_sort;
1091 iface->style_changed = view_details_style_changed;
1092 iface->add_items = view_details_add_items;
1093 iface->update_items = view_details_update_items;
1094 iface->delete_if = view_details_delete_if;
1095 iface->clear = view_details_clear;
1096 iface->select_all = view_details_select_all;
1097 iface->clear_selection = view_details_clear_selection;
1098 iface->count_items = view_details_count_items;
1099 iface->count_selected = view_details_count_selected;
1100 iface->show_cursor = view_details_show_cursor;
1101 iface->get_iter = view_details_get_iter;
1102 iface->get_iter_at_point = view_details_get_iter_at_point;
1103 iface->cursor_to_iter = view_details_cursor_to_iter;
1104 iface->set_selected = view_details_set_selected;
1105 iface->get_selected = view_details_get_selected;
1106 iface->set_frozen = view_details_set_frozen;
1107 iface->select_only = view_details_select_only;
1108 iface->wink_item = view_details_wink_item;
1109 iface->autosize = view_details_autosize;
1110 iface->cursor_visible = view_details_cursor_visible;
1111 iface->set_base = view_details_set_base;
1112 iface->start_lasso_box = view_details_start_lasso_box;
1113 iface->extend_tip = view_details_extend_tip;
1114 iface->auto_scroll_callback = view_details_auto_scroll_callback;
1117 /* Implementations of the View interface. See view_iface.c for comments. */
1119 static void view_details_style_changed(ViewIface *view, int flags)
1121 ViewDetails *view_details = (ViewDetails *) view;
1122 GtkTreeModel *model = (GtkTreeModel *) view;
1123 GtkTreePath *path;
1124 ViewItem **items = (ViewItem **) view_details->items->pdata;
1125 int i;
1126 int n = view_details->items->len;
1128 path = gtk_tree_path_new();
1129 gtk_tree_path_append_index(path, 0);
1131 for (i = 0; i < n; i++)
1133 GtkTreeIter iter;
1134 ViewItem *item = items[i];
1136 iter.user_data = GINT_TO_POINTER(i);
1137 if (item->image)
1139 g_object_unref(G_OBJECT(item->image));
1140 item->image = NULL;
1142 gtk_tree_model_row_changed(model, path, &iter);
1143 gtk_tree_path_next(path);
1146 gtk_tree_path_free(path);
1148 gtk_tree_view_columns_autosize((GtkTreeView *) view);
1150 if (flags & VIEW_UPDATE_HEADERS)
1151 details_update_header_visibility(view_details);
1154 static gint wrap_sort(gconstpointer a, gconstpointer b,
1155 ViewDetails *view_details)
1157 ViewItem *ia = *(ViewItem **) a;
1158 ViewItem *ib = *(ViewItem **) b;
1160 if (view_details->filer_window->sort_order == GTK_SORT_ASCENDING)
1161 return view_details->sort_fn(ia->item, ib->item);
1162 else
1163 return -view_details->sort_fn(ia->item, ib->item);
1166 static void resort(ViewDetails *view_details)
1168 ViewItem **items = (ViewItem **) view_details->items->pdata;
1169 gint i, len = view_details->items->len;
1170 guint *new_order;
1171 GtkTreePath *path;
1172 int wink_item = view_details->wink_item;
1174 if (!len)
1175 return;
1177 for (i = len - 1; i >= 0; i--)
1178 items[i]->old_pos = i;
1180 switch (view_details->filer_window->sort_type)
1182 case SORT_NAME: view_details->sort_fn = sort_by_name; break;
1183 case SORT_TYPE: view_details->sort_fn = sort_by_type; break;
1184 case SORT_DATE: view_details->sort_fn = sort_by_date; break;
1185 case SORT_SIZE: view_details->sort_fn = sort_by_size; break;
1186 case SORT_OWNER: view_details->sort_fn = sort_by_owner; break;
1187 case SORT_GROUP: view_details->sort_fn = sort_by_group; break;
1188 default:
1189 g_assert_not_reached();
1192 g_ptr_array_sort_with_data(view_details->items,
1193 (GCompareDataFunc) wrap_sort,
1194 view_details);
1196 new_order = g_new(guint, len);
1197 for (i = len - 1; i >= 0; i--)
1199 new_order[i] = items[i]->old_pos;
1200 if (wink_item == items[i]->old_pos)
1201 wink_item = i;
1204 view_details->wink_item = wink_item;
1206 path = gtk_tree_path_new();
1207 gtk_tree_model_rows_reordered((GtkTreeModel *) view_details,
1208 path, NULL, new_order);
1209 gtk_tree_path_free(path);
1210 g_free(new_order);
1213 static void view_details_sort(ViewIface *view)
1215 resort((ViewDetails *) view);
1216 gtk_tree_sortable_sort_column_changed((GtkTreeSortable *) view);
1219 static void view_details_add_items(ViewIface *view, GPtrArray *new_items)
1221 ViewDetails *view_details = (ViewDetails *) view;
1222 FilerWindow *filer_window = view_details->filer_window;
1223 GPtrArray *items = view_details->items;
1224 GtkTreeIter iter;
1225 int i;
1226 GtkTreePath *path;
1227 GtkTreeModel *model = (GtkTreeModel *) view;
1229 iter.user_data = GINT_TO_POINTER(items->len);
1230 path = details_get_path(model, &iter);
1232 for (i = 0; i < new_items->len; i++)
1234 DirItem *item = (DirItem *) new_items->pdata[i];
1235 char *leafname = item->leafname;
1236 ViewItem *vitem;
1238 if(!filer_match_filter(filer_window, item))
1239 continue;
1240 if (leafname[0] == '.')
1242 if (leafname[1] == '\0')
1243 continue; /* Never show '.' */
1245 if (leafname[1] == '.' &&
1246 leafname[2] == '\0')
1247 continue; /* Never show '..' */
1250 vitem = g_new(ViewItem, 1);
1251 vitem->item = item;
1252 vitem->image = NULL;
1253 if (!g_utf8_validate(leafname, -1, NULL))
1254 vitem->utf8_name = to_utf8(leafname);
1255 else
1256 vitem->utf8_name = NULL;
1258 g_ptr_array_add(items, vitem);
1260 iter.user_data = GINT_TO_POINTER(items->len - 1);
1261 gtk_tree_model_row_inserted(model, path, &iter);
1262 gtk_tree_path_next(path);
1265 gtk_tree_path_free(path);
1267 resort(view_details);
1270 /* Find an item in the sorted array.
1271 * Returns the item number, or -1 if not found.
1273 static int details_find_item(ViewDetails *view_details, DirItem *item)
1275 ViewItem **items, tmp, *tmpp;
1276 int lower, upper;
1278 g_return_val_if_fail(view_details != NULL, -1);
1279 g_return_val_if_fail(item != NULL, -1);
1281 tmp.item = item;
1282 tmpp = &tmp;
1284 items = (ViewItem **) view_details->items->pdata;
1286 /* If item is here, then: lower <= i < upper */
1287 lower = 0;
1288 upper = view_details->items->len;
1290 while (lower < upper)
1292 int i, cmp;
1294 i = (lower + upper) >> 1;
1296 cmp = wrap_sort(&items[i], &tmpp, view_details);
1297 if (cmp == 0)
1298 return i;
1300 if (cmp > 0)
1301 upper = i;
1302 else
1303 lower = i + 1;
1306 return -1;
1309 static void view_details_update_items(ViewIface *view, GPtrArray *items)
1311 ViewDetails *view_details = (ViewDetails *) view;
1312 FilerWindow *filer_window = view_details->filer_window;
1313 int i;
1314 GtkTreeModel *model = (GtkTreeModel *) view_details;
1316 g_return_if_fail(items->len > 0);
1318 /* The item data has already been modified, so this gives the
1319 * final sort order...
1321 resort(view_details);
1323 for (i = 0; i < items->len; i++)
1325 DirItem *item = (DirItem *) items->pdata[i];
1326 const gchar *leafname = item->leafname;
1327 int j;
1329 if (!filer_match_filter(filer_window, item))
1330 continue;
1332 j = details_find_item(view_details, item);
1334 if (j < 0)
1335 g_warning("Failed to find '%s'\n", leafname);
1336 else
1338 GtkTreePath *path;
1339 GtkTreeIter iter;
1340 ViewItem *view_item = view_details->items->pdata[j];
1341 if (view_item->image)
1343 g_object_unref(G_OBJECT(view_item->image));
1344 view_item->image = NULL;
1346 path = gtk_tree_path_new();
1347 gtk_tree_path_append_index(path, j);
1348 iter.user_data = GINT_TO_POINTER(j);
1349 gtk_tree_model_row_changed(model, path, &iter);
1354 static void view_details_delete_if(ViewIface *view,
1355 gboolean (*test)(gpointer item, gpointer data),
1356 gpointer data)
1358 GtkTreePath *path;
1359 ViewDetails *view_details = (ViewDetails *) view;
1360 int i = 0;
1361 GPtrArray *items = view_details->items;
1362 GtkTreeModel *model = (GtkTreeModel *) view;
1364 path = gtk_tree_path_new();
1366 gtk_tree_path_append_index(path, i);
1368 while (i < items->len)
1370 ViewItem *item = items->pdata[i];
1372 if (test(item->item, data))
1374 free_view_item(items->pdata[i]);
1375 g_ptr_array_remove_index(items, i);
1376 gtk_tree_model_row_deleted(model, path);
1378 else
1380 i++;
1381 gtk_tree_path_next(path);
1385 gtk_tree_path_free(path);
1388 static void view_details_clear(ViewIface *view)
1390 GtkTreePath *path;
1391 GPtrArray *items = ((ViewDetails *) view)->items;
1392 GtkTreeModel *model = (GtkTreeModel *) view;
1394 path = gtk_tree_path_new();
1395 gtk_tree_path_append_index(path, items->len);
1397 while (gtk_tree_path_prev(path))
1398 gtk_tree_model_row_deleted(model, path);
1400 g_ptr_array_set_size(items, 0);
1401 gtk_tree_path_free(path);
1404 static void view_details_select_all(ViewIface *view)
1406 ViewDetails *view_details = (ViewDetails *) view;
1408 view_details->can_change_selection++;
1409 gtk_tree_selection_select_all(view_details->selection);
1410 view_details->can_change_selection--;
1413 static void view_details_clear_selection(ViewIface *view)
1415 ViewDetails *view_details = (ViewDetails *) view;
1417 view_details->can_change_selection++;
1418 gtk_tree_selection_unselect_all(view_details->selection);
1419 view_details->can_change_selection--;
1422 static int view_details_count_items(ViewIface *view)
1424 ViewDetails *view_details = (ViewDetails *) view;
1426 return view_details->items->len;
1429 #if GTK_MINOR_VERSION < 2
1430 static void view_details_count_inc(GtkTreeModel *model, GtkTreePath *path,
1431 GtkTreeIter *iter, gpointer data)
1433 int *count = (int *) data;
1434 (*count) += 1;
1436 #endif
1438 static int view_details_count_selected(ViewIface *view)
1440 ViewDetails *view_details = (ViewDetails *) view;
1442 #if GTK_MINOR_VERSION >= 2
1443 return gtk_tree_selection_count_selected_rows(view_details->selection);
1444 #else
1445 int count = 0;
1447 gtk_tree_selection_selected_foreach(view_details->selection,
1448 view_details_count_inc, &count);
1449 return count;
1450 #endif
1453 static void view_details_show_cursor(ViewIface *view)
1457 static void view_details_get_iter(ViewIface *view,
1458 ViewIter *iter, IterFlags flags)
1460 make_iter((ViewDetails *) view, iter, flags);
1463 static void view_details_get_iter_at_point(ViewIface *view, ViewIter *iter,
1464 GdkWindow *src, int x, int y)
1466 ViewDetails *view_details = (ViewDetails *) view;
1467 GtkTreeView *tree = (GtkTreeView *) view;
1468 GtkTreePath *path = NULL;
1469 int i = -1;
1470 gint cell_y;
1472 if (gtk_tree_view_get_path_at_pos(tree, x, y + 4, &path, NULL,
1473 NULL, &cell_y))
1475 g_return_if_fail(path != NULL);
1477 if (cell_y > 8)
1478 i = gtk_tree_path_get_indices(path)[0];
1479 gtk_tree_path_free(path);
1482 make_item_iter(view_details, iter, i);
1485 static void view_details_cursor_to_iter(ViewIface *view, ViewIter *iter)
1487 GtkTreePath *path;
1488 ViewDetails *view_details = (ViewDetails *) view;
1490 path = gtk_tree_path_new();
1492 if (iter)
1493 gtk_tree_path_append_index(path, iter->i);
1494 else
1496 /* Using depth zero or index -1 gives an error, but this
1497 * is OK!
1499 gtk_tree_path_append_index(path, view_details->items->len);
1502 gtk_tree_view_set_cursor((GtkTreeView *) view, path, NULL, FALSE);
1503 gtk_tree_path_free(path);
1506 static void set_selected(ViewDetails *view_details, int i, gboolean selected)
1508 GtkTreeIter iter;
1510 iter.user_data = GINT_TO_POINTER(i);
1511 view_details->can_change_selection++;
1512 if (selected)
1513 gtk_tree_selection_select_iter(view_details->selection, &iter);
1514 else
1515 gtk_tree_selection_unselect_iter(view_details->selection,
1516 &iter);
1517 view_details->can_change_selection--;
1520 static void view_details_set_selected(ViewIface *view,
1521 ViewIter *iter,
1522 gboolean selected)
1524 set_selected((ViewDetails *) view, iter->i, selected);
1527 static gboolean get_selected(ViewDetails *view_details, int i)
1529 GtkTreeIter iter;
1531 iter.user_data = GINT_TO_POINTER(i);
1533 return gtk_tree_selection_iter_is_selected(view_details->selection,
1534 &iter);
1537 static gboolean view_details_get_selected(ViewIface *view, ViewIter *iter)
1539 return get_selected((ViewDetails *) view, iter->i);
1542 static void view_details_select_only(ViewIface *view, ViewIter *iter)
1544 ViewDetails *view_details = (ViewDetails *) view;
1545 GtkTreePath *path;
1547 path = gtk_tree_path_new();
1548 gtk_tree_path_append_index(path, iter->i);
1549 view_details->can_change_selection++;
1550 gtk_tree_selection_unselect_all(view_details->selection);
1551 gtk_tree_selection_select_range(view_details->selection, path, path);
1552 view_details->can_change_selection--;
1553 gtk_tree_path_free(path);
1556 static void view_details_set_frozen(ViewIface *view, gboolean frozen)
1560 static void redraw_wink_area(ViewDetails *view_details)
1562 GtkTreePath *wink_path;
1563 GdkRectangle wink_area;
1564 GtkTreeView *tree = (GtkTreeView *) view_details;
1566 g_return_if_fail(view_details->wink_item >= 0);
1568 wink_path = gtk_tree_path_new();
1569 gtk_tree_path_append_index(wink_path, view_details->wink_item);
1570 gtk_tree_view_get_background_area(tree, wink_path, NULL, &wink_area);
1571 gtk_tree_path_free(wink_path);
1573 if (wink_area.height)
1575 GdkWindow *window;
1576 window = gtk_tree_view_get_bin_window(tree);
1578 wink_area.width = GTK_WIDGET(tree)->allocation.width;
1579 gdk_window_invalidate_rect(window, &wink_area, FALSE);
1583 static void cancel_wink(ViewDetails *view_details)
1585 if (view_details->wink_item == -1)
1586 return;
1588 if (view_details->filer_window)
1589 redraw_wink_area(view_details);
1591 view_details->wink_item = -1;
1592 g_source_remove(view_details->wink_timeout);
1595 static gboolean wink_timeout(ViewDetails *view_details)
1597 view_details->wink_step--;
1598 if (view_details->wink_step < 1)
1600 cancel_wink(view_details);
1601 return FALSE;
1604 redraw_wink_area(view_details);
1606 return TRUE;
1609 static void view_details_wink_item(ViewIface *view, ViewIter *iter)
1611 ViewDetails *view_details = (ViewDetails *) view;
1612 GtkTreePath *path;
1614 cancel_wink(view_details);
1615 if (!iter)
1616 return;
1618 view_details->wink_item = iter->i;
1619 view_details->wink_timeout = g_timeout_add(70,
1620 (GSourceFunc) wink_timeout, view_details);
1621 view_details->wink_step = 7;
1622 redraw_wink_area(view_details);
1624 path = gtk_tree_path_new();
1625 gtk_tree_path_append_index(path, iter->i);
1626 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(view),
1627 path, NULL, FALSE, 0, 0);
1628 gtk_tree_path_free(path);
1631 static void view_details_autosize(ViewIface *view)
1633 ViewDetails *view_details = (ViewDetails *) view;
1634 FilerWindow *filer_window = view_details->filer_window;
1635 int max_width = (o_filer_size_limit.int_value * monitor_width) / 100;
1636 int max_height = (o_filer_size_limit.int_value * monitor_height) / 100;
1637 int h;
1638 GtkRequisition req;
1640 gtk_widget_queue_resize(GTK_WIDGET(view));
1641 gtk_widget_size_request(GTK_WIDGET(view), &req);
1643 h = MAX(view_details->desired_size.height, SMALL_HEIGHT);
1645 filer_window_set_size(filer_window,
1646 MIN(view_details->desired_size.width, max_width),
1647 MIN(h, max_height));
1650 static gboolean view_details_cursor_visible(ViewIface *view)
1652 GtkTreePath *path = NULL;
1654 gtk_tree_view_get_cursor((GtkTreeView *) view, &path, NULL);
1656 if (path)
1657 gtk_tree_path_free(path);
1659 return path != NULL;
1662 static void view_details_set_base(ViewIface *view, ViewIter *iter)
1664 ViewDetails *view_details = (ViewDetails *) view;
1666 view_details->cursor_base = iter->i;
1669 /* Change the dynamic corner of the lasso box and trigger a redraw */
1670 static void set_lasso(ViewDetails *view_details, int x, int y)
1672 GdkWindow *window;
1673 GdkRectangle area;
1674 int minx, miny, maxx, maxy;
1676 if (x == view_details->drag_box_x[1] &&
1677 y == view_details->drag_box_y[1])
1678 return;
1680 /* Get old region */
1681 minx = MIN(view_details->drag_box_x[0], view_details->drag_box_x[1]);
1682 miny = MIN(view_details->drag_box_y[0], view_details->drag_box_y[1]);
1683 maxx = MAX(view_details->drag_box_x[0], view_details->drag_box_x[1]);
1684 maxy = MAX(view_details->drag_box_y[0], view_details->drag_box_y[1]);
1686 /* Enlarge to cover new point */
1687 minx = MIN(minx, x);
1688 miny = MIN(miny, y);
1689 maxx = MAX(maxx, x);
1690 maxy = MAX(maxy, y);
1692 area.x = minx;
1693 area.y = miny;
1694 area.width = maxx - minx;
1695 area.height = maxy - miny;
1697 view_details->drag_box_x[1] = x;
1698 view_details->drag_box_y[1] = y;
1700 window = gtk_tree_view_get_bin_window((GtkTreeView *) view_details);
1701 if (area.width && area.height)
1703 GtkAdjustment *adj;
1705 adj = gtk_tree_view_get_vadjustment((GtkTreeView *)
1706 view_details);
1707 area.y -= adj->value;
1708 gdk_window_invalidate_rect(window, &area, FALSE);
1712 static void view_details_start_lasso_box(ViewIface *view, GdkEventButton *event)
1714 ViewDetails *view_details = (ViewDetails *) view;
1715 GtkAdjustment *adj;
1717 adj = gtk_tree_view_get_vadjustment((GtkTreeView *) view_details);
1719 view_details->lasso_start_index = get_lasso_index(view_details,
1720 event->y);
1722 filer_set_autoscroll(view_details->filer_window, TRUE);
1724 view_details->drag_box_x[0] = view_details->drag_box_x[1] = event->x;
1725 view_details->drag_box_y[0] = view_details->drag_box_y[1] = event->y +
1726 adj->value;
1727 view_details->lasso_box = TRUE;
1730 static void view_details_extend_tip(ViewIface *view,
1731 ViewIter *iter, GString *tip)
1735 static DirItem *iter_init(ViewIter *iter)
1737 ViewDetails *view_details = (ViewDetails *) iter->view;
1738 int i = -1;
1739 int n = view_details->items->len;
1740 int flags = iter->flags;
1742 iter->peek = iter_peek;
1744 if (iter->n_remaining == 0)
1745 return NULL;
1747 if (flags & VIEW_ITER_FROM_CURSOR)
1749 GtkTreePath *path;
1750 gtk_tree_view_get_cursor((GtkTreeView *) view_details,
1751 &path, NULL);
1752 if (!path)
1753 return NULL; /* No cursor */
1754 i = gtk_tree_path_get_indices(path)[0];
1755 gtk_tree_path_free(path);
1757 else if (flags & VIEW_ITER_FROM_BASE)
1758 i = view_details->cursor_base;
1760 if (i < 0 || i >= n)
1762 /* Either a normal iteration, or an iteration from an
1763 * invalid starting point.
1765 if (flags & VIEW_ITER_BACKWARDS)
1766 i = n - 1;
1767 else
1768 i = 0;
1771 if (i < 0 || i >= n)
1772 return NULL; /* No items at all! */
1774 iter->next = flags & VIEW_ITER_BACKWARDS ? iter_prev : iter_next;
1775 iter->n_remaining--;
1776 iter->i = i;
1778 if (flags & VIEW_ITER_SELECTED && !is_selected(view_details, i))
1779 return iter->next(iter);
1780 return iter->peek(iter);
1783 static DirItem *iter_prev(ViewIter *iter)
1785 ViewDetails *view_details = (ViewDetails *) iter->view;
1786 int n = view_details->items->len;
1787 int i = iter->i;
1789 g_return_val_if_fail(iter->n_remaining >= 0, NULL);
1791 /* i is the last item returned (always valid) */
1793 g_return_val_if_fail(i >= 0 && i < n, NULL);
1795 while (iter->n_remaining)
1797 i--;
1798 iter->n_remaining--;
1800 if (i == -1)
1801 i = n - 1;
1803 g_return_val_if_fail(i >= 0 && i < n, NULL);
1805 if (iter->flags & VIEW_ITER_SELECTED &&
1806 !is_selected(view_details, i))
1807 continue;
1809 iter->i = i;
1810 return ((ViewItem *) view_details->items->pdata[i])->item;
1813 iter->i = -1;
1814 return NULL;
1817 static DirItem *iter_next(ViewIter *iter)
1819 ViewDetails *view_details = (ViewDetails *) iter->view;
1820 int n = view_details->items->len;
1821 int i = iter->i;
1823 g_return_val_if_fail(iter->n_remaining >= 0, NULL);
1825 /* i is the last item returned (always valid) */
1827 g_return_val_if_fail(i >= 0 && i < n, NULL);
1829 while (iter->n_remaining)
1831 i++;
1832 iter->n_remaining--;
1834 if (i == n)
1835 i = 0;
1837 g_return_val_if_fail(i >= 0 && i < n, NULL);
1839 if (iter->flags & VIEW_ITER_SELECTED &&
1840 !is_selected(view_details, i))
1841 continue;
1843 iter->i = i;
1844 return ((ViewItem *) view_details->items->pdata[i])->item;
1847 iter->i = -1;
1848 return NULL;
1851 static DirItem *iter_peek(ViewIter *iter)
1853 ViewDetails *view_details = (ViewDetails *) iter->view;
1854 int n = view_details->items->len;
1855 int i = iter->i;
1857 if (i == -1)
1858 return NULL;
1860 g_return_val_if_fail(i >= 0 && i < n, NULL);
1862 return ((ViewItem *) view_details->items->pdata[i])->item;
1865 /* Set the iterator to return 'i' on the next peek().
1866 * If i is -1, returns NULL on next peek().
1868 static void make_item_iter(ViewDetails *view_details, ViewIter *iter, int i)
1870 make_iter(view_details, iter, 0);
1872 g_return_if_fail(i >= -1 && i < (int) view_details->items->len);
1874 iter->i = i;
1875 iter->next = iter_next;
1876 iter->peek = iter_peek;
1877 iter->n_remaining = 0;
1880 static void make_iter(ViewDetails *view_details, ViewIter *iter,
1881 IterFlags flags)
1883 iter->view = (ViewIface *) view_details;
1884 iter->next = iter_init;
1885 iter->peek = NULL;
1886 iter->i = -1;
1888 iter->flags = flags;
1890 if (flags & VIEW_ITER_ONE_ONLY)
1892 iter->n_remaining = 1;
1893 iter->next(iter);
1895 else
1896 iter->n_remaining = view_details->items->len;
1899 static void free_view_item(ViewItem *view_item)
1901 if (view_item->image)
1902 g_object_unref(G_OBJECT(view_item->image));
1903 g_free(view_item->utf8_name);
1904 g_free(view_item);
1907 static gboolean view_details_auto_scroll_callback(ViewIface *view)
1909 GtkTreeView *tree = (GtkTreeView *) view;
1910 ViewDetails *view_details = (ViewDetails *) view;
1911 FilerWindow *filer_window = view_details->filer_window;
1912 GtkRange *scrollbar = (GtkRange *) filer_window->scrollbar;
1913 GtkAdjustment *adj;
1914 GdkWindow *window;
1915 gint x, y, w, h;
1916 GdkModifierType mask;
1917 int diff = 0;
1919 window = gtk_tree_view_get_bin_window(tree);
1921 gdk_window_get_pointer(window, &x, &y, &mask);
1922 gdk_drawable_get_size(window, &w, NULL);
1924 adj = gtk_range_get_adjustment(scrollbar);
1925 h = adj->page_size;
1927 if ((x < 0 || x > w || y < 0 || y > h) && !view_details->lasso_box)
1928 return FALSE; /* Out of window - stop */
1930 if (y < AUTOSCROLL_STEP)
1931 diff = y - AUTOSCROLL_STEP;
1932 else if (y > h - AUTOSCROLL_STEP)
1933 diff = AUTOSCROLL_STEP + y - h;
1935 if (diff)
1937 int old = adj->value;
1938 int value = old + diff;
1940 value = CLAMP(value, 0, adj->upper - adj->page_size);
1941 gtk_adjustment_set_value(adj, value);
1943 if (adj->value != old)
1944 dnd_spring_abort();
1947 return TRUE;