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_shadow_type(viewport
, GTK_SHADOW_NONE
);
299 gtk_container_add(GTK_CONTAINER(object
), collection
);
300 gtk_widget_show(collection
);
301 gtk_widget_set_size_request(GTK_WIDGET(view_collection
), 4, 4);
303 gtk_container_set_resize_mode(GTK_CONTAINER(viewport
),
304 GTK_RESIZE_IMMEDIATE
);
306 view_collection
->collection
->free_item
= display_free_colitem
;
307 view_collection
->collection
->draw_item
= draw_item
;
308 view_collection
->collection
->test_point
= test_point
;
309 view_collection
->collection
->cb_user_data
= view_collection
;
311 g_signal_connect(collection
, "style_set",
312 G_CALLBACK(style_set
),
315 g_signal_connect(collection
, "lose_selection",
316 G_CALLBACK(lost_selection
), view_collection
);
317 g_signal_connect(collection
, "selection_changed",
318 G_CALLBACK(selection_changed
), view_collection
);
320 g_signal_connect(collection
, "button-release-event",
321 G_CALLBACK(coll_button_release
), view_collection
);
322 g_signal_connect(collection
, "button-press-event",
323 G_CALLBACK(coll_button_press
), view_collection
);
324 g_signal_connect(collection
, "motion-notify-event",
325 G_CALLBACK(coll_motion_notify
), view_collection
);
326 g_signal_connect(viewport
, "size-allocate",
327 G_CALLBACK(size_allocate
), view_collection
);
329 gtk_widget_set_events(collection
,
330 GDK_BUTTON1_MOTION_MASK
| GDK_BUTTON2_MOTION_MASK
|
331 GDK_BUTTON3_MOTION_MASK
| GDK_POINTER_MOTION_MASK
|
332 GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
);
334 /* Drag and drop events */
335 g_signal_connect(collection
, "drag_data_get",
336 GTK_SIGNAL_FUNC(drag_data_get
), NULL
);
338 make_drop_target(collection
, 0);
340 g_signal_connect(collection
, "drag_motion",
341 G_CALLBACK(drag_motion
), view_collection
);
342 g_signal_connect(collection
, "drag_leave",
343 G_CALLBACK(drag_leave
), view_collection
);
344 g_signal_connect(collection
, "drag_end",
345 G_CALLBACK(drag_end
), view_collection
);
349 static void draw_item(GtkWidget
*widget
,
350 CollectionItem
*colitem
,
354 DirItem
*item
= (DirItem
*) colitem
->data
;
355 gboolean selected
= colitem
->selected
;
357 ViewData
*view
= (ViewData
*) colitem
->view_data
;
358 ViewCollection
*view_collection
= (ViewCollection
*) user_data
;
359 FilerWindow
*filer_window
= view_collection
->filer_window
;
361 g_return_if_fail(view
!= NULL
);
363 fill_template(area
, colitem
, view_collection
, &template);
365 /* Set up GC for coloured file types */
367 type_gc
= gdk_gc_new(widget
->window
);
369 gdk_gc_set_foreground(type_gc
, type_get_colour(item
,
370 &widget
->style
->fg
[GTK_STATE_NORMAL
]));
372 if (template.icon
.width
<= SMALL_WIDTH
&&
373 template.icon
.height
<= SMALL_HEIGHT
)
375 draw_small_icon(widget
, &template.icon
,
376 item
, view
->image
, selected
);
378 else if (template.icon
.width
<= ICON_WIDTH
&&
379 template.icon
.height
<= ICON_HEIGHT
)
381 draw_large_icon(widget
, &template.icon
,
382 item
, view
->image
, selected
);
386 draw_huge_icon(widget
, &template.icon
,
387 item
, view
->image
, selected
);
390 draw_string(widget
, view
->layout
,
393 filer_window
->selection_state
,
396 draw_string(widget
, view
->details
,
398 template.details
.width
,
399 filer_window
->selection_state
,
403 /* A template contains the locations of the three rectangles (for the icon,
404 * name and extra details).
405 * Fill in the empty 'template' with the rectanges for this item.
407 static void fill_template(GdkRectangle
*area
, CollectionItem
*colitem
,
408 ViewCollection
*view_collection
, Template
*template)
410 DisplayStyle style
= view_collection
->filer_window
->display_style
;
411 ViewData
*view
= (ViewData
*) colitem
->view_data
;
415 template->details
.width
= view
->details_width
;
416 template->details
.height
= view
->details_height
;
418 if (style
== SMALL_ICONS
)
419 small_full_template(area
, colitem
,
420 view_collection
, template);
421 else if (style
== LARGE_ICONS
)
422 large_full_template(area
, colitem
,
423 view_collection
, template);
425 huge_full_template(area
, colitem
,
426 view_collection
, template);
430 if (style
== HUGE_ICONS
)
431 huge_template(area
, colitem
,
432 view_collection
, template);
433 else if (style
== LARGE_ICONS
)
434 large_template(area
, colitem
,
435 view_collection
, template);
437 small_template(area
, colitem
,
438 view_collection
, template);
442 static void huge_template(GdkRectangle
*area
, CollectionItem
*colitem
,
443 ViewCollection
*view_collection
, Template
*template)
445 int col_width
= view_collection
->collection
->item_width
;
447 ViewData
*view
= (ViewData
*) colitem
->view_data
;
448 MaskedPixmap
*image
= view
->image
;
452 if (!image
->huge_pixbuf
)
453 pixmap_make_huge(image
);
454 template->icon
.width
= image
->huge_width
;
455 template->icon
.height
= image
->huge_height
;
459 template->icon
.width
= HUGE_WIDTH
* 3 / 2;
460 template->icon
.height
= HUGE_HEIGHT
;
463 template->leafname
.width
= view
->name_width
;
464 template->leafname
.height
= view
->name_height
;
466 text_x
= area
->x
+ ((col_width
- template->leafname
.width
) >> 1);
467 text_y
= area
->y
+ area
->height
- template->leafname
.height
;
469 template->leafname
.x
= text_x
;
470 template->leafname
.y
= text_y
;
472 template->icon
.x
= area
->x
+ ((col_width
- template->icon
.width
) >> 1);
473 template->icon
.y
= template->leafname
.y
- template->icon
.height
- 2;
476 static void large_template(GdkRectangle
*area
, CollectionItem
*colitem
,
477 ViewCollection
*view_collection
, Template
*template)
479 int col_width
= view_collection
->collection
->item_width
;
483 ViewData
*view
= (ViewData
*) colitem
->view_data
;
484 MaskedPixmap
*image
= view
->image
;
490 iwidth
= MIN(image
->width
, ICON_WIDTH
);
491 iheight
= MIN(image
->height
+ 6, ICON_HEIGHT
);
496 iheight
= ICON_HEIGHT
;
498 image_x
= area
->x
+ ((col_width
- iwidth
) >> 1);
500 template->leafname
.width
= view
->name_width
;
501 template->leafname
.height
= view
->name_height
;
503 text_x
= area
->x
+ ((col_width
- template->leafname
.width
) >> 1);
504 text_y
= area
->y
+ ICON_HEIGHT
+ 2;
506 template->leafname
.x
= text_x
;
507 template->leafname
.y
= text_y
;
509 image_y
= text_y
- iheight
;
510 image_y
= MAX(area
->y
, image_y
);
512 template->icon
.x
= image_x
;
513 template->icon
.y
= image_y
;
514 template->icon
.width
= iwidth
;
515 template->icon
.height
= MIN(ICON_HEIGHT
, iheight
);
518 static void small_template(GdkRectangle
*area
, CollectionItem
*colitem
,
519 ViewCollection
*view_collection
, Template
*template)
521 int text_x
= area
->x
+ SMALL_WIDTH
+ 4;
523 int max_text_width
= area
->width
- SMALL_WIDTH
- 4;
524 ViewData
*view
= (ViewData
*) colitem
->view_data
;
526 low_text_y
= area
->y
+ area
->height
/ 2 - view
->name_height
/ 2;
528 template->leafname
.x
= text_x
;
529 template->leafname
.y
= low_text_y
;
530 template->leafname
.width
= MIN(max_text_width
, view
->name_width
);
531 template->leafname
.height
= view
->name_height
;
533 template->icon
.x
= area
->x
;
534 template->icon
.y
= area
->y
+ 1;
535 template->icon
.width
= SMALL_WIDTH
;
536 template->icon
.height
= SMALL_HEIGHT
;
539 static void huge_full_template(GdkRectangle
*area
, CollectionItem
*colitem
,
540 ViewCollection
*view_collection
, Template
*template)
542 int max_text_width
= area
->width
- HUGE_WIDTH
- 4;
543 ViewData
*view
= (ViewData
*) colitem
->view_data
;
544 MaskedPixmap
*image
= view
->image
;
548 if (!image
->huge_pixbuf
)
549 pixmap_make_huge(image
);
550 template->icon
.width
= image
->huge_width
;
551 template->icon
.height
= image
->huge_height
;
555 template->icon
.width
= HUGE_WIDTH
* 3 / 2;
556 template->icon
.height
= HUGE_HEIGHT
;
559 template->icon
.x
= area
->x
+ (HUGE_WIDTH
- template->icon
.width
) / 2;
560 template->icon
.y
= area
->y
+ (area
->height
- template->icon
.height
) / 2;
562 template->leafname
.x
= area
->x
+ HUGE_WIDTH
+ 4;
563 template->leafname
.y
= area
->y
+ area
->height
/ 2
564 - (view
->name_height
+ 2 + view
->details_height
) / 2;
565 template->leafname
.width
= MIN(max_text_width
, view
->name_width
);
566 template->leafname
.height
= view
->name_height
;
569 return; /* Not scanned yet */
571 template->details
.x
= template->leafname
.x
;
572 template->details
.y
= template->leafname
.y
+ view
->name_height
+ 2;
575 static void large_full_template(GdkRectangle
*area
, CollectionItem
*colitem
,
576 ViewCollection
*view_collection
, Template
*template)
578 int max_text_width
= area
->width
- ICON_WIDTH
- 4;
579 ViewData
*view
= (ViewData
*) colitem
->view_data
;
580 MaskedPixmap
*image
= view
->image
;
584 template->icon
.width
= image
->width
;
585 template->icon
.height
= image
->height
;
589 template->icon
.width
= ICON_WIDTH
;
590 template->icon
.height
= ICON_HEIGHT
;
593 template->icon
.x
= area
->x
+ (ICON_WIDTH
- template->icon
.width
) / 2;
594 template->icon
.y
= area
->y
+ (area
->height
- template->icon
.height
) / 2;
597 template->leafname
.x
= area
->x
+ ICON_WIDTH
+ 4;
598 template->leafname
.y
= area
->y
+ area
->height
/ 2
599 - (view
->name_height
+ 2 + view
->details_height
) / 2;
600 template->leafname
.width
= MIN(max_text_width
, view
->name_width
);
601 template->leafname
.height
= view
->name_height
;
604 return; /* Not scanned yet */
606 template->details
.x
= template->leafname
.x
;
607 template->details
.y
= template->leafname
.y
+ view
->name_height
+ 2;
610 static void small_full_template(GdkRectangle
*area
, CollectionItem
*colitem
,
611 ViewCollection
*view_collection
, Template
*template)
613 int col_width
= view_collection
->collection
->item_width
;
614 ViewData
*view
= (ViewData
*) colitem
->view_data
;
616 small_template(area
, colitem
, view_collection
, template);
619 return; /* Not scanned yet */
621 template->details
.x
= area
->x
+ col_width
- template->details
.width
;
622 template->details
.y
= area
->y
+ area
->height
/ 2 - \
623 view
->details_height
/ 2;
626 #define INSIDE(px, py, area) \
627 (px >= area.x && py >= area.y && \
628 px <= area.x + area.width && py <= area.y + area.height)
630 static gboolean
test_point(Collection
*collection
,
631 int point_x
, int point_y
,
632 CollectionItem
*colitem
,
633 int width
, int height
,
638 ViewData
*view
= (ViewData
*) colitem
->view_data
;
639 ViewCollection
*view_collection
= (ViewCollection
*) user_data
;
644 area
.height
= height
;
646 fill_template(&area
, colitem
, view_collection
, &template);
648 return INSIDE(point_x
, point_y
, template.leafname
) ||
649 INSIDE(point_x
, point_y
, template.icon
) ||
650 (view
->details
&& INSIDE(point_x
, point_y
, template.details
));
653 /* 'box' renders a background box if the string is also selected */
654 static void draw_string(GtkWidget
*widget
,
656 GdkRectangle
*area
, /* Area available on screen */
657 int width
, /* Width of the full string */
658 GtkStateType selection_state
,
663 ? widget
->style
->fg_gc
[selection_state
]
667 gtk_paint_flat_box(widget
->style
, widget
->window
,
668 selection_state
, GTK_SHADOW_NONE
,
669 NULL
, widget
, "text",
671 MIN(width
, area
->width
),
674 if (width
> area
->width
)
676 gdk_gc_set_clip_origin(gc
, 0, 0);
677 gdk_gc_set_clip_rectangle(gc
, area
);
680 gdk_draw_layout(widget
->window
, gc
, area
->x
, area
->y
, layout
);
682 if (width
> area
->width
)
684 static GdkGC
*red_gc
= NULL
;
689 GdkColor red
= {0, 0xffff, 0, 0};
691 red_gc
= gdk_gc_new(widget
->window
);
692 gdk_colormap_alloc_colors(
693 gtk_widget_get_colormap(widget
),
694 &red
, 1, FALSE
, TRUE
, &success
);
695 gdk_gc_set_foreground(red_gc
, &red
);
697 gdk_draw_rectangle(widget
->window
, red_gc
, TRUE
,
698 area
->x
+ area
->width
- 1, area
->y
,
700 gdk_gc_set_clip_rectangle(gc
, NULL
);
704 static void draw_small_icon(GtkWidget
*widget
,
710 int width
, height
, image_x
, image_y
;
715 if (!image
->sm_pixbuf
)
716 pixmap_make_small(image
);
718 width
= MIN(image
->sm_width
, SMALL_WIDTH
);
719 height
= MIN(image
->sm_height
, SMALL_HEIGHT
);
720 image_x
= area
->x
+ ((area
->width
- width
) >> 1);
721 image_y
= MAX(0, SMALL_HEIGHT
- image
->sm_height
);
723 gdk_pixbuf_render_to_drawable_alpha(
724 selected
? image
->sm_pixbuf_lit
: image
->sm_pixbuf
,
727 image_x
, area
->y
+ image_y
, /* dest */
729 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
730 GDK_RGB_DITHER_NORMAL
, 0, 0);
732 if (item
->flags
& ITEM_FLAG_SYMLINK
)
734 gdk_pixbuf_render_to_drawable_alpha(im_symlink
->pixbuf
,
737 image_x
, area
->y
+ 8, /* dest */
739 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
740 GDK_RGB_DITHER_NORMAL
, 0, 0);
742 else if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
744 MaskedPixmap
*mp
= item
->flags
& ITEM_FLAG_MOUNTED
748 gdk_pixbuf_render_to_drawable_alpha(mp
->pixbuf
,
751 image_x
+ 2, area
->y
+ 2, /* dest */
753 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
754 GDK_RGB_DITHER_NORMAL
, 0, 0);
758 /* Draw this icon (including any symlink or mount symbol) inside the
761 static void draw_huge_icon(GtkWidget
*widget
,
774 width
= image
->huge_width
;
775 height
= image
->huge_height
;
776 image_x
= area
->x
+ ((area
->width
- width
) >> 1);
777 image_y
= MAX(0, area
->height
- height
- 6);
779 gdk_pixbuf_render_to_drawable_alpha(
780 selected
? image
->huge_pixbuf_lit
781 : image
->huge_pixbuf
,
784 image_x
, area
->y
+ image_y
, /* dest */
786 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
787 GDK_RGB_DITHER_NORMAL
, 0, 0);
789 if (item
->flags
& ITEM_FLAG_SYMLINK
)
791 gdk_pixbuf_render_to_drawable_alpha(im_symlink
->pixbuf
,
794 image_x
, area
->y
+ 2, /* dest */
796 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
797 GDK_RGB_DITHER_NORMAL
, 0, 0);
799 else if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
801 MaskedPixmap
*mp
= item
->flags
& ITEM_FLAG_MOUNTED
805 gdk_pixbuf_render_to_drawable_alpha(mp
->pixbuf
,
808 image_x
, area
->y
+ 2, /* dest */
810 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
811 GDK_RGB_DITHER_NORMAL
, 0, 0);
815 /* Create the handers for the View interface */
816 static void view_collection_iface_init(gpointer giface
, gpointer iface_data
)
818 ViewIfaceClass
*iface
= giface
;
820 g_assert(G_TYPE_FROM_INTERFACE(iface
) == VIEW_TYPE_IFACE
);
823 iface
->sort
= view_collection_sort
;
824 iface
->style_changed
= view_collection_style_changed
;
825 iface
->autoselect
= view_collection_autoselect
;
826 iface
->add_items
= view_collection_add_items
;
827 iface
->update_items
= view_collection_update_items
;
828 iface
->delete_if
= view_collection_delete_if
;
829 iface
->clear
= view_collection_clear
;
830 iface
->select_all
= view_collection_select_all
;
831 iface
->clear_selection
= view_collection_clear_selection
;
832 iface
->count_items
= view_collection_count_items
;
833 iface
->count_selected
= view_collection_count_selected
;
834 iface
->show_cursor
= view_collection_show_cursor
;
835 iface
->get_iter
= view_collection_get_iter
;
836 iface
->cursor_to_iter
= view_collection_cursor_to_iter
;
837 iface
->set_selected
= view_collection_set_selected
;
838 iface
->get_selected
= view_collection_get_selected
;
839 iface
->set_frozen
= view_collection_set_frozen
;
840 iface
->select_only
= view_collection_select_only
;
841 iface
->wink_item
= view_collection_wink_item
;
842 iface
->autosize
= view_collection_autosize
;
843 iface
->cursor_visible
= view_collection_cursor_visible
;
844 iface
->set_base
= view_collection_set_base
;
847 /* It's time to make the tooltip appear. If we're not over the item any
848 * more, or the item doesn't need a tooltip, do nothing.
850 static gboolean
tooltip_activate(ViewCollection
*view_collection
)
852 Collection
*collection
;
857 g_return_val_if_fail(tip_item
!= NULL
, 0);
859 if (!view_collection
->filer_window
)
860 return FALSE
; /* Window has been destroyed */
864 collection
= view_collection
->collection
;
865 gdk_window_get_pointer(GTK_WIDGET(collection
)->window
, &x
, &y
, NULL
);
866 i
= collection_get_item(collection
, x
, y
);
867 if (i
== -1 || ((DirItem
*) collection
->items
[i
].data
) != tip_item
)
868 return FALSE
; /* Not still under the pointer */
870 /* OK, the filer window still exists and the pointer is still
871 * over the same item. Do we need to show a tip?
874 tip
= g_string_new(NULL
);
876 if (name_is_truncated(view_collection
, i
))
878 g_string_append(tip
, tip_item
->leafname
);
879 g_string_append_c(tip
, '\n');
882 filer_add_tip_details(view_collection
->filer_window
, tip
, tip_item
);
886 g_string_truncate(tip
, tip
->len
- 1);
888 tooltip_show(tip
->str
);
891 g_string_free(tip
, TRUE
);
896 static gboolean
name_is_truncated(ViewCollection
*view_collection
, int i
)
899 Collection
*collection
= view_collection
->collection
;
900 FilerWindow
*filer_window
= view_collection
->filer_window
;
901 CollectionItem
*colitem
= &collection
->items
[i
];
902 int col
= i
% collection
->columns
;
903 int row
= i
/ collection
->columns
;
905 ViewData
*view
= (ViewData
*) colitem
->view_data
;
907 /* TODO: What if the window is narrower than 1 column? */
908 if (filer_window
->display_style
== LARGE_ICONS
||
909 filer_window
->display_style
== HUGE_ICONS
)
910 return FALSE
; /* These wrap rather than truncate */
912 area
.x
= col
* collection
->item_width
;
913 area
.y
= row
* collection
->item_height
;
914 area
.height
= collection
->item_height
;
916 if (col
== collection
->columns
- 1)
917 area
.width
= GTK_WIDGET(collection
)->allocation
.width
- area
.x
;
919 area
.width
= collection
->item_width
;
921 fill_template(&area
, colitem
, view_collection
, &template);
923 return template.leafname
.width
< view
->name_width
;
926 static gint
coll_motion_notify(GtkWidget
*widget
,
927 GdkEventMotion
*event
,
928 ViewCollection
*view_collection
)
930 Collection
*collection
= view_collection
->collection
;
931 FilerWindow
*filer_window
= view_collection
->filer_window
;
934 i
= collection_get_item(collection
, event
->x
, event
->y
);
943 DirItem
*item
= (DirItem
*) collection
->items
[i
].data
;
945 if (item
!= tip_item
)
951 tooltip_prime((GtkFunction
) tooltip_activate
,
952 G_OBJECT(view_collection
));
956 if (motion_state
!= MOTION_READY_FOR_DND
)
959 if (!dnd_motion_moved(event
))
962 i
= collection_get_item(collection
,
963 event
->x
- (event
->x_root
- drag_start_x
),
964 event
->y
- (event
->y_root
- drag_start_y
));
968 collection_wink_item(collection
, -1);
970 if (!collection
->items
[i
].selected
)
972 if (event
->state
& GDK_BUTTON1_MASK
)
974 /* Select just this one */
975 filer_window
->temp_item_selected
= TRUE
;
976 collection_clear_except(collection
, i
);
980 if (collection
->number_selected
== 0)
981 filer_window
->temp_item_selected
= TRUE
;
982 collection_select_item(collection
, i
);
986 g_return_val_if_fail(collection
->number_selected
> 0, TRUE
);
988 if (collection
->number_selected
== 1)
990 DirItem
*item
= (DirItem
*) collection
->items
[i
].data
;
991 ViewData
*view
= (ViewData
*) collection
->items
[i
].view_data
;
994 item
= dir_update_item(filer_window
->directory
,
999 report_error(_("Item no longer exists!"));
1003 drag_one_item(widget
, event
,
1004 make_path(filer_window
->sym_path
, item
->leafname
)->str
,
1005 item
, view
? view
->image
: NULL
);
1011 uris
= g_string_new(NULL
);
1012 create_uri_list(view_collection
, uris
);
1013 drag_selection(widget
, event
, uris
->str
);
1014 g_string_free(uris
, TRUE
);
1020 /* Viewport is to be resized, so calculate increments */
1021 static void size_allocate(GtkWidget
*w
, GtkAllocation
*a
, gpointer data
)
1023 Collection
*col
= ((ViewCollection
*) data
)->collection
;
1025 col
->vadj
->step_increment
= col
->item_height
;
1026 col
->vadj
->page_increment
= col
->vadj
->page_size
;
1029 /* Append all the URIs in the selection to the string */
1030 static void create_uri_list(ViewCollection
*view_collection
, GString
*string
)
1036 leader
= g_string_new("file://");
1037 g_string_append(leader
, our_host_name_for_dnd());
1038 g_string_append(leader
, view_collection
->filer_window
->sym_path
);
1039 if (leader
->str
[leader
->len
- 1] != '/')
1040 g_string_append_c(leader
, '/');
1042 make_iter(view_collection
, &iter
, VIEW_ITER_SELECTED
);
1043 while ((item
= iter
.next(&iter
)))
1045 g_string_append(string
, leader
->str
);
1046 g_string_append(string
, item
->leafname
);
1047 g_string_append(string
, "\r\n");
1050 g_string_free(leader
, TRUE
);
1053 static gint
coll_button_release(GtkWidget
*widget
,
1054 GdkEventButton
*event
,
1055 ViewCollection
*view_collection
)
1057 if (dnd_motion_release(event
))
1059 if (motion_buttons_pressed
== 0 &&
1060 view_collection
->collection
->lasso_box
)
1062 collection_end_lasso(view_collection
->collection
,
1063 event
->button
== 1 ? GDK_SET
: GDK_INVERT
);
1068 perform_action(view_collection
, event
);
1073 static gint
coll_button_press(GtkWidget
*widget
,
1074 GdkEventButton
*event
,
1075 ViewCollection
*view_collection
)
1077 collection_set_cursor_item(view_collection
->collection
, -1);
1079 if (dnd_motion_press(widget
, event
))
1080 perform_action(view_collection
, event
);
1085 static void perform_action(ViewCollection
*view_collection
,
1086 GdkEventButton
*event
)
1088 Collection
*collection
= view_collection
->collection
;
1091 gboolean press
= event
->type
== GDK_BUTTON_PRESS
;
1092 gboolean selected
= FALSE
;
1093 OpenFlags flags
= 0;
1095 FilerWindow
*filer_window
= view_collection
->filer_window
;
1097 if (event
->button
> 3)
1100 item
= collection_get_item(collection
, event
->x
, event
->y
);
1102 if (item
!= -1 && event
->button
== 1 &&
1103 collection
->items
[item
].selected
&&
1104 filer_window
->selection_state
== GTK_STATE_INSENSITIVE
)
1106 /* Possibly a really slow DnD operation? */
1107 filer_window
->temp_item_selected
= FALSE
;
1109 filer_selection_changed(filer_window
, event
->time
);
1113 if (filer_window
->target_cb
)
1115 dnd_motion_ungrab();
1116 if (item
!= -1 && press
&& event
->button
== 1)
1119 make_item_iter(view_collection
, &iter
, item
);
1121 filer_window
->target_cb(filer_window
, &iter
,
1122 filer_window
->target_data
);
1124 filer_target_mode(filer_window
, NULL
, NULL
, NULL
);
1129 action
= bind_lookup_bev(
1130 item
== -1 ? BIND_DIRECTORY
: BIND_DIRECTORY_ICON
,
1135 dir_item
= (DirItem
*) collection
->items
[item
].data
;
1136 selected
= collection
->items
[item
].selected
;
1143 case ACT_CLEAR_SELECTION
:
1144 collection_clear_selection(collection
);
1146 case ACT_TOGGLE_SELECTED
:
1147 collection_toggle_item(collection
, item
);
1149 case ACT_SELECT_EXCL
:
1150 collection_clear_except(collection
, item
);
1153 flags
|= OPEN_SHIFT
;
1159 make_item_iter(view_collection
, &iter
, item
);
1161 if (event
->button
!= 1 || event
->state
& GDK_MOD1_MASK
)
1162 flags
|= OPEN_CLOSE_WINDOW
;
1164 flags
|= OPEN_SAME_WINDOW
;
1165 if (o_new_button_1
.int_value
)
1166 flags
^= OPEN_SAME_WINDOW
;
1167 if (event
->type
== GDK_2BUTTON_PRESS
)
1168 collection_unselect_item(collection
, item
);
1169 dnd_motion_ungrab();
1171 filer_openitem(filer_window
, &iter
, flags
);
1174 case ACT_POPUP_MENU
:
1178 dnd_motion_ungrab();
1181 make_item_iter(view_collection
, &iter
, item
);
1182 show_filer_menu(filer_window
,
1183 (GdkEvent
*) event
, &iter
);
1186 case ACT_PRIME_AND_SELECT
:
1188 collection_clear_except(collection
, item
);
1189 dnd_motion_start(MOTION_READY_FOR_DND
);
1191 case ACT_PRIME_AND_TOGGLE
:
1192 collection_toggle_item(collection
, item
);
1193 dnd_motion_start(MOTION_READY_FOR_DND
);
1195 case ACT_PRIME_FOR_DND
:
1196 dnd_motion_start(MOTION_READY_FOR_DND
);
1199 if (press
&& event
->button
< 4)
1202 collection_wink_item(collection
, item
);
1203 dnd_motion_start(MOTION_NONE
);
1206 case ACT_LASSO_CLEAR
:
1207 collection_clear_selection(collection
);
1209 case ACT_LASSO_MODIFY
:
1210 collection_lasso_box(collection
, event
->x
, event
->y
);
1213 filer_window_autosize(filer_window
);
1216 g_warning("Unsupported action : %d\n", action
);
1221 /* Nothing is selected anymore - give up primary */
1222 static void lost_selection(Collection
*collection
,
1226 ViewCollection
*view_collection
= VIEW_COLLECTION(user_data
);
1228 filer_lost_selection(view_collection
->filer_window
, time
);
1231 static void selection_changed(Collection
*collection
,
1235 ViewCollection
*view_collection
= VIEW_COLLECTION(user_data
);
1237 filer_selection_changed(view_collection
->filer_window
, time
);
1240 static void display_free_colitem(Collection
*collection
,
1241 CollectionItem
*colitem
)
1243 ViewData
*view
= (ViewData
*) colitem
->view_data
;
1250 g_object_unref(G_OBJECT(view
->layout
));
1251 view
->layout
= NULL
;
1254 g_object_unref(G_OBJECT(view
->details
));
1257 g_object_unref(view
->image
);
1262 static void add_item(ViewCollection
*view_collection
, DirItem
*item
)
1264 Collection
*collection
= view_collection
->collection
;
1265 FilerWindow
*filer_window
= view_collection
->filer_window
;
1266 int old_w
= collection
->item_width
;
1267 int old_h
= collection
->item_height
;
1270 i
= collection_insert(collection
, item
,
1271 display_create_viewdata(filer_window
, item
));
1273 calc_size(filer_window
, &collection
->items
[i
], &w
, &h
);
1275 if (w
> old_w
|| h
> old_h
)
1276 collection_set_item_size(collection
,
1281 static void style_set(Collection
*collection
,
1283 ViewCollection
*view_collection
)
1285 view_collection_style_changed(VIEW(view_collection
),
1286 VIEW_UPDATE_VIEWDATA
| VIEW_UPDATE_NAME
);
1289 /* Return the size needed for this item */
1290 static void calc_size(FilerWindow
*filer_window
, CollectionItem
*colitem
,
1291 int *width
, int *height
)
1293 int pix_width
, pix_height
;
1295 DisplayStyle style
= filer_window
->display_style
;
1296 ViewData
*view
= (ViewData
*) colitem
->view_data
;
1298 if (filer_window
->details_type
== DETAILS_NONE
)
1300 if (style
== HUGE_ICONS
)
1304 if (!view
->image
->huge_pixbuf
)
1305 pixmap_make_huge(view
->image
);
1306 pix_width
= view
->image
->huge_width
;
1307 pix_height
= view
->image
->huge_height
;
1311 pix_width
= HUGE_WIDTH
* 3 / 2;
1312 pix_height
= HUGE_HEIGHT
* 3 / 2;
1314 *width
= MAX(pix_width
, view
->name_width
) + 4;
1315 *height
= view
->name_height
+ pix_height
+ 4;
1317 else if (style
== SMALL_ICONS
)
1319 w
= MIN(view
->name_width
, o_small_width
.int_value
);
1320 *width
= SMALL_WIDTH
+ 12 + w
;
1321 *height
= MAX(view
->name_height
, SMALL_HEIGHT
) + 4;
1326 pix_width
= view
->image
->width
;
1328 pix_width
= ICON_WIDTH
;
1329 *width
= MAX(pix_width
, view
->name_width
) + 4;
1330 *height
= view
->name_height
+ ICON_HEIGHT
+ 2;
1335 w
= view
->details_width
;
1336 if (style
== HUGE_ICONS
)
1338 *width
= HUGE_WIDTH
+ 12 + MAX(w
, view
->name_width
);
1339 *height
= HUGE_HEIGHT
- 4;
1341 else if (style
== SMALL_ICONS
)
1345 *width
= SMALL_WIDTH
+ view
->name_width
+ 12 + w
;
1346 text_height
= MAX(view
->name_height
,
1347 view
->details_height
);
1348 *height
= MAX(text_height
, SMALL_HEIGHT
) + 4;
1352 *width
= ICON_WIDTH
+ 12 + MAX(w
, view
->name_width
);
1353 *height
= ICON_HEIGHT
;
1358 static void update_item(ViewCollection
*view_collection
, int i
)
1360 Collection
*collection
= view_collection
->collection
;
1361 int old_w
= collection
->item_width
;
1362 int old_h
= collection
->item_height
;
1364 CollectionItem
*colitem
;
1365 FilerWindow
*filer_window
= view_collection
->filer_window
;
1367 g_return_if_fail(i
>= 0 && i
< collection
->number_of_items
);
1369 colitem
= &collection
->items
[i
];
1371 display_update_view(filer_window
,
1372 (DirItem
*) colitem
->data
,
1373 (ViewData
*) colitem
->view_data
,
1376 calc_size(filer_window
, colitem
, &w
, &h
);
1377 if (w
> old_w
|| h
> old_h
)
1378 collection_set_item_size(collection
,
1382 collection_draw_item(collection
, i
, TRUE
);
1385 /* Implementations of the View interface. See view_iface.c for comments. */
1387 static void view_collection_style_changed(ViewIface
*view
, int flags
)
1389 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1390 FilerWindow
*filer_window
= view_collection
->filer_window
;
1392 Collection
*col
= view_collection
->collection
;
1393 int width
= MIN_ITEM_WIDTH
;
1394 int height
= SMALL_HEIGHT
;
1395 int n
= col
->number_of_items
;
1397 if (n
== 0 && filer_window
->display_style
!= SMALL_ICONS
)
1398 height
= ICON_HEIGHT
;
1400 /* Recalculate all the ViewData structs for this window
1401 * (needed if the text or image has changed in any way) and
1402 * get the size of each item.
1404 for (i
= 0; i
< n
; i
++)
1406 CollectionItem
*ci
= &col
->items
[i
];
1409 if (flags
& (VIEW_UPDATE_VIEWDATA
| VIEW_UPDATE_NAME
))
1410 display_update_view(filer_window
,
1411 (DirItem
*) ci
->data
,
1412 (ViewData
*) ci
->view_data
,
1413 (flags
& VIEW_UPDATE_NAME
) != 0);
1415 calc_size(filer_window
, ci
, &w
, &h
);
1422 collection_set_item_size(col
, width
, height
);
1424 gtk_widget_queue_draw(GTK_WIDGET(view_collection
));
1427 static void view_collection_sort(ViewIface
*view
)
1429 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1431 collection_qsort(view_collection
->collection
,
1432 view_collection
->filer_window
->sort_fn
);
1435 static gboolean
view_collection_autoselect(ViewIface
*view
, const gchar
*leaf
)
1437 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1438 Collection
*col
= view_collection
->collection
;
1441 for (i
= 0; i
< col
->number_of_items
; i
++)
1443 DirItem
*item
= (DirItem
*) col
->items
[i
].data
;
1445 if (strcmp(item
->leafname
, leaf
) == 0)
1447 if (col
->cursor_item
!= -1)
1448 collection_set_cursor_item(col
, i
);
1450 collection_wink_item(col
, i
);
1458 static void view_collection_add_items(ViewIface
*view
, GPtrArray
*items
)
1460 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1461 Collection
*collection
= view_collection
->collection
;
1462 FilerWindow
*filer_window
= view_collection
->filer_window
;
1465 old_num
= collection
->number_of_items
;
1466 for (i
= 0; i
< items
->len
; i
++)
1468 DirItem
*item
= (DirItem
*) items
->pdata
[i
];
1469 char *leafname
= item
->leafname
;
1471 if (leafname
[0] == '.')
1473 if (!filer_window
->show_hidden
)
1476 if (leafname
[1] == '\0')
1477 continue; /* Never show '.' */
1479 if (leafname
[1] == '.' &&
1480 leafname
[2] == '\0')
1481 continue; /* Never show '..' */
1484 add_item(view_collection
, item
);
1487 if (old_num
!= collection
->number_of_items
)
1488 view_collection_sort(view
);
1491 static void view_collection_update_items(ViewIface
*view
, GPtrArray
*items
)
1493 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1494 Collection
*collection
= view_collection
->collection
;
1495 FilerWindow
*filer_window
= view_collection
->filer_window
;
1498 g_return_if_fail(items
->len
> 0);
1500 /* The item data has already been modified, so this gives the
1501 * final sort order...
1503 collection_qsort(collection
, filer_window
->sort_fn
);
1505 for (i
= 0; i
< items
->len
; i
++)
1507 DirItem
*item
= (DirItem
*) items
->pdata
[i
];
1508 const gchar
*leafname
= item
->leafname
;
1511 if (leafname
[0] == '.' && filer_window
->show_hidden
== FALSE
)
1514 j
= collection_find_item(collection
, item
,
1515 filer_window
->sort_fn
);
1518 g_warning("Failed to find '%s'\n", leafname
);
1520 update_item(view_collection
, j
);
1524 static void view_collection_delete_if(ViewIface
*view
,
1525 gboolean (*test
)(gpointer item
, gpointer data
),
1528 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1529 Collection
*collection
= view_collection
->collection
;
1531 collection_delete_if(collection
, test
, data
);
1534 static void view_collection_clear(ViewIface
*view
)
1536 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1537 Collection
*collection
= view_collection
->collection
;
1539 collection_clear(collection
);
1542 static void view_collection_select_all(ViewIface
*view
)
1544 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1545 Collection
*collection
= view_collection
->collection
;
1547 collection_select_all(collection
);
1550 static void view_collection_clear_selection(ViewIface
*view
)
1552 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1553 Collection
*collection
= view_collection
->collection
;
1555 collection_clear_selection(collection
);
1558 static int view_collection_count_items(ViewIface
*view
)
1560 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1561 Collection
*collection
= view_collection
->collection
;
1563 return collection
->number_of_items
;
1566 static int view_collection_count_selected(ViewIface
*view
)
1568 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1569 Collection
*collection
= view_collection
->collection
;
1571 return collection
->number_selected
;
1574 static void view_collection_show_cursor(ViewIface
*view
)
1576 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1577 Collection
*collection
= view_collection
->collection
;
1579 collection_move_cursor(collection
, 0, 0);
1582 /* The first time the next() method is used, this is called */
1583 static DirItem
*iter_init(ViewIter
*iter
)
1585 ViewCollection
*view_collection
= iter
->view_collection
;
1586 Collection
*collection
= view_collection
->collection
;
1588 int n
= collection
->number_of_items
;
1589 int flags
= iter
->flags
;
1591 iter
->peek
= iter_peek
;
1593 if (iter
->n_remaining
== 0)
1596 if (flags
& VIEW_ITER_FROM_CURSOR
)
1598 i
= collection
->cursor_item
;
1600 return NULL
; /* No cursor */
1602 else if (flags
& VIEW_ITER_FROM_BASE
)
1603 i
= view_collection
->cursor_base
;
1605 if (i
< 0 || i
>= n
)
1607 /* Either a normal iteraction, or an iteration from an
1608 * invalid starting point.
1610 if (flags
& VIEW_ITER_BACKWARDS
)
1616 if (i
< 0 || i
>= n
)
1617 return NULL
; /* No items at all! */
1619 iter
->next
= flags
& VIEW_ITER_BACKWARDS
? iter_prev
: iter_next
;
1620 iter
->n_remaining
--;
1623 if (flags
& VIEW_ITER_SELECTED
&& !collection
->items
[i
].selected
)
1624 return iter
->next(iter
);
1625 return iter
->peek(iter
);
1627 /* Advance iter to point to the next item and return the new item
1628 * (this saves you calling peek after next each time).
1630 static DirItem
*iter_next(ViewIter
*iter
)
1632 Collection
*collection
= iter
->view_collection
->collection
;
1633 int n
= collection
->number_of_items
;
1636 g_return_val_if_fail(iter
->n_remaining
>= 0, NULL
);
1638 /* i is the last item returned (always valid) */
1640 g_return_val_if_fail(i
>= 0 && i
< n
, NULL
);
1642 while (iter
->n_remaining
)
1645 iter
->n_remaining
--;
1650 g_return_val_if_fail(i
>= 0 && i
< n
, NULL
);
1652 if (iter
->flags
& VIEW_ITER_SELECTED
&&
1653 !collection
->items
[i
].selected
)
1657 return collection
->items
[i
].data
;
1664 /* Like iter_next, but in the other direction */
1665 static DirItem
*iter_prev(ViewIter
*iter
)
1667 Collection
*collection
= iter
->view_collection
->collection
;
1668 int n
= collection
->number_of_items
;
1671 g_return_val_if_fail(iter
->n_remaining
>= 0, NULL
);
1673 /* i is the last item returned (always valid) */
1675 g_return_val_if_fail(i
>= 0 && i
< n
, NULL
);
1677 while (iter
->n_remaining
)
1680 iter
->n_remaining
--;
1683 i
= collection
->number_of_items
- 1;
1685 g_return_val_if_fail(i
>= 0 && i
< n
, NULL
);
1687 if (iter
->flags
& VIEW_ITER_SELECTED
&&
1688 !collection
->items
[i
].selected
)
1692 return collection
->items
[i
].data
;
1699 static DirItem
*iter_peek(ViewIter
*iter
)
1701 Collection
*collection
= iter
->view_collection
->collection
;
1707 g_return_val_if_fail(i
>= 0 && i
< collection
->number_of_items
, NULL
);
1709 return collection
->items
[i
].data
;
1712 static void make_iter(ViewCollection
*view_collection
, ViewIter
*iter
,
1715 Collection
*collection
= view_collection
->collection
;
1717 iter
->view_collection
= view_collection
;
1718 iter
->next
= iter_init
;
1722 iter
->flags
= flags
;
1724 if (flags
& VIEW_ITER_ONE_ONLY
)
1726 iter
->n_remaining
= 1;
1730 iter
->n_remaining
= collection
->number_of_items
;
1733 /* Set the iterator to return 'i' on the next peek() */
1734 static void make_item_iter(ViewCollection
*view_collection
,
1735 ViewIter
*iter
, int i
)
1737 Collection
*collection
= view_collection
->collection
;
1739 g_return_if_fail(i
>= -1 && i
< collection
->number_of_items
);
1741 make_iter(view_collection
, iter
, 0);
1744 iter
->next
= iter_next
;
1745 iter
->peek
= iter_peek
;
1746 iter
->n_remaining
= 0;
1749 static void view_collection_get_iter(ViewIface
*view
,
1750 ViewIter
*iter
, IterFlags flags
)
1752 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1754 make_iter(view_collection
, iter
, flags
);
1757 static void view_collection_cursor_to_iter(ViewIface
*view
, ViewIter
*iter
)
1759 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1760 Collection
*collection
= view_collection
->collection
;
1762 g_return_if_fail(iter
== NULL
||
1763 iter
->view_collection
== view_collection
);
1765 collection_set_cursor_item(collection
, iter
? iter
->i
: -1);
1768 static void view_collection_set_selected(ViewIface
*view
,
1772 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1773 Collection
*collection
= view_collection
->collection
;
1775 g_return_if_fail(iter
!= NULL
&&
1776 iter
->view_collection
== view_collection
);
1777 g_return_if_fail(iter
->i
>= 0 && iter
->i
< collection
->number_of_items
);
1780 collection_select_item(collection
, iter
->i
);
1782 collection_unselect_item(collection
, iter
->i
);
1785 static gboolean
view_collection_get_selected(ViewIface
*view
, ViewIter
*iter
)
1787 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1788 Collection
*collection
= view_collection
->collection
;
1790 g_return_val_if_fail(iter
!= NULL
&&
1791 iter
->view_collection
== view_collection
, FALSE
);
1792 g_return_val_if_fail(iter
->i
>= 0 &&
1793 iter
->i
< collection
->number_of_items
, FALSE
);
1795 return collection
->items
[iter
->i
].selected
;
1798 static void view_collection_select_only(ViewIface
*view
, ViewIter
*iter
)
1800 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1801 Collection
*collection
= view_collection
->collection
;
1803 g_return_if_fail(iter
!= NULL
&&
1804 iter
->view_collection
== view_collection
);
1805 g_return_if_fail(iter
->i
>= 0 && iter
->i
< collection
->number_of_items
);
1807 collection_clear_except(collection
, iter
->i
);
1810 static void view_collection_set_frozen(ViewIface
*view
, gboolean frozen
)
1812 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1813 Collection
*collection
= view_collection
->collection
;
1816 collection
->block_selection_changed
++;
1818 collection_unblock_selection_changed(collection
,
1819 gtk_get_current_event_time(), TRUE
);
1822 static void view_collection_wink_item(ViewIface
*view
, ViewIter
*iter
)
1824 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1825 Collection
*collection
= view_collection
->collection
;
1827 g_return_if_fail(iter
!= NULL
&&
1828 iter
->view_collection
== view_collection
);
1829 g_return_if_fail(iter
->i
>= 0 && iter
->i
< collection
->number_of_items
);
1831 collection_wink_item(collection
, iter
->i
);
1834 static void view_collection_autosize(ViewIface
*view
)
1836 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1837 FilerWindow
*filer_window
= view_collection
->filer_window
;
1838 Collection
*collection
= view_collection
->collection
;
1840 int w
= collection
->item_width
;
1841 int h
= collection
->item_height
;
1844 int max_x
, max_rows
;
1845 const float r
= 2.5;
1849 /* Get the extra height required for the toolbar and minibuffer,
1852 if (o_toolbar
.int_value
!= TOOLBAR_NONE
)
1853 t
= filer_window
->toolbar
->allocation
.height
;
1854 if (filer_window
->message
)
1855 t
+= filer_window
->message
->allocation
.height
;
1856 if (GTK_WIDGET_VISIBLE(filer_window
->minibuffer_area
))
1860 gtk_widget_size_request(filer_window
->minibuffer_area
, &req
);
1861 space
= req
.height
+ 2;
1865 n
= collection
->number_of_items
;
1868 max_x
= (o_filer_size_limit
.int_value
* screen_width
) / 100;
1869 max_rows
= (o_filer_size_limit
.int_value
* screen_height
) / (h
* 100);
1871 /* Aim for a size where
1872 * x = r(y + t + h), (1)
1873 * unless that's too wide.
1875 * t = toolbar (and minibuffer) height
1876 * r = desired (width / height) ratio
1878 * Want to display all items:
1881 * => x(x/r - t - h) = nwh (from 1)
1882 * => xx - x.rt - hr(1 - nw) = 0
1883 * => 2x = rt +/- sqrt(rt.rt + 4hr(nw - 1))
1887 * sqrt(rt.rt + ...) > rt
1889 * So, the +/- must be +:
1891 * => x = (rt + sqrt(rt.rt + 4hr(nw - 1))) / 2
1893 * ( + w - 1 to round up)
1895 x
= (r
* t
+ sqrt(r
*r
*t
*t
+ 4*h
*r
* (n
*w
- 1))) / 2 + w
- 1;
1902 cols
= MAX(cols
, 1);
1904 /* Choose rows to display all items given our chosen x.
1905 * Don't make the window *too* big!
1907 rows
= (n
+ cols
- 1) / cols
;
1908 if (rows
> max_rows
)
1911 /* Leave some room for extra icons, but only in Small Icons mode
1912 * otherwise it takes up too much space.
1913 * Also, don't add space if the minibuffer is open.
1916 space
= filer_window
->display_style
== SMALL_ICONS
? h
: 2;
1918 filer_window_set_size(filer_window
,
1920 h
* MAX(rows
, 1) + space
);
1923 static gboolean
view_collection_cursor_visible(ViewIface
*view
)
1925 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1926 Collection
*collection
= view_collection
->collection
;
1928 return collection
->cursor_item
!= -1;
1931 static void view_collection_set_base(ViewIface
*view
, ViewIter
*iter
)
1933 ViewCollection
*view_collection
= VIEW_COLLECTION(view
);
1935 view_collection
->cursor_base
= iter
->i
;
1938 static void drag_end(GtkWidget
*widget
,
1939 GdkDragContext
*context
,
1940 ViewCollection
*view_collection
)
1942 if (view_collection
->filer_window
->temp_item_selected
)
1944 view_collection_clear_selection(VIEW(view_collection
));
1945 view_collection
->filer_window
->temp_item_selected
= FALSE
;
1949 /* Remove highlights */
1950 static void drag_leave(GtkWidget
*widget
,
1951 GdkDragContext
*context
,
1953 ViewCollection
*view_collection
)
1959 g_signal_handler_disconnect(scrolled_adj
, scrolled_signal
);
1960 scrolled_adj
= NULL
;
1965 /* Called during the drag when the mouse is in a widget registered
1966 * as a drop target. Returns TRUE if we can accept the drop.
1968 static gboolean
drag_motion(GtkWidget
*widget
,
1969 GdkDragContext
*context
,
1973 ViewCollection
*view_collection
)
1977 GdkDragAction action
= context
->suggested_action
;
1978 char *new_path
= NULL
;
1979 const char *type
= NULL
;
1980 gboolean retval
= FALSE
;
1981 Collection
*collection
= view_collection
->collection
;
1983 if (o_dnd_drag_to_icons
.int_value
)
1984 item_number
= collection_get_item(collection
, x
, y
);
1988 item
= item_number
>= 0
1989 ? (DirItem
*) collection
->items
[item_number
].data
1992 if (item
&& collection
->items
[item_number
].selected
)
1995 type
= dnd_motion_item(context
, &item
);
2000 /* Don't allow drops to non-writeable directories. BUT, still
2001 * allow drops on non-writeable SUBdirectories so that we can
2002 * do the spring-open thing.
2004 if (item
&& type
== drop_dest_dir
&&
2005 !(item
->flags
& ITEM_FLAG_APPDIR
))
2008 /* XXX: This is needed so that directories don't
2009 * spring open while we scroll. Should go in
2010 * view_collection.c, I think.
2013 /* XXX: Now it IS in view_collection, maybe we should
2017 GtkObject
*vadj
= GTK_OBJECT(collection
->vadj
);
2019 /* Subdir: prepare for spring-open */
2020 if (scrolled_adj
!= vadj
)
2023 gtk_signal_disconnect(scrolled_adj
,
2025 scrolled_adj
= vadj
;
2026 scrolled_signal
= gtk_signal_connect(
2029 GTK_SIGNAL_FUNC(scrolled
),
2033 dnd_spring_load(context
, view_collection
->filer_window
);
2040 collection_set_cursor_item(collection
,
2045 collection_set_cursor_item(collection
, -1);
2047 /* Disallow background drops within a single window */
2048 if (type
&& gtk_drag_get_source_widget(context
) == widget
)
2055 new_path
= make_path(
2056 view_collection
->filer_window
->sym_path
,
2057 item
->leafname
)->str
;
2059 new_path
= view_collection
->filer_window
->sym_path
;
2062 g_dataset_set_data(context
, "drop_dest_type", (gpointer
) type
);
2065 gdk_drag_status(context
, action
, time
);
2066 g_dataset_set_data_full(context
, "drop_dest_path",
2067 g_strdup(new_path
), g_free
);