4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2002, the ROX-Filer team.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
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
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_collection.c - a subclass of Collection, used for displaying files */
32 #include "collection.h"
33 #include "view_iface.h"
34 #include "view_collection.h"
39 #include "gui_support.h"
45 #include "display.h" /* XXX */
46 #include "toolbar.h" /* XXX */
47 #include "filer.h" /* XXX */
48 #include "menu.h" /* XXX */
50 #define MIN_ITEM_WIDTH 64
53 /* The handler of the signal handler for scroll events.
54 * This is used to cancel spring loading when autoscrolling is used.
56 static gulong scrolled_signal
= -1;
57 static GtkObject
*scrolled_adj
= NULL
; /* The object watched */
60 /* Item we are about to display a tooltip for */
61 static DirItem
*tip_item
= NULL
;
63 static gpointer parent_class
= NULL
;
65 struct _ViewCollectionClass
{
66 GtkViewportClass parent
;
69 struct _ViewCollection
{
72 Collection
*collection
;
73 FilerWindow
*filer_window
; /* Used for styles, etc */
75 int cursor_base
; /* Cursor when minibuffer opened */
78 typedef struct _Template Template
;
82 GdkRectangle leafname
;
86 /* GC for drawing colour filenames */
87 static GdkGC
*type_gc
= NULL
;
89 /* Static prototypes */
90 static void view_collection_finialize(GObject
*object
);
91 static void view_collection_class_init(gpointer gclass
, gpointer data
);
92 static void view_collection_init(GTypeInstance
*object
, gpointer gclass
);
94 static void draw_item(GtkWidget
*widget
,
98 static void fill_template(GdkRectangle
*area
, CollectionItem
*item
,
99 ViewCollection
*view_collection
, Template
*template);
100 static void huge_template(GdkRectangle
*area
, CollectionItem
*colitem
,
101 ViewCollection
*view_collection
, Template
*template);
102 static void large_template(GdkRectangle
*area
, CollectionItem
*colitem
,
103 ViewCollection
*view_collection
, Template
*template);
104 static void small_template(GdkRectangle
*area
, CollectionItem
*colitem
,
105 ViewCollection
*view_collection
, Template
*template);
106 static void huge_full_template(GdkRectangle
*area
, CollectionItem
*colitem
,
107 ViewCollection
*view_collection
, Template
*template);
108 static void large_full_template(GdkRectangle
*area
, CollectionItem
*colitem
,
109 ViewCollection
*view_collection
, Template
*template);
110 static void small_full_template(GdkRectangle
*area
, CollectionItem
*colitem
,
111 ViewCollection
*view_collection
, Template
*template);
112 static gboolean
test_point(Collection
*collection
,
113 int point_x
, int point_y
,
114 CollectionItem
*item
,
115 int width
, int height
,
117 static void draw_string(GtkWidget
*widget
,
119 GdkRectangle
*area
, /* Area available on screen */
120 int width
, /* Width of the full string */
121 GtkStateType selection_state
,
124 static void draw_small_icon(GtkWidget
*widget
,
129 static void draw_huge_icon(GtkWidget
*widget
,
134 static void view_collection_iface_init(gpointer giface
, gpointer iface_data
);
135 static gboolean
name_is_truncated(ViewCollection
*view_collection
, int i
);
136 static gint
coll_motion_notify(GtkWidget
*widget
,
137 GdkEventMotion
*event
,
138 ViewCollection
*view_collection
);
139 static gint
coll_button_release(GtkWidget
*widget
,
140 GdkEventButton
*event
,
141 ViewCollection
*view_collection
);
142 static gint
coll_button_press(GtkWidget
*widget
,
143 GdkEventButton
*event
,
144 ViewCollection
*view_collection
);
145 static void size_allocate(GtkWidget
*w
, GtkAllocation
*a
, gpointer data
);
146 static void create_uri_list(ViewCollection
*view_collection
, GString
*string
);
147 static void perform_action(ViewCollection
*view_collection
,
148 GdkEventButton
*event
);
149 static void style_set(Collection
*collection
,
151 ViewCollection
*view_collection
);
152 static void display_free_colitem(Collection
*collection
,
153 CollectionItem
*colitem
);
154 static void lost_selection(Collection
*collection
,
157 static void selection_changed(Collection
*collection
,
160 static void calc_size(FilerWindow
*filer_window
, CollectionItem
*colitem
,
161 int *width
, int *height
);
162 static gboolean
drag_motion(GtkWidget
*widget
,
163 GdkDragContext
*context
,
167 ViewCollection
*view_collection
);
168 static void drag_leave(GtkWidget
*widget
,
169 GdkDragContext
*context
,
171 ViewCollection
*view_collection
);
172 static void drag_end(GtkWidget
*widget
,
173 GdkDragContext
*context
,
174 ViewCollection
*view_collection
);
175 static void make_iter(ViewCollection
*view_collection
, ViewIter
*iter
,
177 static void make_item_iter(ViewCollection
*vc
, ViewIter
*iter
, int i
);
179 static void view_collection_sort(ViewIface
*view
);
180 static void view_collection_style_changed(ViewIface
*view
, int flags
);
181 static gboolean
view_collection_autoselect(ViewIface
*view
, const gchar
*leaf
);
182 static void view_collection_add_items(ViewIface
*view
, GPtrArray
*items
);
183 static void view_collection_update_items(ViewIface
*view
, GPtrArray
*items
);
184 static void view_collection_delete_if(ViewIface
*view
,
185 gboolean (*test
)(gpointer item
, gpointer data
),
187 static void view_collection_clear(ViewIface
*view
);
188 static void view_collection_select_all(ViewIface
*view
);
189 static void view_collection_clear_selection(ViewIface
*view
);
190 static int view_collection_count_items(ViewIface
*view
);
191 static int view_collection_count_selected(ViewIface
*view
);
192 static void view_collection_show_cursor(ViewIface
*view
);
193 static void view_collection_get_iter(ViewIface
*view
,
194 ViewIter
*iter
, IterFlags flags
);
195 static void view_collection_cursor_to_iter(ViewIface
*view
, ViewIter
*iter
);
196 static void view_collection_set_selected(ViewIface
*view
,
199 static gboolean
view_collection_get_selected(ViewIface
*view
, ViewIter
*iter
);
200 static void view_collection_select_only(ViewIface
*view
, ViewIter
*iter
);
201 static void view_collection_set_frozen(ViewIface
*view
, gboolean frozen
);
202 static void view_collection_wink_item(ViewIface
*view
, ViewIter
*iter
);
203 static void view_collection_autosize(ViewIface
*view
);
204 static gboolean
view_collection_cursor_visible(ViewIface
*view
);
205 static void view_collection_set_base(ViewIface
*view
, ViewIter
*iter
);
207 static DirItem
*iter_next(ViewIter
*iter
);
208 static DirItem
*iter_prev(ViewIter
*iter
);
209 static DirItem
*iter_peek(ViewIter
*iter
);
212 /****************************************************************
213 * EXTERNAL INTERFACE *
214 ****************************************************************/
216 GtkWidget
*view_collection_new(FilerWindow
*filer_window
)
218 ViewCollection
*view_collection
;
220 view_collection
= g_object_new(view_collection_get_type(), NULL
);
221 view_collection
->filer_window
= filer_window
;
223 gtk_range_set_adjustment(GTK_RANGE(filer_window
->scrollbar
),
224 view_collection
->collection
->vadj
);
226 return GTK_WIDGET(view_collection
);
229 GType
view_collection_get_type(void)
231 static GType type
= 0;
235 static const GTypeInfo info
=
237 sizeof (ViewCollectionClass
),
238 NULL
, /* base_init */
239 NULL
, /* base_finalise */
240 view_collection_class_init
,
241 NULL
, /* class_finalise */
242 NULL
, /* class_data */
243 sizeof(ViewCollection
),
247 static const GInterfaceInfo iface_info
=
249 view_collection_iface_init
, NULL
, NULL
252 type
= g_type_register_static(gtk_viewport_get_type(),
253 "ViewCollection", &info
, 0);
254 g_type_add_interface_static(type
, VIEW_TYPE_IFACE
, &iface_info
);
260 /****************************************************************
261 * INTERNAL FUNCTIONS *
262 ****************************************************************/
264 static void view_collection_destroy(GtkObject
*view_collection
)
266 VIEW_COLLECTION(view_collection
)->filer_window
= NULL
;
269 static void view_collection_finialize(GObject
*object
)
271 /* ViewCollection *view_collection = (ViewCollection *) object; */
273 G_OBJECT_CLASS(parent_class
)->finalize(object
);
276 static void view_collection_class_init(gpointer gclass
, gpointer data
)
278 GObjectClass
*object
= (GObjectClass
*) gclass
;
280 parent_class
= g_type_class_peek_parent(gclass
);
282 object
->finalize
= view_collection_finialize
;
283 GTK_OBJECT_CLASS(object
)->destroy
= view_collection_destroy
;
286 static void view_collection_init(GTypeInstance
*object
, gpointer gclass
)
288 ViewCollection
*view_collection
= (ViewCollection
*) object
;
289 GtkViewport
*viewport
= (GtkViewport
*) object
;
290 GtkWidget
*collection
;
293 collection
= collection_new();
294 view_collection
->collection
= COLLECTION(collection
);
296 adj
= view_collection
->collection
->vadj
;
297 gtk_viewport_set_vadjustment(viewport
, adj
);
298 gtk_viewport_set_hadjustment(viewport
, NULL
); /* Or Gtk will crash */
299 gtk_viewport_set_shadow_type(viewport
, GTK_SHADOW_NONE
);
300 gtk_container_add(GTK_CONTAINER(object
), collection
);
301 gtk_widget_show(collection
);
302 gtk_widget_set_size_request(GTK_WIDGET(view_collection
), 4, 4);
304 gtk_container_set_resize_mode(GTK_CONTAINER(viewport
),
305 GTK_RESIZE_IMMEDIATE
);
307 view_collection
->collection
->free_item
= display_free_colitem
;
308 view_collection
->collection
->draw_item
= draw_item
;
309 view_collection
->collection
->test_point
= test_point
;
310 view_collection
->collection
->cb_user_data
= view_collection
;
312 g_signal_connect(collection
, "style_set",
313 G_CALLBACK(style_set
),
316 g_signal_connect(collection
, "lose_selection",
317 G_CALLBACK(lost_selection
), view_collection
);
318 g_signal_connect(collection
, "selection_changed",
319 G_CALLBACK(selection_changed
), view_collection
);
321 g_signal_connect(collection
, "button-release-event",
322 G_CALLBACK(coll_button_release
), view_collection
);
323 g_signal_connect(collection
, "button-press-event",
324 G_CALLBACK(coll_button_press
), view_collection
);
325 g_signal_connect(collection
, "motion-notify-event",
326 G_CALLBACK(coll_motion_notify
), view_collection
);
327 g_signal_connect(viewport
, "size-allocate",
328 G_CALLBACK(size_allocate
), view_collection
);
330 gtk_widget_set_events(collection
,
331 GDK_BUTTON1_MOTION_MASK
| GDK_BUTTON2_MOTION_MASK
|
332 GDK_BUTTON3_MOTION_MASK
| GDK_POINTER_MOTION_MASK
|
333 GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
);
335 /* Drag and drop events */
336 g_signal_connect(collection
, "drag_data_get",
337 GTK_SIGNAL_FUNC(drag_data_get
), NULL
);
339 make_drop_target(collection
, 0);
341 g_signal_connect(collection
, "drag_motion",
342 G_CALLBACK(drag_motion
), view_collection
);
343 g_signal_connect(collection
, "drag_leave",
344 G_CALLBACK(drag_leave
), view_collection
);
345 g_signal_connect(collection
, "drag_end",
346 G_CALLBACK(drag_end
), view_collection
);
350 static void draw_item(GtkWidget
*widget
,
351 CollectionItem
*colitem
,
355 DirItem
*item
= (DirItem
*) colitem
->data
;
356 gboolean selected
= colitem
->selected
;
358 ViewData
*view
= (ViewData
*) colitem
->view_data
;
359 ViewCollection
*view_collection
= (ViewCollection
*) user_data
;
360 FilerWindow
*filer_window
= view_collection
->filer_window
;
362 g_return_if_fail(view
!= NULL
);
364 fill_template(area
, colitem
, view_collection
, &template);
366 /* Set up GC for coloured file types */
368 type_gc
= gdk_gc_new(widget
->window
);
370 gdk_gc_set_foreground(type_gc
, type_get_colour(item
,
371 &widget
->style
->fg
[GTK_STATE_NORMAL
]));
373 if (template.icon
.width
<= SMALL_WIDTH
&&
374 template.icon
.height
<= SMALL_HEIGHT
)
376 draw_small_icon(widget
, &template.icon
,
377 item
, view
->image
, selected
);
379 else if (template.icon
.width
<= ICON_WIDTH
&&
380 template.icon
.height
<= ICON_HEIGHT
)
382 draw_large_icon(widget
, &template.icon
,
383 item
, view
->image
, selected
);
387 draw_huge_icon(widget
, &template.icon
,
388 item
, view
->image
, selected
);
391 draw_string(widget
, view
->layout
,
394 filer_window
->selection_state
,
397 draw_string(widget
, view
->details
,
399 template.details
.width
,
400 filer_window
->selection_state
,
404 /* A template contains the locations of the three rectangles (for the icon,
405 * name and extra details).
406 * Fill in the empty 'template' with the rectanges for this item.
408 static void fill_template(GdkRectangle
*area
, CollectionItem
*colitem
,
409 ViewCollection
*view_collection
, Template
*template)
411 DisplayStyle style
= view_collection
->filer_window
->display_style
;
412 ViewData
*view
= (ViewData
*) colitem
->view_data
;
416 template->details
.width
= view
->details_width
;
417 template->details
.height
= view
->details_height
;
419 if (style
== SMALL_ICONS
)
420 small_full_template(area
, colitem
,
421 view_collection
, template);
422 else if (style
== LARGE_ICONS
)
423 large_full_template(area
, colitem
,
424 view_collection
, template);
426 huge_full_template(area
, colitem
,
427 view_collection
, template);
431 if (style
== HUGE_ICONS
)
432 huge_template(area
, colitem
,
433 view_collection
, template);
434 else if (style
== LARGE_ICONS
)
435 large_template(area
, colitem
,
436 view_collection
, template);
438 small_template(area
, colitem
,
439 view_collection
, template);
443 static void huge_template(GdkRectangle
*area
, CollectionItem
*colitem
,
444 ViewCollection
*view_collection
, Template
*template)
446 int col_width
= view_collection
->collection
->item_width
;
448 ViewData
*view
= (ViewData
*) colitem
->view_data
;
449 MaskedPixmap
*image
= view
->image
;
453 if (!image
->huge_pixbuf
)
454 pixmap_make_huge(image
);
455 template->icon
.width
= image
->huge_width
;
456 template->icon
.height
= image
->huge_height
;
460 template->icon
.width
= HUGE_WIDTH
* 3 / 2;
461 template->icon
.height
= HUGE_HEIGHT
;
464 template->leafname
.width
= view
->name_width
;
465 template->leafname
.height
= view
->name_height
;
467 text_x
= area
->x
+ ((col_width
- template->leafname
.width
) >> 1);
468 text_y
= area
->y
+ area
->height
- template->leafname
.height
;
470 template->leafname
.x
= text_x
;
471 template->leafname
.y
= text_y
;
473 template->icon
.x
= area
->x
+ ((col_width
- template->icon
.width
) >> 1);
474 template->icon
.y
= template->leafname
.y
- template->icon
.height
- 2;
477 static void large_template(GdkRectangle
*area
, CollectionItem
*colitem
,
478 ViewCollection
*view_collection
, Template
*template)
480 int col_width
= view_collection
->collection
->item_width
;
484 ViewData
*view
= (ViewData
*) colitem
->view_data
;
485 MaskedPixmap
*image
= view
->image
;
491 iwidth
= MIN(image
->width
, ICON_WIDTH
);
492 iheight
= MIN(image
->height
+ 6, ICON_HEIGHT
);
497 iheight
= ICON_HEIGHT
;
499 image_x
= area
->x
+ ((col_width
- iwidth
) >> 1);
501 template->leafname
.width
= view
->name_width
;
502 template->leafname
.height
= view
->name_height
;
504 text_x
= area
->x
+ ((col_width
- template->leafname
.width
) >> 1);
505 text_y
= area
->y
+ ICON_HEIGHT
+ 2;
507 template->leafname
.x
= text_x
;
508 template->leafname
.y
= text_y
;
510 image_y
= text_y
- iheight
;
511 image_y
= MAX(area
->y
, image_y
);
513 template->icon
.x
= image_x
;
514 template->icon
.y
= image_y
;
515 template->icon
.width
= iwidth
;
516 template->icon
.height
= MIN(ICON_HEIGHT
, iheight
);
519 static void small_template(GdkRectangle
*area
, CollectionItem
*colitem
,
520 ViewCollection
*view_collection
, Template
*template)
522 int text_x
= area
->x
+ SMALL_WIDTH
+ 4;
524 int max_text_width
= area
->width
- SMALL_WIDTH
- 4;
525 ViewData
*view
= (ViewData
*) colitem
->view_data
;
527 low_text_y
= area
->y
+ area
->height
/ 2 - view
->name_height
/ 2;
529 template->leafname
.x
= text_x
;
530 template->leafname
.y
= low_text_y
;
531 template->leafname
.width
= MIN(max_text_width
, view
->name_width
);
532 template->leafname
.height
= view
->name_height
;
534 template->icon
.x
= area
->x
;
535 template->icon
.y
= area
->y
+ 1;
536 template->icon
.width
= SMALL_WIDTH
;
537 template->icon
.height
= SMALL_HEIGHT
;
540 static void huge_full_template(GdkRectangle
*area
, CollectionItem
*colitem
,
541 ViewCollection
*view_collection
, Template
*template)
543 int max_text_width
= area
->width
- HUGE_WIDTH
- 4;
544 ViewData
*view
= (ViewData
*) colitem
->view_data
;
545 MaskedPixmap
*image
= view
->image
;
549 if (!image
->huge_pixbuf
)
550 pixmap_make_huge(image
);
551 template->icon
.width
= image
->huge_width
;
552 template->icon
.height
= image
->huge_height
;
556 template->icon
.width
= HUGE_WIDTH
* 3 / 2;
557 template->icon
.height
= HUGE_HEIGHT
;
560 template->icon
.x
= area
->x
+ (HUGE_WIDTH
- template->icon
.width
) / 2;
561 template->icon
.y
= area
->y
+ (area
->height
- template->icon
.height
) / 2;
563 template->leafname
.x
= area
->x
+ HUGE_WIDTH
+ 4;
564 template->leafname
.y
= area
->y
+ area
->height
/ 2
565 - (view
->name_height
+ 2 + view
->details_height
) / 2;
566 template->leafname
.width
= MIN(max_text_width
, view
->name_width
);
567 template->leafname
.height
= view
->name_height
;
570 return; /* Not scanned yet */
572 template->details
.x
= template->leafname
.x
;
573 template->details
.y
= template->leafname
.y
+ view
->name_height
+ 2;
576 static void large_full_template(GdkRectangle
*area
, CollectionItem
*colitem
,
577 ViewCollection
*view_collection
, Template
*template)
579 int max_text_width
= area
->width
- ICON_WIDTH
- 4;
580 ViewData
*view
= (ViewData
*) colitem
->view_data
;
581 MaskedPixmap
*image
= view
->image
;
585 template->icon
.width
= image
->width
;
586 template->icon
.height
= image
->height
;
590 template->icon
.width
= ICON_WIDTH
;
591 template->icon
.height
= ICON_HEIGHT
;
594 template->icon
.x
= area
->x
+ (ICON_WIDTH
- template->icon
.width
) / 2;
595 template->icon
.y
= area
->y
+ (area
->height
- template->icon
.height
) / 2;
598 template->leafname
.x
= area
->x
+ ICON_WIDTH
+ 4;
599 template->leafname
.y
= area
->y
+ area
->height
/ 2
600 - (view
->name_height
+ 2 + view
->details_height
) / 2;
601 template->leafname
.width
= MIN(max_text_width
, view
->name_width
);
602 template->leafname
.height
= view
->name_height
;
605 return; /* Not scanned yet */
607 template->details
.x
= template->leafname
.x
;
608 template->details
.y
= template->leafname
.y
+ view
->name_height
+ 2;
611 static void small_full_template(GdkRectangle
*area
, CollectionItem
*colitem
,
612 ViewCollection
*view_collection
, Template
*template)
614 int col_width
= view_collection
->collection
->item_width
;
615 ViewData
*view
= (ViewData
*) colitem
->view_data
;
617 small_template(area
, colitem
, view_collection
, template);
620 return; /* Not scanned yet */
622 template->details
.x
= area
->x
+ col_width
- template->details
.width
;
623 template->details
.y
= area
->y
+ area
->height
/ 2 - \
624 view
->details_height
/ 2;
627 #define INSIDE(px, py, area) \
628 (px >= area.x && py >= area.y && \
629 px <= area.x + area.width && py <= area.y + area.height)
631 static gboolean
test_point(Collection
*collection
,
632 int point_x
, int point_y
,
633 CollectionItem
*colitem
,
634 int width
, int height
,
639 ViewData
*view
= (ViewData
*) colitem
->view_data
;
640 ViewCollection
*view_collection
= (ViewCollection
*) user_data
;
645 area
.height
= height
;
647 fill_template(&area
, colitem
, view_collection
, &template);
649 return INSIDE(point_x
, point_y
, template.leafname
) ||
650 INSIDE(point_x
, point_y
, template.icon
) ||
651 (view
->details
&& INSIDE(point_x
, point_y
, template.details
));
654 /* 'box' renders a background box if the string is also selected */
655 static void draw_string(GtkWidget
*widget
,
657 GdkRectangle
*area
, /* Area available on screen */
658 int width
, /* Width of the full string */
659 GtkStateType selection_state
,
664 ? widget
->style
->fg_gc
[selection_state
]
668 gtk_paint_flat_box(widget
->style
, widget
->window
,
669 selection_state
, GTK_SHADOW_NONE
,
670 NULL
, widget
, "text",
672 MIN(width
, area
->width
),
675 if (width
> area
->width
)
677 gdk_gc_set_clip_origin(gc
, 0, 0);
678 gdk_gc_set_clip_rectangle(gc
, area
);
681 gdk_draw_layout(widget
->window
, gc
, area
->x
, area
->y
, layout
);
683 if (width
> area
->width
)
685 static GdkGC
*red_gc
= NULL
;
690 GdkColor red
= {0, 0xffff, 0, 0};
692 red_gc
= gdk_gc_new(widget
->window
);
693 gdk_colormap_alloc_colors(
694 gtk_widget_get_colormap(widget
),
695 &red
, 1, FALSE
, TRUE
, &success
);
696 gdk_gc_set_foreground(red_gc
, &red
);
698 gdk_draw_rectangle(widget
->window
, red_gc
, TRUE
,
699 area
->x
+ area
->width
- 1, area
->y
,
701 gdk_gc_set_clip_rectangle(gc
, NULL
);
705 static void draw_small_icon(GtkWidget
*widget
,
711 int width
, height
, image_x
, image_y
;
716 if (!image
->sm_pixbuf
)
717 pixmap_make_small(image
);
719 width
= MIN(image
->sm_width
, SMALL_WIDTH
);
720 height
= MIN(image
->sm_height
, SMALL_HEIGHT
);
721 image_x
= area
->x
+ ((area
->width
- width
) >> 1);
722 image_y
= MAX(0, SMALL_HEIGHT
- image
->sm_height
);
724 gdk_pixbuf_render_to_drawable_alpha(
725 selected
? image
->sm_pixbuf_lit
: image
->sm_pixbuf
,
728 image_x
, area
->y
+ image_y
, /* dest */
730 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
731 GDK_RGB_DITHER_NORMAL
, 0, 0);
733 if (item
->flags
& ITEM_FLAG_SYMLINK
)
735 gdk_pixbuf_render_to_drawable_alpha(im_symlink
->pixbuf
,
738 image_x
, area
->y
+ 8, /* dest */
740 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
741 GDK_RGB_DITHER_NORMAL
, 0, 0);
743 else if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
745 MaskedPixmap
*mp
= item
->flags
& ITEM_FLAG_MOUNTED
749 gdk_pixbuf_render_to_drawable_alpha(mp
->pixbuf
,
752 image_x
+ 2, area
->y
+ 2, /* dest */
754 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
755 GDK_RGB_DITHER_NORMAL
, 0, 0);
759 /* Draw this icon (including any symlink or mount symbol) inside the
762 static void draw_huge_icon(GtkWidget
*widget
,
775 width
= image
->huge_width
;
776 height
= image
->huge_height
;
777 image_x
= area
->x
+ ((area
->width
- width
) >> 1);
778 image_y
= MAX(0, area
->height
- height
- 6);
780 gdk_pixbuf_render_to_drawable_alpha(
781 selected
? image
->huge_pixbuf_lit
782 : image
->huge_pixbuf
,
785 image_x
, area
->y
+ image_y
, /* dest */
787 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
788 GDK_RGB_DITHER_NORMAL
, 0, 0);
790 if (item
->flags
& ITEM_FLAG_SYMLINK
)
792 gdk_pixbuf_render_to_drawable_alpha(im_symlink
->pixbuf
,
795 image_x
, area
->y
+ 2, /* dest */
797 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
798 GDK_RGB_DITHER_NORMAL
, 0, 0);
800 else if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
802 MaskedPixmap
*mp
= item
->flags
& ITEM_FLAG_MOUNTED
806 gdk_pixbuf_render_to_drawable_alpha(mp
->pixbuf
,
809 image_x
, area
->y
+ 2, /* dest */
811 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
812 GDK_RGB_DITHER_NORMAL
, 0, 0);
816 /* Create the handers for the View interface */
817 static void view_collection_iface_init(gpointer giface
, gpointer iface_data
)
819 ViewIfaceClass
*iface
= giface
;
821 g_assert(G_TYPE_FROM_INTERFACE(iface
) == VIEW_TYPE_IFACE
);
824 iface
->sort
= view_collection_sort
;
825 iface
->style_changed
= view_collection_style_changed
;
826 iface
->autoselect
= view_collection_autoselect
;
827 iface
->add_items
= view_collection_add_items
;
828 iface
->update_items
= view_collection_update_items
;
829 iface
->delete_if
= view_collection_delete_if
;
830 iface
->clear
= view_collection_clear
;
831 iface
->select_all
= view_collection_select_all
;
832 iface
->clear_selection
= view_collection_clear_selection
;
833 iface
->count_items
= view_collection_count_items
;
834 iface
->count_selected
= view_collection_count_selected
;
835 iface
->show_cursor
= view_collection_show_cursor
;
836 iface
->get_iter
= view_collection_get_iter
;
837 iface
->cursor_to_iter
= view_collection_cursor_to_iter
;
838 iface
->set_selected
= view_collection_set_selected
;
839 iface
->get_selected
= view_collection_get_selected
;
840 iface
->set_frozen
= view_collection_set_frozen
;
841 iface
->select_only
= view_collection_select_only
;
842 iface
->wink_item
= view_collection_wink_item
;
843 iface
->autosize
= view_collection_autosize
;
844 iface
->cursor_visible
= view_collection_cursor_visible
;
845 iface
->set_base
= view_collection_set_base
;
848 /* It's time to make the tooltip appear. If we're not over the item any
849 * more, or the item doesn't need a tooltip, do nothing.
851 static gboolean
tooltip_activate(ViewCollection
*view_collection
)
853 Collection
*collection
;
858 g_return_val_if_fail(tip_item
!= NULL
, 0);
860 if (!view_collection
->filer_window
)
861 return FALSE
; /* Window has been destroyed */
865 collection
= view_collection
->collection
;
866 gdk_window_get_pointer(GTK_WIDGET(collection
)->window
, &x
, &y
, NULL
);
867 i
= collection_get_item(collection
, x
, y
);
868 if (i
== -1 || ((DirItem
*) collection
->items
[i
].data
) != tip_item
)
869 return FALSE
; /* Not still under the pointer */
871 /* OK, the filer window still exists and the pointer is still
872 * over the same item. Do we need to show a tip?
875 tip
= g_string_new(NULL
);
877 if (name_is_truncated(view_collection
, i
))
879 g_string_append(tip
, tip_item
->leafname
);
880 g_string_append_c(tip
, '\n');
883 filer_add_tip_details(view_collection
->filer_window
, tip
, tip_item
);
887 g_string_truncate(tip
, tip
->len
- 1);
889 tooltip_show(tip
->str
);
892 g_string_free(tip
, TRUE
);
897 static gboolean
name_is_truncated(ViewCollection
*view_collection
, int i
)
900 Collection
*collection
= view_collection
->collection
;
901 FilerWindow
*filer_window
= view_collection
->filer_window
;
902 CollectionItem
*colitem
= &collection
->items
[i
];
903 int col
= i
% collection
->columns
;
904 int row
= i
/ collection
->columns
;
906 ViewData
*view
= (ViewData
*) colitem
->view_data
;
908 /* TODO: What if the window is narrower than 1 column? */
909 if (filer_window
->display_style
== LARGE_ICONS
||
910 filer_window
->display_style
== HUGE_ICONS
)
911 return FALSE
; /* These wrap rather than truncate */
913 area
.x
= col
* collection
->item_width
;
914 area
.y
= row
* collection
->item_height
;
915 area
.height
= collection
->item_height
;
917 if (col
== collection
->columns
- 1)
918 area
.width
= GTK_WIDGET(collection
)->allocation
.width
- area
.x
;
920 area
.width
= collection
->item_width
;
922 fill_template(&area
, colitem
, view_collection
, &template);
924 return template.leafname
.width
< view
->name_width
;
927 static gint
coll_motion_notify(GtkWidget
*widget
,
928 GdkEventMotion
*event
,
929 ViewCollection
*view_collection
)
931 Collection
*collection
= view_collection
->collection
;
932 FilerWindow
*filer_window
= view_collection
->filer_window
;
935 i
= collection_get_item(collection
, event
->x
, event
->y
);
944 DirItem
*item
= (DirItem
*) collection
->items
[i
].data
;
946 if (item
!= tip_item
)
952 tooltip_prime((GtkFunction
) tooltip_activate
,
953 G_OBJECT(view_collection
));
957 if (motion_state
!= MOTION_READY_FOR_DND
)
960 if (!dnd_motion_moved(event
))
963 i
= collection_get_item(collection
,
964 event
->x
- (event
->x_root
- drag_start_x
),
965 event
->y
- (event
->y_root
- drag_start_y
));
969 collection_wink_item(collection
, -1);
971 if (!collection
->items
[i
].selected
)
973 if (event
->state
& GDK_BUTTON1_MASK
)
975 /* Select just this one */
976 filer_window
->temp_item_selected
= TRUE
;
977 collection_clear_except(collection
, i
);
981 if (collection
->number_selected
== 0)
982 filer_window
->temp_item_selected
= TRUE
;
983 collection_select_item(collection
, i
);
987 g_return_val_if_fail(collection
->number_selected
> 0, TRUE
);
989 if (collection
->number_selected
== 1)
991 DirItem
*item
= (DirItem
*) collection
->items
[i
].data
;
992 ViewData
*view
= (ViewData
*) collection
->items
[i
].view_data
;
995 item
= dir_update_item(filer_window
->directory
,
1000 report_error(_("Item no longer exists!"));
1004 drag_one_item(widget
, event
,
1005 make_path(filer_window
->sym_path
, item
->leafname
),
1006 item
, view
? view
->image
: NULL
);
1012 uris
= g_string_new(NULL
);
1013 create_uri_list(view_collection
, uris
);
1014 drag_selection(widget
, event
, uris
->str
);
1015 g_string_free(uris
, TRUE
);
1021 /* Viewport is to be resized, so calculate increments */
1022 static void size_allocate(GtkWidget
*w
, GtkAllocation
*a
, gpointer data
)
1024 Collection
*col
= ((ViewCollection
*) data
)->collection
;
1026 col
->vadj
->step_increment
= col
->item_height
;
1027 col
->vadj
->page_increment
= col
->vadj
->page_size
;
1030 /* Append all the URIs in the selection to the string */
1031 static void create_uri_list(ViewCollection
*view_collection
, GString
*string
)
1037 leader
= g_string_new("file://");
1038 g_string_append(leader
, our_host_name_for_dnd());
1039 g_string_append(leader
, view_collection
->filer_window
->sym_path
);
1040 if (leader
->str
[leader
->len
- 1] != '/')
1041 g_string_append_c(leader
, '/');
1043 make_iter(view_collection
, &iter
, VIEW_ITER_SELECTED
);
1044 while ((item
= iter
.next(&iter
)))
1046 g_string_append(string
, leader
->str
);
1047 g_string_append(string
, item
->leafname
);
1048 g_string_append(string
, "\r\n");
1051 g_string_free(leader
, TRUE
);
1054 static gint
coll_button_release(GtkWidget
*widget
,
1055 GdkEventButton
*event
,
1056 ViewCollection
*view_collection
)
1058 if (dnd_motion_release(event
))
1060 if (motion_buttons_pressed
== 0 &&
1061 view_collection
->collection
->lasso_box
)
1063 collection_end_lasso(view_collection
->collection
,
1064 event
->button
== 1 ? GDK_SET
: GDK_INVERT
);
1069 perform_action(view_collection
, event
);
1074 static gint
coll_button_press(GtkWidget
*widget
,
1075 GdkEventButton
*event
,
1076 ViewCollection
*view_collection
)
1078 collection_set_cursor_item(view_collection
->collection
, -1);
1080 if (dnd_motion_press(widget
, event
))
1081 perform_action(view_collection
, event
);
1086 static void perform_action(ViewCollection
*view_collection
,
1087 GdkEventButton
*event
)
1089 Collection
*collection
= view_collection
->collection
;
1092 gboolean press
= event
->type
== GDK_BUTTON_PRESS
;
1093 gboolean selected
= FALSE
;
1094 OpenFlags flags
= 0;
1096 FilerWindow
*filer_window
= view_collection
->filer_window
;
1098 if (event
->button
> 3)
1101 item
= collection_get_item(collection
, event
->x
, event
->y
);
1103 if (item
!= -1 && event
->button
== 1 &&
1104 collection
->items
[item
].selected
&&
1105 filer_window
->selection_state
== GTK_STATE_INSENSITIVE
)
1107 /* Possibly a really slow DnD operation? */
1108 filer_window
->temp_item_selected
= FALSE
;
1110 filer_selection_changed(filer_window
, event
->time
);
1114 if (filer_window
->target_cb
)
1116 dnd_motion_ungrab();
1117 if (item
!= -1 && press
&& event
->button
== 1)
1120 make_item_iter(view_collection
, &iter
, item
);
1122 filer_window
->target_cb(filer_window
, &iter
,
1123 filer_window
->target_data
);
1125 filer_target_mode(filer_window
, NULL
, NULL
, NULL
);
1130 action
= bind_lookup_bev(
1131 item
== -1 ? BIND_DIRECTORY
: BIND_DIRECTORY_ICON
,
1136 dir_item
= (DirItem
*) collection
->items
[item
].data
;
1137 selected
= collection
->items
[item
].selected
;
1144 case ACT_CLEAR_SELECTION
:
1145 collection_clear_selection(collection
);
1147 case ACT_TOGGLE_SELECTED
:
1148 collection_toggle_item(collection
, item
);
1150 case ACT_SELECT_EXCL
:
1151 collection_clear_except(collection
, item
);
1154 flags
|= OPEN_SHIFT
;
1160 make_item_iter(view_collection
, &iter
, item
);
1162 if (event
->button
!= 1 || event
->state
& GDK_MOD1_MASK
)
1163 flags
|= OPEN_CLOSE_WINDOW
;
1165 flags
|= OPEN_SAME_WINDOW
;
1166 if (o_new_button_1
.int_value
)
1167 flags
^= OPEN_SAME_WINDOW
;
1168 if (event
->type
== GDK_2BUTTON_PRESS
)
1169 collection_unselect_item(collection
, item
);
1170 dnd_motion_ungrab();
1172 filer_openitem(filer_window
, &iter
, flags
);
1175 case ACT_POPUP_MENU
:
1179 dnd_motion_ungrab();
1182 make_item_iter(view_collection
, &iter
, item
);
1183 show_filer_menu(filer_window
,
1184 (GdkEvent
*) event
, &iter
);
1187 case ACT_PRIME_AND_SELECT
:
1189 collection_clear_except(collection
, item
);
1190 dnd_motion_start(MOTION_READY_FOR_DND
);
1192 case ACT_PRIME_AND_TOGGLE
:
1193 collection_toggle_item(collection
, item
);
1194 dnd_motion_start(MOTION_READY_FOR_DND
);
1196 case ACT_PRIME_FOR_DND
:
1197 dnd_motion_start(MOTION_READY_FOR_DND
);
1200 if (press
&& event
->button
< 4)
1203 collection_wink_item(collection
, item
);
1204 dnd_motion_start(MOTION_NONE
);
1207 case ACT_LASSO_CLEAR
:
1208 collection_clear_selection(collection
);
1210 case ACT_LASSO_MODIFY
:
1211 collection_lasso_box(collection
, event
->x
, event
->y
);
1214 filer_window_autosize(filer_window
);
1217 g_warning("Unsupported action : %d\n", action
);
1222 /* Nothing is selected anymore - give up primary */
1223 static void lost_selection(Collection
*collection
,
1227 ViewCollection
*view_collection
= VIEW_COLLECTION(user_data
);
1229 filer_lost_selection(view_collection
->filer_window
, time
);
1232 static void selection_changed(Collection
*collection
,
1236 ViewCollection
*view_collection
= VIEW_COLLECTION(user_data
);
1238 filer_selection_changed(view_collection
->filer_window
, time
);
1241 static void display_free_colitem(Collection
*collection
,
1242 CollectionItem
*colitem
)
1244 ViewData
*view
= (ViewData
*) colitem
->view_data
;
1251 g_object_unref(G_OBJECT(view
->layout
));
1252 view
->layout
= NULL
;
1255 g_object_unref(G_OBJECT(view
->details
));
1258 g_object_unref(view
->image
);
1263 static void add_item(ViewCollection
*view_collection
, DirItem
*item
)
1265 Collection
*collection
= view_collection
->collection
;
1266 FilerWindow
*filer_window
= view_collection
->filer_window
;
1267 int old_w
= collection
->item_width
;
1268 int old_h
= collection
->item_height
;
1271 i
= collection_insert(collection
, item
,
1272 display_create_viewdata(filer_window
, item
));
1274 calc_size(filer_window
, &collection
->items
[i
], &w
, &h
);
1276 if (w
> old_w
|| h
> old_h
)
1277 collection_set_item_size(collection
,
1282 static void style_set(Collection
*collection
,
1284 ViewCollection
*view_collection
)
1286 view_collection_style_changed(VIEW(view_collection
),
1287 VIEW_UPDATE_VIEWDATA
| VIEW_UPDATE_NAME
);
1290 /* Return the size needed for this item */
1291 static void calc_size(FilerWindow
*filer_window
, CollectionItem
*colitem
,
1292 int *width
, int *height
)
1294 int pix_width
, pix_height
;
1296 DisplayStyle style
= filer_window
->display_style
;
1297 ViewData
*view
= (ViewData
*) colitem
->view_data
;
1299 if (filer_window
->details_type
== DETAILS_NONE
)
1301 if (style
== HUGE_ICONS
)
1305 if (!view
->image
->huge_pixbuf
)
1306 pixmap_make_huge(view
->image
);
1307 pix_width
= view
->image
->huge_width
;
1308 pix_height
= view
->image
->huge_height
;
1312 pix_width
= HUGE_WIDTH
* 3 / 2;
1313 pix_height
= HUGE_HEIGHT
* 3 / 2;
1315 *width
= MAX(pix_width
, view
->name_width
) + 4;
1316 *height
= view
->name_height
+ pix_height
+ 4;
1318 else if (style
== SMALL_ICONS
)
1320 w
= MIN(view
->name_width
, o_small_width
.int_value
);
1321 *width
= SMALL_WIDTH
+ 12 + w
;
1322 *height
= MAX(view
->name_height
, SMALL_HEIGHT
) + 4;
1327 pix_width
= view
->image
->width
;
1329 pix_width
= ICON_WIDTH
;
1330 *width
= MAX(pix_width
, view
->name_width
) + 4;
1331 *height
= view
->name_height
+ ICON_HEIGHT
+ 2;
1336 w
= view
->details_width
;
1337 if (style
== HUGE_ICONS
)
1339 *width
= HUGE_WIDTH
+ 12 + MAX(w
, view
->name_width
);
1340 *height
= HUGE_HEIGHT
- 4;
1342 else if (style
== SMALL_ICONS
)
1346 *width
= SMALL_WIDTH
+ view
->name_width
+ 12 + w
;
1347 text_height
= MAX(view
->name_height
,
1348 view
->details_height
);
1349 *height
= MAX(text_height
, SMALL_HEIGHT
) + 4;
1353 *width
= ICON_WIDTH
+ 12 + MAX(w
, view
->name_width
);
1354 *height
= ICON_HEIGHT
;
1359 static void update_item(ViewCollection
*view_collection
, int i
)
1361 Collection
*collection
= view_collection
->collection
;
1362 int old_w
= collection
->item_width
;
1363 int old_h
= collection
->item_height
;
1365 CollectionItem
*colitem
;
1366 FilerWindow
*filer_window
= view_collection
->filer_window
;
1368 g_return_if_fail(i
>= 0 && i
< collection
->number_of_items
);
1370 colitem
= &collection
->items
[i
];
1372 display_update_view(filer_window
,
1373 (DirItem
*) colitem
->data
,
1374 (ViewData
*) colitem
->view_data
,
1377 calc_size(filer_window
, colitem
, &w
, &h
);
1378 if (w
> old_w
|| h
> old_h
)
1379 collection_set_item_size(collection
,
1383 collection_draw_item(collection
, i
, TRUE
);
1386 /* Implementations of the View interface. See view_iface.c for comments. */
1388 static void view_collection_style_changed(ViewIface
*view
, int flags
)
1390 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1391 FilerWindow
*filer_window
= view_collection
->filer_window
;
1393 Collection
*col
= view_collection
->collection
;
1394 int width
= MIN_ITEM_WIDTH
;
1395 int height
= SMALL_HEIGHT
;
1396 int n
= col
->number_of_items
;
1398 if (n
== 0 && filer_window
->display_style
!= SMALL_ICONS
)
1399 height
= ICON_HEIGHT
;
1401 /* Recalculate all the ViewData structs for this window
1402 * (needed if the text or image has changed in any way) and
1403 * get the size of each item.
1405 for (i
= 0; i
< n
; i
++)
1407 CollectionItem
*ci
= &col
->items
[i
];
1410 if (flags
& (VIEW_UPDATE_VIEWDATA
| VIEW_UPDATE_NAME
))
1411 display_update_view(filer_window
,
1412 (DirItem
*) ci
->data
,
1413 (ViewData
*) ci
->view_data
,
1414 (flags
& VIEW_UPDATE_NAME
) != 0);
1416 calc_size(filer_window
, ci
, &w
, &h
);
1423 collection_set_item_size(col
, width
, height
);
1425 gtk_widget_queue_draw(GTK_WIDGET(view_collection
));
1428 static void view_collection_sort(ViewIface
*view
)
1430 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1432 collection_qsort(view_collection
->collection
,
1433 view_collection
->filer_window
->sort_fn
);
1436 static gboolean
view_collection_autoselect(ViewIface
*view
, const gchar
*leaf
)
1438 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1439 Collection
*col
= view_collection
->collection
;
1442 for (i
= 0; i
< col
->number_of_items
; i
++)
1444 DirItem
*item
= (DirItem
*) col
->items
[i
].data
;
1446 if (strcmp(item
->leafname
, leaf
) == 0)
1448 if (col
->cursor_item
!= -1)
1449 collection_set_cursor_item(col
, i
);
1451 collection_wink_item(col
, i
);
1459 static void view_collection_add_items(ViewIface
*view
, GPtrArray
*items
)
1461 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1462 Collection
*collection
= view_collection
->collection
;
1463 FilerWindow
*filer_window
= view_collection
->filer_window
;
1466 old_num
= collection
->number_of_items
;
1467 for (i
= 0; i
< items
->len
; i
++)
1469 DirItem
*item
= (DirItem
*) items
->pdata
[i
];
1470 char *leafname
= item
->leafname
;
1472 if (leafname
[0] == '.' && !filer_window
->show_hidden
)
1475 add_item(view_collection
, item
);
1478 if (old_num
!= collection
->number_of_items
)
1479 view_collection_sort(view
);
1482 static void view_collection_update_items(ViewIface
*view
, GPtrArray
*items
)
1484 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1485 Collection
*collection
= view_collection
->collection
;
1486 FilerWindow
*filer_window
= view_collection
->filer_window
;
1489 g_return_if_fail(items
->len
> 0);
1491 /* The item data has already been modified, so this gives the
1492 * final sort order...
1494 collection_qsort(collection
, filer_window
->sort_fn
);
1496 for (i
= 0; i
< items
->len
; i
++)
1498 DirItem
*item
= (DirItem
*) items
->pdata
[i
];
1499 const gchar
*leafname
= item
->leafname
;
1502 if (leafname
[0] == '.' && filer_window
->show_hidden
== FALSE
)
1505 j
= collection_find_item(collection
, item
,
1506 filer_window
->sort_fn
);
1509 g_warning("Failed to find '%s'\n", leafname
);
1511 update_item(view_collection
, j
);
1515 static void view_collection_delete_if(ViewIface
*view
,
1516 gboolean (*test
)(gpointer item
, gpointer data
),
1519 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1520 Collection
*collection
= view_collection
->collection
;
1522 collection_delete_if(collection
, test
, data
);
1525 static void view_collection_clear(ViewIface
*view
)
1527 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1528 Collection
*collection
= view_collection
->collection
;
1530 collection_clear(collection
);
1533 static void view_collection_select_all(ViewIface
*view
)
1535 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1536 Collection
*collection
= view_collection
->collection
;
1538 collection_select_all(collection
);
1541 static void view_collection_clear_selection(ViewIface
*view
)
1543 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1544 Collection
*collection
= view_collection
->collection
;
1546 collection_clear_selection(collection
);
1549 static int view_collection_count_items(ViewIface
*view
)
1551 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1552 Collection
*collection
= view_collection
->collection
;
1554 return collection
->number_of_items
;
1557 static int view_collection_count_selected(ViewIface
*view
)
1559 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1560 Collection
*collection
= view_collection
->collection
;
1562 return collection
->number_selected
;
1565 static void view_collection_show_cursor(ViewIface
*view
)
1567 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1568 Collection
*collection
= view_collection
->collection
;
1570 collection_move_cursor(collection
, 0, 0);
1573 /* The first time the next() method is used, this is called */
1574 static DirItem
*iter_init(ViewIter
*iter
)
1576 ViewCollection
*view_collection
= iter
->view_collection
;
1577 Collection
*collection
= view_collection
->collection
;
1579 int n
= collection
->number_of_items
;
1580 int flags
= iter
->flags
;
1582 iter
->peek
= iter_peek
;
1584 if (iter
->n_remaining
== 0)
1587 if (flags
& VIEW_ITER_FROM_CURSOR
)
1589 i
= collection
->cursor_item
;
1591 return NULL
; /* No cursor */
1593 else if (flags
& VIEW_ITER_FROM_BASE
)
1594 i
= view_collection
->cursor_base
;
1596 if (i
< 0 || i
>= n
)
1598 /* Either a normal iteration, or an iteration from an
1599 * invalid starting point.
1601 if (flags
& VIEW_ITER_BACKWARDS
)
1607 if (i
< 0 || i
>= n
)
1608 return NULL
; /* No items at all! */
1610 iter
->next
= flags
& VIEW_ITER_BACKWARDS
? iter_prev
: iter_next
;
1611 iter
->n_remaining
--;
1614 if (flags
& VIEW_ITER_SELECTED
&& !collection
->items
[i
].selected
)
1615 return iter
->next(iter
);
1616 return iter
->peek(iter
);
1618 /* Advance iter to point to the next item and return the new item
1619 * (this saves you calling peek after next each time).
1621 static DirItem
*iter_next(ViewIter
*iter
)
1623 Collection
*collection
= iter
->view_collection
->collection
;
1624 int n
= collection
->number_of_items
;
1627 g_return_val_if_fail(iter
->n_remaining
>= 0, NULL
);
1629 /* i is the last item returned (always valid) */
1631 g_return_val_if_fail(i
>= 0 && i
< n
, NULL
);
1633 while (iter
->n_remaining
)
1636 iter
->n_remaining
--;
1641 g_return_val_if_fail(i
>= 0 && i
< n
, NULL
);
1643 if (iter
->flags
& VIEW_ITER_SELECTED
&&
1644 !collection
->items
[i
].selected
)
1648 return collection
->items
[i
].data
;
1655 /* Like iter_next, but in the other direction */
1656 static DirItem
*iter_prev(ViewIter
*iter
)
1658 Collection
*collection
= iter
->view_collection
->collection
;
1659 int n
= collection
->number_of_items
;
1662 g_return_val_if_fail(iter
->n_remaining
>= 0, NULL
);
1664 /* i is the last item returned (always valid) */
1666 g_return_val_if_fail(i
>= 0 && i
< n
, NULL
);
1668 while (iter
->n_remaining
)
1671 iter
->n_remaining
--;
1674 i
= collection
->number_of_items
- 1;
1676 g_return_val_if_fail(i
>= 0 && i
< n
, NULL
);
1678 if (iter
->flags
& VIEW_ITER_SELECTED
&&
1679 !collection
->items
[i
].selected
)
1683 return collection
->items
[i
].data
;
1690 static DirItem
*iter_peek(ViewIter
*iter
)
1692 Collection
*collection
= iter
->view_collection
->collection
;
1698 g_return_val_if_fail(i
>= 0 && i
< collection
->number_of_items
, NULL
);
1700 return collection
->items
[i
].data
;
1703 static void make_iter(ViewCollection
*view_collection
, ViewIter
*iter
,
1706 Collection
*collection
= view_collection
->collection
;
1708 iter
->view_collection
= view_collection
;
1709 iter
->next
= iter_init
;
1713 iter
->flags
= flags
;
1715 if (flags
& VIEW_ITER_ONE_ONLY
)
1717 iter
->n_remaining
= 1;
1721 iter
->n_remaining
= collection
->number_of_items
;
1724 /* Set the iterator to return 'i' on the next peek() */
1725 static void make_item_iter(ViewCollection
*view_collection
,
1726 ViewIter
*iter
, int i
)
1728 Collection
*collection
= view_collection
->collection
;
1730 g_return_if_fail(i
>= -1 && i
< collection
->number_of_items
);
1732 make_iter(view_collection
, iter
, 0);
1735 iter
->next
= iter_next
;
1736 iter
->peek
= iter_peek
;
1737 iter
->n_remaining
= 0;
1740 static void view_collection_get_iter(ViewIface
*view
,
1741 ViewIter
*iter
, IterFlags flags
)
1743 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1745 make_iter(view_collection
, iter
, flags
);
1748 static void view_collection_cursor_to_iter(ViewIface
*view
, ViewIter
*iter
)
1750 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1751 Collection
*collection
= view_collection
->collection
;
1753 g_return_if_fail(iter
== NULL
||
1754 iter
->view_collection
== view_collection
);
1756 collection_set_cursor_item(collection
, iter
? iter
->i
: -1);
1759 static void view_collection_set_selected(ViewIface
*view
,
1763 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1764 Collection
*collection
= view_collection
->collection
;
1766 g_return_if_fail(iter
!= NULL
&&
1767 iter
->view_collection
== view_collection
);
1768 g_return_if_fail(iter
->i
>= 0 && iter
->i
< collection
->number_of_items
);
1771 collection_select_item(collection
, iter
->i
);
1773 collection_unselect_item(collection
, iter
->i
);
1776 static gboolean
view_collection_get_selected(ViewIface
*view
, ViewIter
*iter
)
1778 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1779 Collection
*collection
= view_collection
->collection
;
1781 g_return_val_if_fail(iter
!= NULL
&&
1782 iter
->view_collection
== view_collection
, FALSE
);
1783 g_return_val_if_fail(iter
->i
>= 0 &&
1784 iter
->i
< collection
->number_of_items
, FALSE
);
1786 return collection
->items
[iter
->i
].selected
;
1789 static void view_collection_select_only(ViewIface
*view
, ViewIter
*iter
)
1791 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1792 Collection
*collection
= view_collection
->collection
;
1794 g_return_if_fail(iter
!= NULL
&&
1795 iter
->view_collection
== view_collection
);
1796 g_return_if_fail(iter
->i
>= 0 && iter
->i
< collection
->number_of_items
);
1798 collection_clear_except(collection
, iter
->i
);
1801 static void view_collection_set_frozen(ViewIface
*view
, gboolean frozen
)
1803 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1804 Collection
*collection
= view_collection
->collection
;
1807 collection
->block_selection_changed
++;
1809 collection_unblock_selection_changed(collection
,
1810 gtk_get_current_event_time(), TRUE
);
1813 static void view_collection_wink_item(ViewIface
*view
, ViewIter
*iter
)
1815 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1816 Collection
*collection
= view_collection
->collection
;
1818 g_return_if_fail(iter
!= NULL
&&
1819 iter
->view_collection
== view_collection
);
1820 g_return_if_fail(iter
->i
>= 0 && iter
->i
< collection
->number_of_items
);
1822 collection_wink_item(collection
, iter
->i
);
1825 static void view_collection_autosize(ViewIface
*view
)
1827 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1828 FilerWindow
*filer_window
= view_collection
->filer_window
;
1829 Collection
*collection
= view_collection
->collection
;
1831 int w
= collection
->item_width
;
1832 int h
= collection
->item_height
;
1835 int max_x
, max_rows
;
1836 const float r
= 2.5;
1840 /* Get the extra height required for the toolbar and minibuffer,
1843 if (o_toolbar
.int_value
!= TOOLBAR_NONE
)
1844 t
= filer_window
->toolbar
->allocation
.height
;
1845 if (filer_window
->message
)
1846 t
+= filer_window
->message
->allocation
.height
;
1847 if (GTK_WIDGET_VISIBLE(filer_window
->minibuffer_area
))
1851 gtk_widget_size_request(filer_window
->minibuffer_area
, &req
);
1852 space
= req
.height
+ 2;
1856 n
= collection
->number_of_items
;
1858 h
= ICON_HEIGHT
* 1.5;
1861 max_x
= (o_filer_size_limit
.int_value
* screen_width
) / 100;
1862 max_rows
= (o_filer_size_limit
.int_value
* screen_height
) / (h
* 100);
1864 /* Aim for a size where
1865 * x = r(y + t + h), (1)
1866 * unless that's too wide.
1868 * t = toolbar (and minibuffer) height
1869 * r = desired (width / height) ratio
1871 * Want to display all items:
1874 * => x(x/r - t - h) = nwh (from 1)
1875 * => xx - x.rt - hr(1 - nw) = 0
1876 * => 2x = rt +/- sqrt(rt.rt + 4hr(nw - 1))
1880 * sqrt(rt.rt + ...) > rt
1882 * So, the +/- must be +:
1884 * => x = (rt + sqrt(rt.rt + 4hr(nw - 1))) / 2
1886 * ( + w - 1 to round up)
1888 x
= (r
* t
+ sqrt(r
*r
*t
*t
+ 4*h
*r
* (n
*w
- 1))) / 2 + w
- 1;
1895 cols
= MAX(cols
, 1);
1897 /* Choose rows to display all items given our chosen x.
1898 * Don't make the window *too* big!
1900 rows
= (n
+ cols
- 1) / cols
;
1901 if (rows
> max_rows
)
1904 /* Leave some room for extra icons, but only in Small Icons mode
1905 * otherwise it takes up too much space.
1906 * Also, don't add space if the minibuffer is open.
1909 space
= filer_window
->display_style
== SMALL_ICONS
? h
: 2;
1911 filer_window_set_size(filer_window
,
1913 h
* MAX(rows
, 1) + space
);
1916 static gboolean
view_collection_cursor_visible(ViewIface
*view
)
1918 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1919 Collection
*collection
= view_collection
->collection
;
1921 return collection
->cursor_item
!= -1;
1924 static void view_collection_set_base(ViewIface
*view
, ViewIter
*iter
)
1926 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1928 view_collection
->cursor_base
= iter
->i
;
1931 static void drag_end(GtkWidget
*widget
,
1932 GdkDragContext
*context
,
1933 ViewCollection
*view_collection
)
1935 if (view_collection
->filer_window
->temp_item_selected
)
1937 view_collection_clear_selection(VIEW(view_collection
));
1938 view_collection
->filer_window
->temp_item_selected
= FALSE
;
1942 /* Remove highlights */
1943 static void drag_leave(GtkWidget
*widget
,
1944 GdkDragContext
*context
,
1946 ViewCollection
*view_collection
)
1952 g_signal_handler_disconnect(scrolled_adj
, scrolled_signal
);
1953 scrolled_adj
= NULL
;
1958 /* Called during the drag when the mouse is in a widget registered
1959 * as a drop target. Returns TRUE if we can accept the drop.
1961 static gboolean
drag_motion(GtkWidget
*widget
,
1962 GdkDragContext
*context
,
1966 ViewCollection
*view_collection
)
1970 GdkDragAction action
= context
->suggested_action
;
1971 const guchar
*new_path
= NULL
;
1972 const char *type
= NULL
;
1973 gboolean retval
= FALSE
;
1974 Collection
*collection
= view_collection
->collection
;
1976 if (o_dnd_drag_to_icons
.int_value
)
1977 item_number
= collection_get_item(collection
, x
, y
);
1981 item
= item_number
>= 0
1982 ? (DirItem
*) collection
->items
[item_number
].data
1985 if (item
&& collection
->items
[item_number
].selected
)
1988 type
= dnd_motion_item(context
, &item
);
1993 /* Don't allow drops to non-writeable directories. BUT, still
1994 * allow drops on non-writeable SUBdirectories so that we can
1995 * do the spring-open thing.
1997 if (item
&& type
== drop_dest_dir
&&
1998 !(item
->flags
& ITEM_FLAG_APPDIR
))
2001 /* XXX: This is needed so that directories don't
2002 * spring open while we scroll. Should go in
2003 * view_collection.c, I think.
2006 /* XXX: Now it IS in view_collection, maybe we should
2010 GtkObject
*vadj
= GTK_OBJECT(collection
->vadj
);
2012 /* Subdir: prepare for spring-open */
2013 if (scrolled_adj
!= vadj
)
2016 gtk_signal_disconnect(scrolled_adj
,
2018 scrolled_adj
= vadj
;
2019 scrolled_signal
= gtk_signal_connect(
2022 GTK_SIGNAL_FUNC(scrolled
),
2026 dnd_spring_load(context
, view_collection
->filer_window
);
2033 collection_set_cursor_item(collection
,
2038 collection_set_cursor_item(collection
, -1);
2040 /* Disallow background drops within a single window */
2041 if (type
&& gtk_drag_get_source_widget(context
) == widget
)
2048 new_path
= make_path(
2049 view_collection
->filer_window
->sym_path
,
2052 new_path
= view_collection
->filer_window
->sym_path
;
2055 g_dataset_set_data(context
, "drop_dest_type", (gpointer
) type
);
2058 gdk_drag_status(context
, action
, time
);
2059 g_dataset_set_data_full(context
, "drop_dest_path",
2060 g_strdup(new_path
), g_free
);