]> Pileus Git - ~andy/gtk/blob - gtk/gtkiconview.c
6d41f8c09361feaf026c89e5fa731b8840eeec47
[~andy/gtk] / gtk / gtkiconview.c
1 /* gtkiconview.c
2  * Copyright (C) 2002, 2004  Anders Carlsson <andersca@gnu.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include <config.h>
21 #include <string.h>
22
23 #include <gdk/gdkkeysyms.h>
24
25 #include "gtkiconview.h"
26 #include "gtkmarshalers.h"
27 #include "gtkbindings.h"
28 #include "gtkdnd.h"
29 #include "gtkmain.h"
30 #include "gtksignal.h"
31 #include "gtkintl.h"
32
33 #define MINIMUM_ICON_ITEM_WIDTH 100
34 #define ICON_TEXT_PADDING 3
35
36 #define ICON_VIEW_TOP_MARGIN 6
37 #define ICON_VIEW_BOTTOM_MARGIN 6
38 #define ICON_VIEW_LEFT_MARGIN 6
39 #define ICON_VIEW_RIGHT_MARGIN 6
40 #define ICON_VIEW_ICON_PADDING 6
41
42 #define GTK_ICON_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_ICON_VIEW, GtkIconViewPrivate))
43 #define VALID_MODEL_AND_COLUMNS(obj) ((obj)->priv->model != NULL && \
44                                       ((obj)->priv->pixbuf_column != -1 || \
45                                        (obj)->priv->text_column != -1 || \
46                                        (obj)->priv->markup_column != -1))
47
48 typedef struct 
49 {
50   GtkTreeIter iter;
51   int index;
52   
53   gint row, col;
54
55   /* Bounding boxes */
56   gint x, y;
57   gint width, height;
58
59   gint pixbuf_x, pixbuf_y;
60   gint pixbuf_height, pixbuf_width;
61
62   gint layout_x, layout_y;
63   gint layout_width, layout_height;
64
65   guint selected : 1;
66   guint selected_before_rubberbanding : 1;
67 } GtkIconViewItem;
68
69 struct _GtkIconViewPrivate
70 {
71   gint width, height;
72
73   gint text_column;
74   gint markup_column;
75   gint pixbuf_column;
76   
77   GtkSelectionMode selection_mode;
78
79   GdkWindow *bin_window;
80
81   GtkTreeModel *model;
82   
83   GList *items;
84   
85   GtkAdjustment *hadjustment;
86   GtkAdjustment *vadjustment;
87
88   guint layout_idle_id;
89   
90   gboolean doing_rubberband;
91   gint rubberband_x1, rubberband_y1;
92   gint rubberband_x2, rubberband_y2;
93
94   guint scroll_timeout_id;
95   gint scroll_value_diff;
96   gint event_last_x, event_last_y;
97
98   GtkIconViewItem *anchor_item;
99   GtkIconViewItem *cursor_item;
100
101   guint ctrl_pressed : 1;
102   guint shift_pressed : 1;
103   
104   GtkIconViewItem *last_single_clicked;
105
106 #ifdef DND_WORKS
107   /* Drag-and-drop. */
108   gint pressed_button;
109   gint press_start_x;
110   gint press_start_y;
111 #endif
112   
113   /* Layout used to draw icon text */
114   PangoLayout *layout;
115 };
116
117 /* Signals */
118 enum
119 {
120   ITEM_ACTIVATED,
121   SELECTION_CHANGED,
122   SELECT_ALL,
123   UNSELECT_ALL,
124   SELECT_CURSOR_ITEM,
125   TOGGLE_CURSOR_ITEM,
126   MOVE_CURSOR,
127   ACTIVATE_CURSOR_ITEM,
128   LAST_SIGNAL
129 };
130
131 /* Properties */
132 enum
133 {
134   PROP_0,
135   PROP_PIXBUF_COLUMN,
136   PROP_TEXT_COLUMN,
137   PROP_MARKUP_COLUMN,  
138   PROP_SELECTION_MODE,
139   PROP_MODEL,
140 };
141
142 /* GObject signals */
143 static void gtk_icon_view_finalize     (GObject      *object);
144 static void gtk_icon_view_set_property (GObject      *object,
145                                         guint         prop_id,
146                                         const GValue *value,
147                                         GParamSpec   *pspec);
148 static void gtk_icon_view_get_property (GObject      *object,
149                                         guint         prop_id,
150                                         GValue       *value,
151                                         GParamSpec   *pspec);
152
153
154 /* GtkObject signals */
155 static void gtk_icon_view_destroy (GtkObject *object);
156
157 /* GtkWidget signals */
158 static void     gtk_icon_view_realize        (GtkWidget      *widget);
159 static void     gtk_icon_view_unrealize      (GtkWidget      *widget);
160 static void     gtk_icon_view_map            (GtkWidget      *widget);
161 static void     gtk_icon_view_size_request   (GtkWidget      *widget,
162                                               GtkRequisition *requisition);
163 static void     gtk_icon_view_size_allocate  (GtkWidget      *widget,
164                                               GtkAllocation  *allocation);
165 static gboolean gtk_icon_view_expose         (GtkWidget      *widget,
166                                               GdkEventExpose *expose);
167 static gboolean gtk_icon_view_motion         (GtkWidget      *widget,
168                                               GdkEventMotion *event);
169 static gboolean gtk_icon_view_button_press   (GtkWidget      *widget,
170                                               GdkEventButton *event);
171 static gboolean gtk_icon_view_button_release (GtkWidget      *widget,
172                                               GdkEventButton *event);
173
174 /* GtkIconView signals */
175 static void     gtk_icon_view_set_adjustments           (GtkIconView   *icon_view,
176                                                          GtkAdjustment *hadj,
177                                                          GtkAdjustment *vadj);
178 static void     gtk_icon_view_real_select_all           (GtkIconView   *icon_view);
179 static void     gtk_icon_view_real_unselect_all         (GtkIconView   *icon_view);
180 static void     gtk_icon_view_real_select_cursor_item   (GtkIconView   *icon_view);
181 static void     gtk_icon_view_real_toggle_cursor_item   (GtkIconView   *icon_view);
182 static gboolean gtk_icon_view_real_activate_cursor_item (GtkIconView   *icon_view);
183
184 /* Internal functions */
185 static void       gtk_icon_view_adjustment_changed          (GtkAdjustment   *adjustment,
186                                                              GtkIconView     *icon_view);
187 static void       gtk_icon_view_layout                      (GtkIconView     *icon_view);
188 static void       gtk_icon_view_paint_item                  (GtkIconView     *icon_view,
189                                                              GtkIconViewItem *item,
190                                                              GdkRectangle    *area);
191 static void       gtk_icon_view_paint_rubberband            (GtkIconView     *icon_view,
192                                                              GdkRectangle    *area);
193 static void       gtk_icon_view_queue_draw_item             (GtkIconView     *icon_view,
194                                                              GtkIconViewItem *item);
195 static void       gtk_icon_view_queue_layout                (GtkIconView     *icon_view);
196 static void       gtk_icon_view_set_cursor_item             (GtkIconView     *icon_view,
197                                                              GtkIconViewItem *item);
198 static void       gtk_icon_view_start_rubberbanding         (GtkIconView     *icon_view,
199                                                              gint             x,
200                                                              gint             y);
201 static void       gtk_icon_view_stop_rubberbanding          (GtkIconView     *icon_view);
202 static void       gtk_icon_view_update_rubberband_selection (GtkIconView     *icon_view);
203 static gboolean   gtk_icon_view_item_hit_test               (GtkIconViewItem *item,
204                                                              gint             x,
205                                                              gint             y,
206                                                              gint             width,
207                                                              gint             height);
208 #ifdef DND_WORKS
209 static gboolean   gtk_icon_view_maybe_begin_dragging_items  (GtkIconView     *icon_view,
210                                                              GdkEventMotion  *event);
211 #endif
212 static gboolean   gtk_icon_view_unselect_all_internal       (GtkIconView     *icon_view);
213 static void       gtk_icon_view_calculate_item_size         (GtkIconView     *icon_view,
214                                                              GtkIconViewItem *item);
215 static void       gtk_icon_view_update_rubberband           (gpointer         data);
216 static void       gtk_icon_view_item_invalidate_size        (GtkIconViewItem *item);
217 static void       gtk_icon_view_invalidate_sizes            (GtkIconView     *icon_view);
218 static void       gtk_icon_view_add_move_binding            (GtkBindingSet   *binding_set,
219                                                              guint            keyval,
220                                                              guint            modmask,
221                                                              GtkMovementStep  step,
222                                                              gint             count);
223 static gboolean   gtk_icon_view_real_move_cursor            (GtkIconView     *icon_view,
224                                                              GtkMovementStep  step,
225                                                              gint             count);
226 static void       gtk_icon_view_move_cursor_up_down         (GtkIconView     *icon_view,
227                                                              gint             count);
228 static void       gtk_icon_view_move_cursor_page_up_down    (GtkIconView     *icon_view,
229                                                              gint             count);
230 static void       gtk_icon_view_move_cursor_left_right      (GtkIconView     *icon_view,
231                                                              gint             count);
232 static void       gtk_icon_view_move_cursor_start_end       (GtkIconView     *icon_view,
233                                                              gint             count);
234 static void       gtk_icon_view_scroll_to_item              (GtkIconView     *icon_view,
235                                                              GtkIconViewItem *item);
236 static GdkPixbuf *gtk_icon_view_get_item_icon               (GtkIconView     *icon_view,
237                                                              GtkIconViewItem *item);
238 static void       gtk_icon_view_update_item_text            (GtkIconView     *icon_view,
239                                                              GtkIconViewItem *item);
240 static void       gtk_icon_view_select_item                 (GtkIconView     *icon_view,
241                                                              GtkIconViewItem *item);
242 static void       gtk_icon_view_unselect_item               (GtkIconView     *icon_view,
243                                                              GtkIconViewItem *item);
244 static gboolean gtk_icon_view_select_all_between            (GtkIconView     *icon_view,
245                                                              GtkIconViewItem *anchor,
246                                                              GtkIconViewItem *cursor);
247
248 static GtkIconViewItem *gtk_icon_view_get_item_at_pos (GtkIconView *icon_view,
249                                                        gint         x,
250                                                        gint         y);
251
252
253
254
255
256 static GtkContainerClass *parent_class = NULL;
257 static guint icon_view_signals[LAST_SIGNAL] = { 0 };
258
259 G_DEFINE_TYPE (GtkIconView, gtk_icon_view, GTK_TYPE_CONTAINER);
260
261 static void
262 gtk_icon_view_class_init (GtkIconViewClass *klass)
263 {
264   GObjectClass *gobject_class;
265   GtkObjectClass *object_class;
266   GtkWidgetClass *widget_class;
267   GtkBindingSet *binding_set;
268   
269   parent_class = g_type_class_peek_parent (klass);
270   binding_set = gtk_binding_set_by_class (klass);
271
272   g_type_class_add_private (klass, sizeof (GtkIconViewPrivate));
273
274   gobject_class = (GObjectClass *) klass;
275   object_class = (GtkObjectClass *) klass;
276   widget_class = (GtkWidgetClass *) klass;
277
278   gobject_class->finalize = gtk_icon_view_finalize;
279   gobject_class->set_property = gtk_icon_view_set_property;
280   gobject_class->get_property = gtk_icon_view_get_property;
281
282   object_class->destroy = gtk_icon_view_destroy;
283   
284   widget_class->realize = gtk_icon_view_realize;
285   widget_class->unrealize = gtk_icon_view_unrealize;
286   widget_class->map = gtk_icon_view_map;
287   widget_class->size_request = gtk_icon_view_size_request;
288   widget_class->size_allocate = gtk_icon_view_size_allocate;
289   widget_class->expose_event = gtk_icon_view_expose;
290   widget_class->motion_notify_event = gtk_icon_view_motion;
291   widget_class->button_press_event = gtk_icon_view_button_press;
292   widget_class->button_release_event = gtk_icon_view_button_release;
293   
294   klass->set_scroll_adjustments = gtk_icon_view_set_adjustments;
295   klass->select_all = gtk_icon_view_real_select_all;
296   klass->unselect_all = gtk_icon_view_real_unselect_all;
297   klass->select_cursor_item = gtk_icon_view_real_select_cursor_item;
298   klass->toggle_cursor_item = gtk_icon_view_real_toggle_cursor_item;
299   klass->activate_cursor_item = gtk_icon_view_real_activate_cursor_item;  
300   klass->move_cursor = gtk_icon_view_real_move_cursor;
301   
302   /* Properties */
303   /**
304    * GtkIconView:selection-mode:
305    * 
306    * The ::selection-mode property specifies the selection mode of
307    * icon view. If the mode is #GTK_SELECTION_MULTIPLE, rubberband selection
308    * is enabled, for the other modes, only keyboard selection is possible.
309    *
310    * Since: 2.6
311    */
312   g_object_class_install_property (gobject_class,
313                                    PROP_SELECTION_MODE,
314                                    g_param_spec_enum ("selection_mode",
315                                                       P_("Selection mode"),
316                                                       P_("The selection mode"),
317                                                       GTK_TYPE_SELECTION_MODE,
318                                                       GTK_SELECTION_SINGLE,
319                                                       G_PARAM_READWRITE));
320
321   /**
322    * GtkIconView:pixbuf-column:
323    *
324    * The ::pixbuf-column property contains the number of the model column
325    * containing the pixbufs which are displayed. The pixbuf column must be 
326    * of type #GDK_TYPE_PIXBUF. Setting this property to -1 turns off the
327    * display of pixbufs.
328    *
329    * Since: 2.6
330    */
331   g_object_class_install_property (gobject_class,
332                                    PROP_PIXBUF_COLUMN,
333                                    g_param_spec_int ("pixbuf_column",
334                                                      P_("Pixbuf column"),
335                                                      P_("Model column used to retrieve the icon pixbuf from"),
336                                                      -1, G_MAXINT, -1,
337                                                      G_PARAM_READWRITE));
338
339   /**
340    * GtkIconView:text-column:
341    *
342    * The ::text-column property contains the number of the model column
343    * containing the texts which are displayed. The text column must be 
344    * of type #G_TYPE_STRING. If this property and the :markup-column 
345    * property are both set to -1, no texts are displayed.   
346    *
347    * Since: 2.6
348    */
349   g_object_class_install_property (gobject_class,
350                                    PROP_TEXT_COLUMN,
351                                    g_param_spec_int ("text_column",
352                                                      P_("Text column"),
353                                                      P_("Model column used to retrieve the text from"),
354                                                      -1, G_MAXINT, -1,
355                                                      G_PARAM_READWRITE));
356
357   
358   /**
359    * GtkIconView:markup-column:
360    *
361    * The ::markup-column property contains the number of the model column
362    * containing markup information to be displayed. The markup column must be 
363    * of type #G_TYPE_STRING. If this property and the :text-column property 
364    * are both set to column numbers, it overrides the text column.
365    * If both are set to -1, no texts are displayed.   
366    *
367    * Since: 2.6
368    */
369   g_object_class_install_property (gobject_class,
370                                    PROP_MARKUP_COLUMN,
371                                    g_param_spec_int ("markup_column",
372                                                      P_("Markup column"),
373                                                      P_("Model column used to retrieve the text if using Pango markup"),
374                                                      -1, G_MAXINT, -1,
375                                                      G_PARAM_READWRITE));
376   
377   g_object_class_install_property (gobject_class,
378                                    PROP_MODEL,
379                                    g_param_spec_object ("model",
380                                                         P_("Icon View Model"),
381                                                         P_("The model for the icon view"),
382                                                         GTK_TYPE_TREE_MODEL,
383                                                         G_PARAM_READWRITE));
384   
385
386   /* Style properties */
387   gtk_widget_class_install_style_property (widget_class,
388                                            g_param_spec_boxed ("selection_box_color",
389                                                                P_("Selection Box Color"),
390                                                                P_("Color of the selection box"),
391                                                                GDK_TYPE_COLOR,
392                                                                G_PARAM_READABLE));
393
394   gtk_widget_class_install_style_property (widget_class,
395                                            g_param_spec_uchar ("selection_box_alpha",
396                                                                P_("Selection Box Alpha"),
397                                                                P_("Opacity of the selection box"),
398                                                                0, 0xff,
399                                                                0x40,
400                                                                G_PARAM_READABLE));
401
402   /* Signals */
403   widget_class->set_scroll_adjustments_signal =
404     g_signal_new ("set_scroll_adjustments",
405                   G_TYPE_FROM_CLASS (gobject_class),
406                   G_SIGNAL_RUN_LAST,
407                   G_STRUCT_OFFSET (GtkIconViewClass, set_scroll_adjustments),
408                   NULL, NULL, 
409                   _gtk_marshal_VOID__OBJECT_OBJECT,
410                   G_TYPE_NONE, 2,
411                   GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
412
413   icon_view_signals[ITEM_ACTIVATED] =
414     g_signal_new ("item_activated",
415                   G_TYPE_FROM_CLASS (gobject_class),
416                   G_SIGNAL_RUN_LAST,
417                   G_STRUCT_OFFSET (GtkIconViewClass, item_activated),
418                   NULL, NULL,
419                   g_cclosure_marshal_VOID__BOXED,
420                   G_TYPE_NONE, 1,
421                   GTK_TYPE_TREE_PATH);
422
423   icon_view_signals[SELECTION_CHANGED] =
424     g_signal_new ("selection_changed",
425                   G_TYPE_FROM_CLASS (gobject_class),
426                   G_SIGNAL_RUN_FIRST,
427                   G_STRUCT_OFFSET (GtkIconViewClass, selection_changed),
428                   NULL, NULL,
429                   g_cclosure_marshal_VOID__VOID,
430                   G_TYPE_NONE, 0);
431   
432   icon_view_signals[SELECT_ALL] =
433     g_signal_new ("select_all",
434                   G_TYPE_FROM_CLASS (gobject_class),
435                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
436                   G_STRUCT_OFFSET (GtkIconViewClass, select_all),
437                   NULL, NULL,
438                   g_cclosure_marshal_VOID__VOID,
439                   G_TYPE_NONE, 0);
440   
441   icon_view_signals[UNSELECT_ALL] =
442     g_signal_new ("unselect_all",
443                   G_TYPE_FROM_CLASS (gobject_class),
444                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
445                   G_STRUCT_OFFSET (GtkIconViewClass, unselect_all),
446                   NULL, NULL,
447                   g_cclosure_marshal_VOID__VOID,
448                   G_TYPE_NONE, 0);
449
450   icon_view_signals[SELECT_CURSOR_ITEM] =
451     g_signal_new ("select_cursor_item",
452                   G_TYPE_FROM_CLASS (gobject_class),
453                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
454                   G_STRUCT_OFFSET (GtkIconViewClass, select_cursor_item),
455                   NULL, NULL,
456                   g_cclosure_marshal_VOID__VOID,
457                   G_TYPE_NONE, 0);
458
459   icon_view_signals[TOGGLE_CURSOR_ITEM] =
460     g_signal_new ("toggle_cursor_item",
461                   G_TYPE_FROM_CLASS (gobject_class),
462                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
463                   G_STRUCT_OFFSET (GtkIconViewClass, toggle_cursor_item),
464                   NULL, NULL,
465                   g_cclosure_marshal_VOID__VOID,
466                   G_TYPE_NONE, 0);
467
468   icon_view_signals[ACTIVATE_CURSOR_ITEM] =
469     g_signal_new ("activate_cursor_item",
470                   G_TYPE_FROM_CLASS (gobject_class),
471                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
472                   G_STRUCT_OFFSET (GtkIconViewClass, activate_cursor_item),
473                   NULL, NULL,
474                   _gtk_marshal_BOOLEAN__VOID,
475                   G_TYPE_BOOLEAN, 0);
476   
477   icon_view_signals[MOVE_CURSOR] =
478     g_signal_new ("move_cursor",
479                   G_TYPE_FROM_CLASS (gobject_class),
480                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
481                   G_STRUCT_OFFSET (GtkIconViewClass, move_cursor),
482                   NULL, NULL,
483                   _gtk_marshal_BOOLEAN__ENUM_INT,
484                   G_TYPE_BOOLEAN, 2,
485                   GTK_TYPE_MOVEMENT_STEP,
486                   G_TYPE_INT);
487
488   /* Key bindings */
489   gtk_binding_entry_add_signal (binding_set, GDK_a, GDK_CONTROL_MASK, "select_all", 0);
490   gtk_binding_entry_add_signal (binding_set, GDK_a, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "unselect_all", 0);
491   gtk_binding_entry_add_signal (binding_set, GDK_space, 0, "select_cursor_item", 0);
492   gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_CONTROL_MASK, "toggle_cursor_item", 0);
493
494   gtk_binding_entry_add_signal (binding_set, GDK_Return, 0, "activate_cursor_item", 0);
495   gtk_binding_entry_add_signal (binding_set, GDK_ISO_Enter, 0, "activate_cursor_item", 0);
496   gtk_binding_entry_add_signal (binding_set, GDK_KP_Enter, 0, "activate_cursor_item", 0);
497
498   gtk_icon_view_add_move_binding (binding_set, GDK_Up, 0,
499                                   GTK_MOVEMENT_DISPLAY_LINES, -1);
500   gtk_icon_view_add_move_binding (binding_set, GDK_KP_Up, 0,
501                                   GTK_MOVEMENT_DISPLAY_LINES, -1);
502
503   gtk_icon_view_add_move_binding (binding_set, GDK_Down, 0,
504                                   GTK_MOVEMENT_DISPLAY_LINES, 1);
505   gtk_icon_view_add_move_binding (binding_set, GDK_KP_Down, 0,
506                                   GTK_MOVEMENT_DISPLAY_LINES, 1);
507
508   gtk_icon_view_add_move_binding (binding_set, GDK_p, GDK_CONTROL_MASK,
509                                   GTK_MOVEMENT_DISPLAY_LINES, -1);
510
511   gtk_icon_view_add_move_binding (binding_set, GDK_n, GDK_CONTROL_MASK,
512                                   GTK_MOVEMENT_DISPLAY_LINES, 1);
513
514   gtk_icon_view_add_move_binding (binding_set, GDK_Home, 0,
515                                   GTK_MOVEMENT_BUFFER_ENDS, -1);
516   gtk_icon_view_add_move_binding (binding_set, GDK_KP_Home, 0,
517                                   GTK_MOVEMENT_BUFFER_ENDS, -1);
518
519   gtk_icon_view_add_move_binding (binding_set, GDK_End, 0,
520                                   GTK_MOVEMENT_BUFFER_ENDS, 1);
521   gtk_icon_view_add_move_binding (binding_set, GDK_KP_End, 0,
522                                   GTK_MOVEMENT_BUFFER_ENDS, 1);
523
524   gtk_icon_view_add_move_binding (binding_set, GDK_Page_Up, 0,
525                                   GTK_MOVEMENT_PAGES, -1);
526   gtk_icon_view_add_move_binding (binding_set, GDK_KP_Page_Up, 0,
527                                   GTK_MOVEMENT_PAGES, -1);
528
529   gtk_icon_view_add_move_binding (binding_set, GDK_Page_Down, 0,
530                                   GTK_MOVEMENT_PAGES, 1);
531   gtk_icon_view_add_move_binding (binding_set, GDK_KP_Page_Down, 0,
532                                   GTK_MOVEMENT_PAGES, 1);
533
534   gtk_icon_view_add_move_binding (binding_set, GDK_Right, 0, 
535                                   GTK_MOVEMENT_VISUAL_POSITIONS, 1);
536   gtk_icon_view_add_move_binding (binding_set, GDK_Left, 0, 
537                                   GTK_MOVEMENT_VISUAL_POSITIONS, -1);
538
539   gtk_icon_view_add_move_binding (binding_set, GDK_KP_Right, 0, 
540                                   GTK_MOVEMENT_VISUAL_POSITIONS, 1);
541   gtk_icon_view_add_move_binding (binding_set, GDK_KP_Left, 0, 
542                                   GTK_MOVEMENT_VISUAL_POSITIONS, -1);
543 }
544
545 static void
546 gtk_icon_view_init (GtkIconView *icon_view)
547 {
548   icon_view->priv = GTK_ICON_VIEW_GET_PRIVATE (icon_view);
549   
550   icon_view->priv->width = 0;
551   icon_view->priv->height = 0;
552   icon_view->priv->selection_mode = GTK_SELECTION_SINGLE;
553 #ifdef DND_WORKS
554   icon_view->priv->pressed_button = -1;
555   icon_view->priv->press_start_x = -1;
556   icon_view->priv->press_start_y = -1;
557 #endif
558   icon_view->priv->text_column = -1;
559   icon_view->priv->markup_column = -1;  
560   icon_view->priv->pixbuf_column = -1;
561   
562   icon_view->priv->layout = gtk_widget_create_pango_layout (GTK_WIDGET (icon_view), NULL);
563
564   pango_layout_set_wrap (icon_view->priv->layout, PANGO_WRAP_WORD_CHAR);
565
566   GTK_WIDGET_SET_FLAGS (icon_view, GTK_CAN_FOCUS);
567   
568   gtk_icon_view_set_adjustments (icon_view, NULL, NULL);
569 }
570
571 static void
572 gtk_icon_view_destroy (GtkObject *object)
573 {
574   GtkIconView *icon_view;
575
576   icon_view = GTK_ICON_VIEW (object);
577   
578   gtk_icon_view_set_model (icon_view, NULL);
579   
580   if (icon_view->priv->layout_idle_id != 0)
581     g_source_remove (icon_view->priv->layout_idle_id);
582
583   if (icon_view->priv->scroll_timeout_id != 0)
584     g_source_remove (icon_view->priv->scroll_timeout_id);
585   
586   (GTK_OBJECT_CLASS (parent_class)->destroy) (object);
587 }
588
589 /* GObject methods */
590 static void
591 gtk_icon_view_finalize (GObject *object)
592 {
593   GtkIconView *icon_view;
594
595   icon_view = GTK_ICON_VIEW (object);
596
597   g_object_unref (icon_view->priv->layout);
598   
599   (G_OBJECT_CLASS (parent_class)->finalize) (object);
600 }
601
602
603 static void
604 gtk_icon_view_set_property (GObject      *object,
605                             guint         prop_id,
606                             const GValue *value,
607                             GParamSpec   *pspec)
608 {
609   GtkIconView *icon_view;
610
611   icon_view = GTK_ICON_VIEW (object);
612
613   switch (prop_id)
614     {
615     case PROP_SELECTION_MODE:
616       gtk_icon_view_set_selection_mode (icon_view, g_value_get_enum (value));
617       break;
618     case PROP_PIXBUF_COLUMN:
619       gtk_icon_view_set_pixbuf_column (icon_view, g_value_get_int (value));
620       break;
621     case PROP_TEXT_COLUMN:
622       gtk_icon_view_set_text_column (icon_view, g_value_get_int (value));
623       break;
624     case PROP_MARKUP_COLUMN:
625       gtk_icon_view_set_markup_column (icon_view, g_value_get_int (value));
626       break;
627     case PROP_MODEL:
628       gtk_icon_view_set_model (icon_view, g_value_get_object (value));
629       break;
630       
631     default:
632       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
633       break;
634     }
635 }
636
637 static void
638 gtk_icon_view_get_property (GObject      *object,
639                             guint         prop_id,
640                             GValue       *value,
641                             GParamSpec   *pspec)
642 {
643   GtkIconView *icon_view;
644
645   icon_view = GTK_ICON_VIEW (object);
646
647   switch (prop_id)
648     {
649     case PROP_SELECTION_MODE:
650       g_value_set_enum (value, icon_view->priv->selection_mode);
651       break;
652     case PROP_PIXBUF_COLUMN:
653       g_value_set_int (value, icon_view->priv->pixbuf_column);
654       break;
655     case PROP_TEXT_COLUMN:
656       g_value_set_int (value, icon_view->priv->text_column);
657       break;
658     case PROP_MARKUP_COLUMN:
659       g_value_set_int (value, icon_view->priv->markup_column);
660       break;
661     case PROP_MODEL:
662       g_value_set_object (value, icon_view->priv->model);
663       break;
664     default:
665       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
666       break;
667     }
668 }
669
670 /* GtkWidget signals */
671 static void
672 gtk_icon_view_realize (GtkWidget *widget)
673 {
674   GtkIconView *icon_view;
675   GdkWindowAttr attributes;
676   gint attributes_mask;
677   
678   icon_view = GTK_ICON_VIEW (widget);
679
680   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
681
682   /* Make the main, clipping window */
683   attributes.window_type = GDK_WINDOW_CHILD;
684   attributes.x = widget->allocation.x;
685   attributes.y = widget->allocation.y;
686   attributes.width = widget->allocation.width;
687   attributes.height = widget->allocation.height;
688   attributes.wclass = GDK_INPUT_OUTPUT;
689   attributes.visual = gtk_widget_get_visual (widget);
690   attributes.colormap = gtk_widget_get_colormap (widget);
691   attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
692
693   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
694
695   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
696                                    &attributes, attributes_mask);
697   gdk_window_set_user_data (widget->window, widget);
698
699   /* Make the window for the icon view */
700   attributes.x = 0;
701   attributes.y = 0;
702   attributes.width = MAX (icon_view->priv->width, widget->allocation.width);
703   attributes.height = MAX (icon_view->priv->height, widget->allocation.height);
704   attributes.event_mask = (GDK_EXPOSURE_MASK |
705                            GDK_SCROLL_MASK |
706                            GDK_POINTER_MOTION_MASK |
707                            GDK_BUTTON_PRESS_MASK |
708                            GDK_BUTTON_RELEASE_MASK |
709                            GDK_KEY_PRESS_MASK |
710                            GDK_KEY_RELEASE_MASK) |
711     gtk_widget_get_events (widget);
712   
713   icon_view->priv->bin_window = gdk_window_new (widget->window,
714                                           &attributes, attributes_mask);
715   gdk_window_set_user_data (icon_view->priv->bin_window, widget);
716
717   widget->style = gtk_style_attach (widget->style, widget->window);
718   gdk_window_set_background (icon_view->priv->bin_window, &widget->style->base[widget->state]);
719   gdk_window_set_background (widget->window, &widget->style->base[widget->state]);
720
721   
722 }
723
724 static void
725 gtk_icon_view_unrealize (GtkWidget *widget)
726 {
727   GtkIconView *icon_view;
728
729   icon_view = GTK_ICON_VIEW (widget);
730
731   gdk_window_set_user_data (icon_view->priv->bin_window, NULL);
732   gdk_window_destroy (icon_view->priv->bin_window);
733   icon_view->priv->bin_window = NULL;
734
735   /* GtkWidget::unrealize destroys children and widget->window */
736   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
737     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
738 }
739
740 static void
741 gtk_icon_view_map (GtkWidget *widget)
742 {
743   GtkIconView *icon_view;
744
745   icon_view = GTK_ICON_VIEW (widget);
746
747   GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
748
749   gdk_window_show (icon_view->priv->bin_window);
750   gdk_window_show (widget->window);
751 }
752
753 static void
754 gtk_icon_view_size_request (GtkWidget      *widget,
755                             GtkRequisition *requisition)
756 {
757   GtkIconView *icon_view;
758
759   icon_view = GTK_ICON_VIEW (widget);
760
761   requisition->width = icon_view->priv->width;
762   requisition->height = icon_view->priv->height;
763 }
764
765 static void
766 gtk_icon_view_size_allocate (GtkWidget      *widget,
767                              GtkAllocation  *allocation)
768 {
769   GtkIconView *icon_view;
770
771   widget->allocation = *allocation;
772   
773   icon_view = GTK_ICON_VIEW (widget);
774   
775   if (GTK_WIDGET_REALIZED (widget))
776     {
777       gdk_window_move_resize (widget->window,
778                               allocation->x, allocation->y,
779                               allocation->width, allocation->height);
780       gdk_window_resize (icon_view->priv->bin_window,
781                          MAX (icon_view->priv->width, allocation->width),
782                          MAX (icon_view->priv->height, allocation->height));
783     }
784
785   icon_view->priv->hadjustment->page_size = allocation->width;
786   icon_view->priv->hadjustment->page_increment = allocation->width * 0.9;
787   icon_view->priv->hadjustment->step_increment = allocation->width * 0.1;
788   icon_view->priv->hadjustment->lower = 0;
789   icon_view->priv->hadjustment->upper = MAX (allocation->width, icon_view->priv->width);
790   gtk_adjustment_changed (icon_view->priv->hadjustment);
791
792   icon_view->priv->vadjustment->page_size = allocation->height;
793   icon_view->priv->vadjustment->page_increment = allocation->height * 0.9;
794   icon_view->priv->vadjustment->step_increment = allocation->width * 0.1;
795   icon_view->priv->vadjustment->lower = 0;
796   icon_view->priv->vadjustment->upper = MAX (allocation->height, icon_view->priv->height);
797   gtk_adjustment_changed (icon_view->priv->vadjustment);
798
799   gtk_icon_view_layout (icon_view);
800 }
801
802 static gboolean
803 gtk_icon_view_expose (GtkWidget *widget,
804                       GdkEventExpose *expose)
805 {
806   GtkIconView *icon_view;
807   GList *icons;
808
809   icon_view = GTK_ICON_VIEW (widget);
810
811   if (expose->window != icon_view->priv->bin_window)
812     return FALSE;
813
814   for (icons = icon_view->priv->items; icons; icons = icons->next) {
815     GtkIconViewItem *item = icons->data;
816     GdkRectangle item_rectangle;
817
818     item_rectangle.x = item->x;
819     item_rectangle.y = item->y;
820     item_rectangle.width = item->width;
821     item_rectangle.height = item->height;
822
823     if (gdk_region_rect_in (expose->region, &item_rectangle) == GDK_OVERLAP_RECTANGLE_OUT)
824       continue;
825
826     gtk_icon_view_paint_item (icon_view, item, &expose->area);
827   }
828
829   if (icon_view->priv->doing_rubberband)
830     {
831       GdkRectangle *rectangles;
832       gint n_rectangles;
833       
834       gdk_region_get_rectangles (expose->region,
835                                  &rectangles,
836                                  &n_rectangles);
837       
838       while (n_rectangles--)
839         gtk_icon_view_paint_rubberband (icon_view, &rectangles[n_rectangles]);
840
841       g_free (rectangles);
842     }
843
844   return TRUE;
845 }
846
847 static gboolean
848 scroll_timeout (gpointer data)
849 {
850   GtkIconView *icon_view;
851   gdouble value;
852   
853   icon_view = data;
854
855   value = MIN (icon_view->priv->vadjustment->value +
856                icon_view->priv->scroll_value_diff,
857                icon_view->priv->vadjustment->upper -
858                icon_view->priv->vadjustment->page_size);
859
860   gtk_adjustment_set_value (icon_view->priv->vadjustment,
861                             value);
862
863   gtk_icon_view_update_rubberband (icon_view);
864   
865   return TRUE;
866 }
867
868 static gboolean
869 gtk_icon_view_motion (GtkWidget      *widget,
870                       GdkEventMotion *event)
871 {
872   GtkIconView *icon_view;
873   gint abs_y;
874   
875   icon_view = GTK_ICON_VIEW (widget);
876 #ifdef DND_WORKS
877   gtk_icon_view_maybe_begin_dragging_items (icon_view, event);
878 #endif
879   if (icon_view->priv->doing_rubberband)
880     {
881       gtk_icon_view_update_rubberband (widget);
882       
883       abs_y = event->y - icon_view->priv->height *
884         (icon_view->priv->vadjustment->value /
885          (icon_view->priv->vadjustment->upper -
886           icon_view->priv->vadjustment->lower));
887
888       if (abs_y < 0 || abs_y > widget->allocation.height)
889         {
890           if (icon_view->priv->scroll_timeout_id == 0)
891             icon_view->priv->scroll_timeout_id = g_timeout_add (30, scroll_timeout, icon_view);
892
893           if (abs_y < 0)
894             icon_view->priv->scroll_value_diff = abs_y;
895           else
896             icon_view->priv->scroll_value_diff = abs_y - widget->allocation.height;
897
898           icon_view->priv->event_last_x = event->x;
899           icon_view->priv->event_last_y = event->y;
900         }
901       else if (icon_view->priv->scroll_timeout_id != 0)
902         {
903           g_source_remove (icon_view->priv->scroll_timeout_id);
904
905           icon_view->priv->scroll_timeout_id = 0;
906         }
907     }
908   
909   return TRUE;
910 }
911
912 static gboolean
913 gtk_icon_view_button_press (GtkWidget      *widget,
914                             GdkEventButton *event)
915 {
916   GtkIconView *icon_view;
917   GtkIconViewItem *item;
918   gboolean dirty = FALSE;
919
920   icon_view = GTK_ICON_VIEW (widget);
921
922   if (event->window != icon_view->priv->bin_window)
923     return FALSE;
924
925   if (!GTK_WIDGET_HAS_FOCUS (widget))
926     gtk_widget_grab_focus (widget);
927
928   if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
929     {
930
931       item = gtk_icon_view_get_item_at_pos (icon_view,
932                                             event->x, event->y);
933       
934       if (item != NULL)
935         {
936           gtk_icon_view_scroll_to_item (icon_view, item);
937           
938           if (icon_view->priv->selection_mode == GTK_SELECTION_NONE)
939             {
940               gtk_icon_view_set_cursor_item (icon_view, item);
941             }
942           else if (icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE &&
943                    (event->state & GDK_SHIFT_MASK))
944             {
945               gtk_icon_view_unselect_all_internal (icon_view);
946
947               gtk_icon_view_set_cursor_item (icon_view, item);
948               if (!icon_view->priv->anchor_item)
949                 icon_view->priv->anchor_item = item;
950               else 
951                 gtk_icon_view_select_all_between (icon_view,
952                                                   icon_view->priv->anchor_item,
953                                                   item);
954               dirty = TRUE;
955             }
956           else 
957             {
958               if (icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE &&
959                   (event->state & GDK_CONTROL_MASK))
960                 {
961                   item->selected = !item->selected;
962                   gtk_icon_view_queue_draw_item (icon_view, item);
963                   dirty = TRUE;
964                 }
965               else
966                 {
967                   if (!item->selected)
968                     {
969                       gtk_icon_view_unselect_all_internal (icon_view);
970                       
971                       item->selected = TRUE;
972                       gtk_icon_view_queue_draw_item (icon_view, item);
973                       dirty = TRUE;
974                     }
975                 }
976               gtk_icon_view_set_cursor_item (icon_view, item);
977               icon_view->priv->anchor_item = item;
978             }
979 #ifdef DND_WORKS
980           /* Save press to possibly begin a drag */
981           if (icon_view->priv->pressed_button < 0)
982             {
983               icon_view->priv->pressed_button = event->button;
984               icon_view->priv->press_start_x = event->x;
985               icon_view->priv->press_start_y = event->y;
986             }
987 #endif
988           if (!icon_view->priv->last_single_clicked)
989             icon_view->priv->last_single_clicked = item;
990         }
991       else
992         {
993           if (icon_view->priv->selection_mode != GTK_SELECTION_BROWSE &&
994               !(event->state & GDK_CONTROL_MASK))
995             {
996               dirty = gtk_icon_view_unselect_all_internal (icon_view);
997             }
998           
999           if (icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE)
1000             gtk_icon_view_start_rubberbanding (icon_view, event->x, event->y);
1001         }
1002
1003     }
1004
1005   if (event->button == 1 && event->type == GDK_2BUTTON_PRESS)
1006     {
1007       item = gtk_icon_view_get_item_at_pos (icon_view,
1008                                             event->x, event->y);
1009
1010       if (item && item == icon_view->priv->last_single_clicked)
1011         {
1012           GtkTreePath *path;
1013
1014           path = gtk_tree_path_new_from_indices (item->index, -1);
1015           gtk_icon_view_item_activated (icon_view, path);
1016           gtk_tree_path_free (path);
1017         }
1018
1019       icon_view->priv->last_single_clicked = NULL;
1020     }
1021   
1022   if (dirty)
1023     g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
1024
1025   return TRUE;
1026 }
1027
1028 static gboolean
1029 gtk_icon_view_button_release (GtkWidget      *widget,
1030                               GdkEventButton *event)
1031 {
1032   GtkIconView *icon_view;
1033
1034   icon_view = GTK_ICON_VIEW (widget);
1035   
1036 #ifdef DND_WORKS
1037   if (icon_view->priv->pressed_button == event->button)
1038     icon_view->priv->pressed_button = -1;
1039 #endif
1040   gtk_icon_view_stop_rubberbanding (icon_view);
1041
1042   if (icon_view->priv->scroll_timeout_id != 0)
1043     {
1044       g_source_remove (icon_view->priv->scroll_timeout_id);
1045       icon_view->priv->scroll_timeout_id = 0;
1046     }
1047
1048   return TRUE;
1049 }
1050
1051
1052 static void
1053 gtk_icon_view_update_rubberband (gpointer data)
1054 {
1055   GtkIconView *icon_view;
1056   gint x, y;
1057   GdkRectangle old_area;
1058   GdkRectangle new_area;
1059   GdkRectangle common;
1060   GdkRegion *invalid_region;
1061   
1062   icon_view = GTK_ICON_VIEW (data);
1063
1064   gdk_window_get_pointer (icon_view->priv->bin_window, &x, &y, NULL);
1065
1066   x = MAX (x, 0);
1067   y = MAX (y, 0);
1068
1069   old_area.x = MIN (icon_view->priv->rubberband_x1,
1070                     icon_view->priv->rubberband_x2);
1071   old_area.y = MIN (icon_view->priv->rubberband_y1,
1072                     icon_view->priv->rubberband_y2);
1073   old_area.width = ABS (icon_view->priv->rubberband_x2 -
1074                         icon_view->priv->rubberband_x1) + 1;
1075   old_area.height = ABS (icon_view->priv->rubberband_y2 -
1076                          icon_view->priv->rubberband_y1) + 1;
1077   
1078   new_area.x = MIN (icon_view->priv->rubberband_x1, x);
1079   new_area.y = MIN (icon_view->priv->rubberband_y1, y);
1080   new_area.width = ABS (x - icon_view->priv->rubberband_x1) + 1;
1081   new_area.height = ABS (y - icon_view->priv->rubberband_y1) + 1;
1082
1083   invalid_region = gdk_region_rectangle (&old_area);
1084   gdk_region_union_with_rect (invalid_region, &new_area);
1085
1086   gdk_rectangle_intersect (&old_area, &new_area, &common);
1087   if (common.width > 2 && common.height > 2)
1088     {
1089       GdkRegion *common_region;
1090
1091       /* make sure the border is invalidated */
1092       common.x += 1;
1093       common.y += 1;
1094       common.width -= 2;
1095       common.height -= 2;
1096       
1097       common_region = gdk_region_rectangle (&common);
1098
1099       gdk_region_subtract (invalid_region, common_region);
1100       gdk_region_destroy (common_region);
1101     }
1102   
1103   gdk_window_invalidate_region (icon_view->priv->bin_window, invalid_region, TRUE);
1104     
1105   gdk_region_destroy (invalid_region);
1106
1107   icon_view->priv->rubberband_x2 = x;
1108   icon_view->priv->rubberband_y2 = y;  
1109
1110   gtk_icon_view_update_rubberband_selection (icon_view);
1111 }
1112
1113 static void
1114 gtk_icon_view_start_rubberbanding (GtkIconView  *icon_view,
1115                                    gint          x,
1116                                    gint          y)
1117 {
1118   GList *items;
1119
1120   g_assert (!icon_view->priv->doing_rubberband);
1121
1122   for (items = icon_view->priv->items; items; items = items->next)
1123     {
1124       GtkIconViewItem *item = items->data;
1125
1126       item->selected_before_rubberbanding = item->selected;
1127     }
1128   
1129   icon_view->priv->rubberband_x1 = x;
1130   icon_view->priv->rubberband_y1 = y;
1131   icon_view->priv->rubberband_x2 = x;
1132   icon_view->priv->rubberband_y2 = y;
1133
1134   icon_view->priv->doing_rubberband = TRUE;
1135
1136   gtk_grab_add (GTK_WIDGET (icon_view));
1137 }
1138
1139 static void
1140 gtk_icon_view_stop_rubberbanding (GtkIconView *icon_view)
1141 {
1142   if (!icon_view->priv->doing_rubberband)
1143     return;
1144
1145   icon_view->priv->doing_rubberband = FALSE;
1146
1147   gtk_grab_remove (GTK_WIDGET (icon_view));
1148   
1149   gtk_widget_queue_draw (GTK_WIDGET (icon_view));
1150 }
1151
1152 static void
1153 gtk_icon_view_update_rubberband_selection (GtkIconView *icon_view)
1154 {
1155   GList *items;
1156   gint x, y, width, height;
1157   gboolean dirty = FALSE;
1158   
1159   x = MIN (icon_view->priv->rubberband_x1,
1160            icon_view->priv->rubberband_x2);
1161   y = MIN (icon_view->priv->rubberband_y1,
1162            icon_view->priv->rubberband_y2);
1163   width = ABS (icon_view->priv->rubberband_x1 - 
1164                icon_view->priv->rubberband_x2);
1165   height = ABS (icon_view->priv->rubberband_y1 - 
1166                 icon_view->priv->rubberband_y2);
1167   
1168   for (items = icon_view->priv->items; items; items = items->next)
1169     {
1170       GtkIconViewItem *item = items->data;
1171       gboolean is_in;
1172       gboolean selected;
1173       
1174       is_in = gtk_icon_view_item_hit_test (item, x, y, width, height);
1175
1176       selected = is_in ^ item->selected_before_rubberbanding;
1177
1178       if (item->selected != selected)
1179         {
1180           item->selected = selected;
1181           dirty = TRUE;
1182           gtk_icon_view_queue_draw_item (icon_view, item);
1183         }
1184     }
1185
1186   if (dirty)
1187     g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
1188 }
1189
1190 static gboolean
1191 gtk_icon_view_item_hit_test (GtkIconViewItem  *item,
1192                              gint              x,
1193                              gint              y,
1194                              gint              width,
1195                              gint              height)
1196 {
1197   /* First try the pixbuf */
1198   if (MIN (x + width, item->pixbuf_x + item->pixbuf_width) - MAX (x, item->pixbuf_x) > 0 &&
1199       MIN (y + height, item->pixbuf_y + item->pixbuf_height) - MAX (y, item->pixbuf_y) > 0)
1200     return TRUE;
1201
1202   /* Then try the text */
1203   if (MIN (x + width, item->layout_x + item->layout_width) - MAX (x, item->layout_x) > 0 &&
1204       MIN (y + height, item->layout_y + item->layout_height) - MAX (y, item->layout_y) > 0)
1205     return TRUE;
1206   
1207   return FALSE;
1208 }
1209
1210 #ifdef DND_WORKS
1211 static gboolean
1212 gtk_icon_view_maybe_begin_dragging_items (GtkIconView     *icon_view,
1213                                           GdkEventMotion  *event)
1214 {
1215   gboolean retval = FALSE;
1216   gint button;
1217   if (icon_view->priv->pressed_button < 0)
1218     return retval;
1219
1220   if (!gtk_drag_check_threshold (GTK_WIDGET (icon_view),
1221                                  icon_view->priv->press_start_x,
1222                                  icon_view->priv->press_start_y,
1223                                  event->x, event->y))
1224     return retval;
1225
1226   button = icon_view->priv->pressed_button;
1227   icon_view->priv->pressed_button = -1;
1228   
1229   {
1230     static GtkTargetEntry row_targets[] = {
1231       { "GTK_ICON_VIEW_ITEMS", GTK_TARGET_SAME_APP, 0 }
1232     };
1233     GtkTargetList *target_list;
1234     GdkDragContext *context;
1235     GtkIconViewItem *item;
1236     
1237     retval = TRUE;
1238     
1239     target_list = gtk_target_list_new (row_targets, G_N_ELEMENTS (row_targets));
1240
1241     context = gtk_drag_begin (GTK_WIDGET (icon_view),
1242                               target_list, GDK_ACTION_MOVE,
1243                               button,
1244                               (GdkEvent *)event);
1245
1246     item = gtk_icon_view_get_item_at_pos (icon_view,
1247                                           icon_view->priv->press_start_x,
1248                                           icon_view->priv->press_start_y);
1249     g_assert (item != NULL);
1250     gtk_drag_set_icon_pixbuf (context, gtk_icon_view_get_item_icon (icon_view, item),
1251                               event->x - item->x,
1252                               event->y - item->y);
1253   }
1254   
1255   return retval;
1256 }
1257 #endif
1258
1259 static gboolean
1260 gtk_icon_view_unselect_all_internal (GtkIconView  *icon_view)
1261 {
1262   gboolean dirty = FALSE;
1263   GList *items;
1264
1265   if (icon_view->priv->selection_mode == GTK_SELECTION_NONE ||
1266       icon_view->priv->selection_mode == GTK_SELECTION_BROWSE)
1267     return FALSE;
1268   
1269   for (items = icon_view->priv->items; items; items = items->next)
1270     {
1271       GtkIconViewItem *item = items->data;
1272
1273       if (item->selected)
1274         {
1275           item->selected = FALSE;
1276           dirty = TRUE;
1277           gtk_icon_view_queue_draw_item (icon_view, item);
1278         }
1279     }
1280
1281   return dirty;
1282 }
1283
1284
1285 /* GtkIconView signals */
1286 static void
1287 gtk_icon_view_set_adjustments (GtkIconView   *icon_view,
1288                                GtkAdjustment *hadj,
1289                                GtkAdjustment *vadj)
1290 {
1291   gboolean need_adjust = FALSE;
1292
1293   if (hadj)
1294     g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
1295   else
1296     hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
1297   if (vadj)
1298     g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
1299   else
1300     vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
1301
1302   if (icon_view->priv->hadjustment && (icon_view->priv->hadjustment != hadj))
1303     {
1304       g_signal_handlers_disconnect_matched (icon_view->priv->hadjustment, G_SIGNAL_MATCH_DATA,
1305                                            0, 0, NULL, NULL, icon_view);
1306       g_object_unref (icon_view->priv->hadjustment);
1307     }
1308
1309   if (icon_view->priv->vadjustment && (icon_view->priv->vadjustment != vadj))
1310     {
1311       g_signal_handlers_disconnect_matched (icon_view->priv->vadjustment, G_SIGNAL_MATCH_DATA,
1312                                             0, 0, NULL, NULL, icon_view);
1313       g_object_unref (icon_view->priv->vadjustment);
1314     }
1315
1316   if (icon_view->priv->hadjustment != hadj)
1317     {
1318       icon_view->priv->hadjustment = hadj;
1319       g_object_ref (icon_view->priv->hadjustment);
1320       gtk_object_sink (GTK_OBJECT (icon_view->priv->hadjustment));
1321
1322       g_signal_connect (icon_view->priv->hadjustment, "value_changed",
1323                         G_CALLBACK (gtk_icon_view_adjustment_changed),
1324                         icon_view);
1325       need_adjust = TRUE;
1326     }
1327
1328   if (icon_view->priv->vadjustment != vadj)
1329     {
1330       icon_view->priv->vadjustment = vadj;
1331       g_object_ref (icon_view->priv->vadjustment);
1332       gtk_object_sink (GTK_OBJECT (icon_view->priv->vadjustment));
1333
1334       g_signal_connect (icon_view->priv->vadjustment, "value_changed",
1335                         G_CALLBACK (gtk_icon_view_adjustment_changed),
1336                         icon_view);
1337       need_adjust = TRUE;
1338     }
1339
1340   if (need_adjust)
1341     gtk_icon_view_adjustment_changed (NULL, icon_view);
1342 }
1343
1344 static void
1345 gtk_icon_view_real_select_all (GtkIconView *icon_view)
1346 {
1347   gtk_icon_view_select_all (icon_view);
1348 }
1349
1350 static void
1351 gtk_icon_view_real_unselect_all (GtkIconView *icon_view)
1352 {
1353   if (icon_view->priv->selection_mode == GTK_SELECTION_BROWSE)
1354     return;
1355
1356   gtk_icon_view_unselect_all (icon_view);
1357 }
1358
1359 static void
1360 gtk_icon_view_real_select_cursor_item (GtkIconView *icon_view)
1361 {
1362   gtk_icon_view_unselect_all (icon_view);
1363   
1364   if (icon_view->priv->cursor_item != NULL)
1365     gtk_icon_view_select_item (icon_view, icon_view->priv->cursor_item);
1366 }
1367
1368 static gboolean
1369 gtk_icon_view_real_activate_cursor_item (GtkIconView *icon_view)
1370 {
1371   GtkTreePath *path;
1372   
1373   if (!icon_view->priv->cursor_item)
1374     return FALSE;
1375
1376   path = gtk_tree_path_new_from_indices (icon_view->priv->cursor_item->index, -1);
1377   
1378   gtk_icon_view_item_activated (icon_view, path);
1379
1380   gtk_tree_path_free (path);
1381
1382   return TRUE;
1383 }
1384
1385 static void
1386 gtk_icon_view_real_toggle_cursor_item (GtkIconView *icon_view)
1387 {
1388   if (!icon_view->priv->cursor_item)
1389     return;
1390
1391   switch (icon_view->priv->selection_mode)
1392     {
1393     case GTK_SELECTION_NONE:
1394       break;
1395     case GTK_SELECTION_BROWSE:
1396       gtk_icon_view_select_item (icon_view, icon_view->priv->cursor_item);
1397       break;
1398     case GTK_SELECTION_SINGLE:
1399       if (icon_view->priv->cursor_item->selected)
1400         gtk_icon_view_unselect_item (icon_view, icon_view->priv->cursor_item);
1401       else
1402         gtk_icon_view_select_item (icon_view, icon_view->priv->cursor_item);
1403       break;
1404     case GTK_SELECTION_MULTIPLE:
1405       icon_view->priv->cursor_item->selected = !icon_view->priv->cursor_item->selected;
1406       g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); 
1407       
1408       gtk_icon_view_queue_draw_item (icon_view, icon_view->priv->cursor_item);
1409       break;
1410     }
1411 }
1412
1413 /* Internal functions */
1414 static void
1415 gtk_icon_view_adjustment_changed (GtkAdjustment *adjustment,
1416                                   GtkIconView   *icon_view)
1417 {
1418   if (GTK_WIDGET_REALIZED (icon_view))
1419     {
1420       gdk_window_move (icon_view->priv->bin_window,
1421                        - icon_view->priv->hadjustment->value,
1422                        - icon_view->priv->vadjustment->value);
1423
1424       if (icon_view->priv->doing_rubberband)
1425         gtk_icon_view_update_rubberband (GTK_WIDGET (icon_view));
1426
1427       gdk_window_process_updates (icon_view->priv->bin_window, TRUE);
1428     }
1429 }
1430
1431 static GList *
1432 gtk_icon_view_layout_single_row (GtkIconView *icon_view, GList *first_item, gint *y, gint *maximum_width, gint row)
1433 {
1434   gint x, current_width, max_height, max_pixbuf_height;
1435   GList *items, *last_item;
1436   gint maximum_layout_width;
1437   gint col;
1438   gboolean rtl = gtk_widget_get_direction (GTK_WIDGET (icon_view)) == GTK_TEXT_DIR_RTL;
1439
1440   x = 0;
1441   col = 0;
1442   max_height = 0;
1443   max_pixbuf_height = 0;
1444   items = first_item;
1445   current_width = 0;
1446
1447   x += ICON_VIEW_LEFT_MARGIN;
1448   current_width += ICON_VIEW_LEFT_MARGIN + ICON_VIEW_RIGHT_MARGIN;
1449   items = first_item;
1450
1451   while (items)
1452     {
1453       GtkIconViewItem *item = items->data;
1454
1455       gtk_icon_view_calculate_item_size (icon_view, item);
1456
1457       current_width += MAX (item->width, MINIMUM_ICON_ITEM_WIDTH);
1458
1459       /* Don't add padding to the first or last icon */
1460       
1461       if (current_width > GTK_WIDGET (icon_view)->allocation.width &&
1462           items != first_item)
1463         break;
1464
1465       maximum_layout_width = MAX (item->pixbuf_width, MINIMUM_ICON_ITEM_WIDTH);
1466
1467       item->y = *y;
1468       item->x = rtl ? GTK_WIDGET (icon_view)->allocation.width - item->width - x : x;
1469       if (item->width < MINIMUM_ICON_ITEM_WIDTH) {
1470         if (rtl)
1471           item->x -= (MINIMUM_ICON_ITEM_WIDTH - item->width) / 2;
1472         else
1473           item->x += (MINIMUM_ICON_ITEM_WIDTH - item->width) / 2;
1474         x += (MINIMUM_ICON_ITEM_WIDTH - item->width);
1475       }
1476
1477       item->pixbuf_x = item->x + (item->width - item->pixbuf_width) / 2;
1478       item->layout_x = item->x + (item->width - item->layout_width) / 2;
1479
1480       x += item->width;
1481
1482       max_height = MAX (max_height, item->height);
1483       max_pixbuf_height = MAX (max_pixbuf_height, item->pixbuf_height);
1484       
1485       if (current_width > *maximum_width)
1486         *maximum_width = current_width;
1487
1488       item->row = row;
1489       item->col = col;
1490
1491       col++;
1492       items = items->next;
1493     }
1494
1495   last_item = items;
1496
1497   *y += max_height + ICON_VIEW_ICON_PADDING;
1498
1499   /* Now go through the row again and align the icons */
1500   for (items = first_item; items != last_item; items = items->next)
1501     {
1502       GtkIconViewItem *item = items->data;
1503
1504       item->pixbuf_y = item->y + (max_pixbuf_height - item->pixbuf_height);
1505       item->layout_y = item->pixbuf_y + item->pixbuf_height + ICON_TEXT_PADDING;
1506       /* Update the bounding box */
1507       item->y = item->pixbuf_y;
1508
1509       /* We may want to readjust the new y coordinate. */
1510       if (item->y + item->height > *y)
1511         *y = item->y + item->height;
1512
1513       if (rtl)
1514         item->col = col - 1 - item->col;
1515     }
1516   
1517   return last_item;
1518 }
1519
1520 static void
1521 gtk_icon_view_set_adjustment_upper (GtkAdjustment *adj,
1522                                     gdouble        upper)
1523 {
1524   if (upper != adj->upper)
1525     {
1526       gdouble min = MAX (0.0, upper - adj->page_size);
1527       gboolean value_changed = FALSE;
1528       
1529       adj->upper = upper;
1530
1531       if (adj->value > min)
1532         {
1533           adj->value = min;
1534           value_changed = TRUE;
1535         }
1536       
1537       gtk_adjustment_changed (adj);
1538       
1539       if (value_changed)
1540         gtk_adjustment_value_changed (adj);
1541     }
1542 }
1543
1544 static void
1545 gtk_icon_view_layout (GtkIconView *icon_view)
1546 {
1547   gint y = 0, maximum_width = 0;
1548   GList *icons;
1549   GtkWidget *widget;
1550   gint row;
1551
1552   if (!VALID_MODEL_AND_COLUMNS (icon_view))
1553     return;
1554
1555   widget = GTK_WIDGET (icon_view);
1556   icons = icon_view->priv->items;
1557
1558   y += ICON_VIEW_TOP_MARGIN;
1559   row = 0;
1560
1561   do
1562     {
1563       icons = gtk_icon_view_layout_single_row (icon_view, icons, &y, &maximum_width, row);
1564       row++;
1565     }
1566   while (icons != NULL);
1567
1568   if (maximum_width != icon_view->priv->width)
1569     {
1570       icon_view->priv->width = maximum_width;
1571     }
1572   y += ICON_VIEW_BOTTOM_MARGIN;
1573   
1574   if (y != icon_view->priv->height)
1575     {
1576       icon_view->priv->height = y;
1577     }
1578
1579   gtk_icon_view_set_adjustment_upper (icon_view->priv->hadjustment, icon_view->priv->width);
1580   gtk_icon_view_set_adjustment_upper (icon_view->priv->vadjustment, icon_view->priv->height);
1581
1582   if (GTK_WIDGET_REALIZED (icon_view))
1583     {
1584       gdk_window_resize (icon_view->priv->bin_window,
1585                          MAX (icon_view->priv->width, widget->allocation.width),
1586                          MAX (icon_view->priv->height, widget->allocation.height));
1587     }
1588
1589   if (icon_view->priv->layout_idle_id != 0)
1590     {
1591       g_source_remove (icon_view->priv->layout_idle_id);
1592       icon_view->priv->layout_idle_id = 0;
1593     }
1594
1595   gtk_widget_queue_draw (GTK_WIDGET (icon_view));
1596 }
1597
1598 /* Updates the pango layout and calculates the size */
1599 static void
1600 gtk_icon_view_calculate_item_size (GtkIconView *icon_view,
1601                                    GtkIconViewItem *item)
1602 {
1603   int layout_width, layout_height;
1604   int maximum_layout_width;
1605   GdkPixbuf *pixbuf;
1606   
1607   if (item->width != -1 && item->width != -1) 
1608     return;
1609
1610   if (icon_view->priv->pixbuf_column != -1)
1611     {
1612       pixbuf = gtk_icon_view_get_item_icon (icon_view, item);
1613       item->pixbuf_width = gdk_pixbuf_get_width (pixbuf);
1614       item->pixbuf_height = gdk_pixbuf_get_height (pixbuf);
1615       g_object_unref (pixbuf);
1616     }
1617   else
1618     {
1619       item->pixbuf_width = 0;
1620       item->pixbuf_height = 0;
1621     }
1622   
1623   maximum_layout_width = MAX (item->pixbuf_width, MINIMUM_ICON_ITEM_WIDTH);
1624
1625   if (icon_view->priv->markup_column != 1 ||
1626       icon_view->priv->text_column != -1)
1627     {
1628       gtk_icon_view_update_item_text (icon_view, item);
1629
1630       pango_layout_set_alignment (icon_view->priv->layout, PANGO_ALIGN_CENTER);
1631       pango_layout_set_width (icon_view->priv->layout, maximum_layout_width * PANGO_SCALE);
1632       
1633       pango_layout_get_pixel_size (icon_view->priv->layout, &layout_width, &layout_height);
1634       
1635       item->width = MAX ((layout_width + 2 * ICON_TEXT_PADDING), item->pixbuf_width);
1636       item->height = layout_height + 2 * ICON_TEXT_PADDING + item->pixbuf_height;
1637       item->layout_width = layout_width;
1638       item->layout_height = layout_height;
1639     }
1640   else
1641     {
1642       item->layout_width = 0;
1643       item->layout_height = 0;
1644     }
1645 }
1646
1647 static void
1648 gtk_icon_view_invalidate_sizes (GtkIconView *icon_view)
1649 {
1650   g_list_foreach (icon_view->priv->items,
1651                   (GFunc)gtk_icon_view_item_invalidate_size, NULL);
1652 }
1653
1654 static void
1655 gtk_icon_view_item_invalidate_size (GtkIconViewItem *item)
1656 {
1657   item->width = -1;
1658   item->height = -1;
1659 }
1660
1661 static GdkPixbuf *
1662 create_colorized_pixbuf (GdkPixbuf *src, GdkColor *new_color)
1663 {
1664         gint i, j;
1665         gint width, height, has_alpha, src_row_stride, dst_row_stride;
1666         gint red_value, green_value, blue_value;
1667         guchar *target_pixels;
1668         guchar *original_pixels;
1669         guchar *pixsrc;
1670         guchar *pixdest;
1671         GdkPixbuf *dest;
1672
1673         red_value = new_color->red / 255.0;
1674         green_value = new_color->green / 255.0;
1675         blue_value = new_color->blue / 255.0;
1676
1677         dest = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (src),
1678                                gdk_pixbuf_get_has_alpha (src),
1679                                gdk_pixbuf_get_bits_per_sample (src),
1680                                gdk_pixbuf_get_width (src),
1681                                gdk_pixbuf_get_height (src));
1682         
1683         has_alpha = gdk_pixbuf_get_has_alpha (src);
1684         width = gdk_pixbuf_get_width (src);
1685         height = gdk_pixbuf_get_height (src);
1686         src_row_stride = gdk_pixbuf_get_rowstride (src);
1687         dst_row_stride = gdk_pixbuf_get_rowstride (dest);
1688         target_pixels = gdk_pixbuf_get_pixels (dest);
1689         original_pixels = gdk_pixbuf_get_pixels (src);
1690
1691         for (i = 0; i < height; i++) {
1692                 pixdest = target_pixels + i*dst_row_stride;
1693                 pixsrc = original_pixels + i*src_row_stride;
1694                 for (j = 0; j < width; j++) {           
1695                         *pixdest++ = (*pixsrc++ * red_value) >> 8;
1696                         *pixdest++ = (*pixsrc++ * green_value) >> 8;
1697                         *pixdest++ = (*pixsrc++ * blue_value) >> 8;
1698                         if (has_alpha) {
1699                                 *pixdest++ = *pixsrc++;
1700                         }
1701                 }
1702         }
1703         return dest;
1704 }
1705
1706 static void
1707 gtk_icon_view_paint_item (GtkIconView     *icon_view,
1708                           GtkIconViewItem *item,
1709                           GdkRectangle    *area)
1710 {
1711   GdkPixbuf *pixbuf, *tmp;
1712   GtkStateType state;
1713   
1714   if (!VALID_MODEL_AND_COLUMNS (icon_view))
1715     return;
1716   
1717   if (GTK_WIDGET_HAS_FOCUS (icon_view))
1718     state = GTK_STATE_SELECTED;
1719   else
1720     state = GTK_STATE_ACTIVE;
1721
1722   if (icon_view->priv->pixbuf_column != -1)
1723     {
1724       tmp = gtk_icon_view_get_item_icon (icon_view, item);
1725       if (item->selected)
1726         {
1727           pixbuf = create_colorized_pixbuf (tmp,
1728                                             &GTK_WIDGET (icon_view)->style->base[state]);
1729           g_object_unref (tmp);
1730         }
1731       else
1732         pixbuf = tmp;
1733       
1734       gdk_draw_pixbuf (icon_view->priv->bin_window, NULL, pixbuf,
1735                        0, 0,
1736                        item->pixbuf_x, item->pixbuf_y,
1737                        item->pixbuf_width, item->pixbuf_height,
1738                        GDK_RGB_DITHER_NORMAL,
1739                        item->pixbuf_width, item->pixbuf_height);
1740       g_object_unref (pixbuf);
1741     }
1742
1743   if (icon_view->priv->text_column != -1)
1744     {
1745       if (item->selected)
1746         {
1747           gdk_draw_rectangle (icon_view->priv->bin_window,
1748                               GTK_WIDGET (icon_view)->style->base_gc[state],
1749                               TRUE,
1750                               item->layout_x - ICON_TEXT_PADDING,
1751                               item->layout_y - ICON_TEXT_PADDING,
1752                               item->layout_width + 2 * ICON_TEXT_PADDING,
1753                               item->layout_height + 2 * ICON_TEXT_PADDING);
1754         }
1755
1756       gtk_icon_view_update_item_text (icon_view, item);
1757       gtk_paint_layout (GTK_WIDGET (icon_view)->style,
1758                         icon_view->priv->bin_window,
1759                         item->selected ? state : GTK_STATE_NORMAL,
1760                         TRUE, area, GTK_WIDGET (icon_view), "icon_view",
1761                         item->layout_x - ((item->width - item->layout_width) / 2) - (MAX (item->pixbuf_width, MINIMUM_ICON_ITEM_WIDTH) - item->width) / 2,
1762                         item->layout_y,
1763                         icon_view->priv->layout);
1764
1765       if (GTK_WIDGET_HAS_FOCUS (icon_view) &&
1766           item == icon_view->priv->cursor_item)
1767         gtk_paint_focus (GTK_WIDGET (icon_view)->style,
1768                          icon_view->priv->bin_window,
1769                          item->selected ? GTK_STATE_SELECTED : GTK_STATE_NORMAL,
1770                          area,
1771                          GTK_WIDGET (icon_view),
1772                          "icon_view",
1773                          item->layout_x - ICON_TEXT_PADDING,
1774                          item->layout_y - ICON_TEXT_PADDING,
1775                          item->layout_width + 2 * ICON_TEXT_PADDING,
1776                          item->layout_height + 2 * ICON_TEXT_PADDING);
1777     }
1778 }
1779
1780 static guint32
1781 gtk_gdk_color_to_rgb (const GdkColor *color)
1782 {
1783   guint32 result;
1784   result = (0xff0000 | (color->red & 0xff00));
1785   result <<= 8;
1786   result |= ((color->green & 0xff00) | (color->blue >> 8));
1787   return result;
1788 }
1789
1790 static void
1791 gtk_icon_view_paint_rubberband (GtkIconView     *icon_view,
1792                                 GdkRectangle    *area)
1793 {
1794   GdkRectangle rect;
1795   GdkPixbuf *pixbuf;
1796   GdkGC *gc;
1797   GdkRectangle rubber_rect;
1798   GdkColor *fill_color_gdk;
1799   guint fill_color;
1800   guchar fill_color_alpha;
1801
1802   rubber_rect.x = MIN (icon_view->priv->rubberband_x1, icon_view->priv->rubberband_x2);
1803   rubber_rect.y = MIN (icon_view->priv->rubberband_y1, icon_view->priv->rubberband_y2);
1804   rubber_rect.width = ABS (icon_view->priv->rubberband_x1 - icon_view->priv->rubberband_x2) + 1;
1805   rubber_rect.height = ABS (icon_view->priv->rubberband_y1 - icon_view->priv->rubberband_y2) + 1;
1806
1807   if (!gdk_rectangle_intersect (&rubber_rect, area, &rect))
1808     return;
1809
1810   gtk_widget_style_get (GTK_WIDGET (icon_view),
1811                         "selection_box_color", &fill_color_gdk,
1812                         "selection_box_alpha", &fill_color_alpha,
1813                         NULL);
1814
1815   if (!fill_color_gdk) {
1816     fill_color_gdk = gdk_color_copy (&GTK_WIDGET (icon_view)->style->base[GTK_STATE_SELECTED]);
1817   }
1818
1819   fill_color = gtk_gdk_color_to_rgb (fill_color_gdk) << 8 | fill_color_alpha;
1820
1821   pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, rect.width, rect.height);
1822   gdk_pixbuf_fill (pixbuf, fill_color);
1823
1824   gdk_draw_pixbuf (icon_view->priv->bin_window, NULL, pixbuf,
1825                    0, 0, 
1826                    rect.x,rect.y,
1827                    rect.width, rect.height,
1828                    GDK_RGB_DITHER_NONE,
1829                    0, 0);
1830   g_object_unref (pixbuf);
1831   gc = gdk_gc_new (icon_view->priv->bin_window);
1832   gdk_gc_set_rgb_fg_color (gc, fill_color_gdk);
1833   gdk_gc_set_clip_rectangle (gc, &rect);
1834   gdk_draw_rectangle (icon_view->priv->bin_window,
1835                       gc, FALSE,
1836                       rubber_rect.x, rubber_rect.y,
1837                       rubber_rect.width - 1, rubber_rect.height - 1);
1838   gdk_color_free (fill_color_gdk);
1839   g_object_unref (gc);
1840 }
1841
1842 static void
1843 gtk_icon_view_queue_draw_item (GtkIconView     *icon_view,
1844                                GtkIconViewItem *item)
1845 {
1846   GdkRectangle rect;
1847
1848   rect.x = item->x;
1849   rect.y = item->y;
1850   rect.width = item->width;
1851   rect.height = item->height;
1852
1853   if (icon_view->priv->bin_window)
1854     gdk_window_invalidate_rect (icon_view->priv->bin_window, &rect, TRUE);
1855 }
1856
1857 static gboolean
1858 layout_callback (gpointer user_data)
1859 {
1860   GtkIconView *icon_view;
1861
1862   icon_view = GTK_ICON_VIEW (user_data);
1863   
1864   icon_view->priv->layout_idle_id = 0;
1865
1866   gtk_icon_view_layout (icon_view);
1867   
1868   return FALSE;
1869 }
1870
1871 static void
1872 gtk_icon_view_queue_layout (GtkIconView *icon_view)
1873 {
1874   if (icon_view->priv->layout_idle_id != 0)
1875     return;
1876
1877   icon_view->priv->layout_idle_id = g_idle_add (layout_callback, icon_view);
1878 }
1879
1880 static void
1881 gtk_icon_view_set_cursor_item (GtkIconView     *icon_view,
1882                                GtkIconViewItem *item)
1883 {
1884   if (icon_view->priv->cursor_item == item)
1885     return;
1886
1887   if (icon_view->priv->cursor_item != NULL)
1888     gtk_icon_view_queue_draw_item (icon_view, icon_view->priv->cursor_item);
1889   
1890   icon_view->priv->cursor_item = item;
1891   gtk_icon_view_queue_draw_item (icon_view, item);
1892 }
1893
1894
1895 static GtkIconViewItem *
1896 gtk_icon_view_item_new (void)
1897 {
1898   GtkIconViewItem *item;
1899
1900   item = g_new0 (GtkIconViewItem, 1);
1901
1902   item->width = -1;
1903   item->height = -1;
1904   
1905   return item;
1906 }
1907
1908 static void
1909 gtk_icon_view_item_free (GtkIconViewItem *item)
1910 {
1911   g_return_if_fail (item != NULL);
1912
1913   g_free (item);
1914 }
1915
1916 static void
1917 gtk_icon_view_update_item_text (GtkIconView     *icon_view,
1918                                 GtkIconViewItem *item)
1919 {
1920   gboolean iters_persist;
1921   GtkTreeIter iter;
1922   GtkTreePath *path;
1923   gchar *text;
1924   
1925   iters_persist = gtk_tree_model_get_flags (icon_view->priv->model) & GTK_TREE_MODEL_ITERS_PERSIST;
1926   
1927   if (!iters_persist)
1928     {
1929       path = gtk_tree_path_new_from_indices (item->index, -1);
1930       gtk_tree_model_get_iter (icon_view->priv->model, &iter, path);
1931       gtk_tree_path_free (path);
1932     }
1933   else
1934     iter = item->iter;
1935
1936   if (icon_view->priv->markup_column != -1)
1937     {
1938       gtk_tree_model_get (icon_view->priv->model, &iter,
1939                           icon_view->priv->markup_column, &text,
1940                           -1);
1941       pango_layout_set_markup (icon_view->priv->layout, text, -1);
1942     }
1943   else
1944     {
1945       gtk_tree_model_get (icon_view->priv->model, &iter,
1946                           icon_view->priv->text_column, &text,
1947                           -1);
1948       pango_layout_set_text (icon_view->priv->layout, text, -1);
1949     }
1950
1951   g_free (text);        
1952 }
1953
1954 static GdkPixbuf *
1955 gtk_icon_view_get_item_icon (GtkIconView      *icon_view,
1956                              GtkIconViewItem  *item)
1957 {
1958   gboolean iters_persist;
1959   GtkTreeIter iter;
1960   GtkTreePath *path;
1961   GdkPixbuf *pixbuf;
1962   
1963   g_return_val_if_fail (item != NULL, NULL);
1964
1965   iters_persist = gtk_tree_model_get_flags (icon_view->priv->model) & GTK_TREE_MODEL_ITERS_PERSIST;
1966   
1967   if (!iters_persist)
1968     {
1969       path = gtk_tree_path_new_from_indices (item->index, -1);
1970       gtk_tree_model_get_iter (icon_view->priv->model, &iter, path);
1971       gtk_tree_path_free (path);
1972     }
1973   else
1974     iter = item->iter;
1975   
1976   gtk_tree_model_get (icon_view->priv->model, &iter,
1977                       icon_view->priv->pixbuf_column, &pixbuf,
1978                       -1);
1979
1980   return pixbuf;
1981 }
1982
1983
1984 static GtkIconViewItem *
1985 gtk_icon_view_get_item_at_pos (GtkIconView *icon_view,
1986                                gint         x,
1987                                gint         y)
1988 {
1989   GList *items;
1990   
1991   for (items = icon_view->priv->items; items; items = items->next)
1992     {
1993       GtkIconViewItem *item = items->data;
1994       
1995       if (x > item->x && x < item->x + item->width &&
1996           y > item->y && y < item->y + item->height)
1997         {
1998           gint layout_x = item->x + (item->width - item->layout_width) / 2;
1999           /* Check if the mouse is inside the icon or the label */
2000           if ((x > item->pixbuf_x && x < item->pixbuf_x + item->pixbuf_width &&
2001                y > item->pixbuf_y && y < item->pixbuf_y + item->pixbuf_height) ||
2002               (x > layout_x - ICON_TEXT_PADDING &&
2003                x < layout_x + item->layout_width + ICON_TEXT_PADDING * 2 &&
2004                y > item->layout_y - ICON_TEXT_PADDING
2005                && y < item->layout_y + item->layout_height + ICON_TEXT_PADDING * 2))
2006             return item;
2007         }
2008     }
2009
2010   return NULL;
2011 }
2012
2013 static void
2014 gtk_icon_view_select_item (GtkIconView      *icon_view,
2015                            GtkIconViewItem  *item)
2016 {
2017   g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
2018   g_return_if_fail (item != NULL);
2019
2020   if (item->selected)
2021     return;
2022   
2023   if (icon_view->priv->selection_mode == GTK_SELECTION_NONE)
2024     return;
2025   else if (icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
2026     gtk_icon_view_unselect_all_internal (icon_view);
2027
2028   item->selected = TRUE;
2029
2030   gtk_icon_view_queue_draw_item (icon_view, item);
2031
2032   g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
2033 }
2034
2035
2036 static void
2037 gtk_icon_view_unselect_item (GtkIconView      *icon_view,
2038                              GtkIconViewItem  *item)
2039 {
2040   g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
2041   g_return_if_fail (item != NULL);
2042
2043   if (!item->selected)
2044     return;
2045   
2046   if (icon_view->priv->selection_mode == GTK_SELECTION_NONE ||
2047       icon_view->priv->selection_mode == GTK_SELECTION_BROWSE)
2048     return;
2049   
2050   item->selected = FALSE;
2051
2052   g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
2053
2054   gtk_icon_view_queue_draw_item (icon_view, item);
2055 }
2056
2057 static void
2058 verify_items (GtkIconView *icon_view)
2059 {
2060   GList *items;
2061   int i = 0;
2062
2063   for (items = icon_view->priv->items; items; items = items->next)
2064     {
2065       GtkIconViewItem *item = items->data;
2066
2067       if (item->index != i)
2068         g_error ("List item does not match its index: item index %d and list index %d\n", item->index, i);
2069
2070       i++;
2071     }
2072 }
2073
2074 static void
2075 gtk_icon_view_row_changed (GtkTreeModel *model,
2076                            GtkTreePath  *path,
2077                            GtkTreeIter  *iter,
2078                            gpointer      data)
2079 {
2080   GtkIconViewItem *item;
2081   gint index;
2082   GtkIconView *icon_view;
2083
2084   icon_view = GTK_ICON_VIEW (data);
2085   
2086   index = gtk_tree_path_get_indices(path)[0];
2087   item = g_list_nth (icon_view->priv->items, index)->data;
2088
2089   gtk_icon_view_item_invalidate_size (item);
2090   gtk_icon_view_queue_layout (icon_view);
2091
2092   verify_items (icon_view);
2093 }
2094
2095 static void
2096 gtk_icon_view_row_inserted (GtkTreeModel *model,
2097                             GtkTreePath  *path,
2098                             GtkTreeIter  *iter,
2099                             gpointer      data)
2100 {
2101   gint length, index;
2102   GtkIconViewItem *item;
2103   gboolean iters_persist;
2104   GtkIconView *icon_view;
2105   GList *list;
2106   
2107   icon_view = GTK_ICON_VIEW (data);
2108   iters_persist = gtk_tree_model_get_flags (icon_view->priv->model) & GTK_TREE_MODEL_ITERS_PERSIST;
2109   
2110   length = gtk_tree_model_iter_n_children (model, NULL);
2111   index = gtk_tree_path_get_indices(path)[0];
2112
2113   item = gtk_icon_view_item_new ();
2114
2115   if (iters_persist)
2116     item->iter = *iter;
2117
2118   item->index = index;
2119
2120   /* FIXME: We can be more efficient here,
2121      we can store a tail pointer and use that when
2122      appending (which is a rather common operation)
2123   */
2124   icon_view->priv->items = g_list_insert (icon_view->priv->items,
2125                                          item, index);
2126   
2127   list = g_list_nth (icon_view->priv->items, index + 1);
2128   for (; list; list = list->next)
2129     {
2130       item = list->data;
2131
2132       item->index++;
2133     }
2134     
2135   verify_items (icon_view);
2136 }
2137
2138 static void
2139 gtk_icon_view_row_deleted (GtkTreeModel *model,
2140                            GtkTreePath  *path,
2141                            gpointer      data)
2142 {
2143   gint index;
2144   GtkIconView *icon_view;
2145   GtkIconViewItem *item;
2146   GList *list, *next;
2147   gboolean emit = FALSE;
2148   
2149   icon_view = GTK_ICON_VIEW (data);
2150
2151   index = gtk_tree_path_get_indices(path)[0];
2152
2153   list = g_list_nth (icon_view->priv->items, index);
2154   item = list->data;
2155
2156   if (item == icon_view->priv->anchor_item)
2157     icon_view->priv->anchor_item = NULL;
2158
2159   if (item == icon_view->priv->cursor_item)
2160     icon_view->priv->cursor_item = NULL;
2161
2162   if (item->selected)
2163     emit = TRUE;
2164   
2165   gtk_icon_view_item_free (item);
2166
2167   for (next = list->next; next; next = next->next)
2168     {
2169       item = next->data;
2170
2171       item->index--;
2172     }
2173   
2174   icon_view->priv->items = g_list_delete_link (icon_view->priv->items, list);
2175
2176   gtk_icon_view_queue_layout (icon_view);
2177
2178   verify_items (icon_view);  
2179   
2180   if (emit)
2181     g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
2182 }
2183
2184 static void
2185 gtk_icon_view_rows_reordered (GtkTreeModel *model,
2186                               GtkTreePath  *parent,
2187                               GtkTreeIter  *iter,
2188                               gint         *new_order,
2189                               gpointer      data)
2190 {
2191   int i;
2192   int length;
2193   GtkIconView *icon_view;
2194   GList *items = NULL, *list;
2195   gint *inverted_order;
2196   GtkIconViewItem **item_array;
2197   
2198   icon_view = GTK_ICON_VIEW (data);
2199
2200   length = gtk_tree_model_iter_n_children (model, NULL);
2201   inverted_order = g_new (gint, length);
2202
2203   /* Invert the array */
2204   for (i = 0; i < length; i++)
2205     inverted_order[new_order[i]] = i;
2206
2207   item_array = g_new (GtkIconViewItem *, length);
2208   for (i = 0, list = icon_view->priv->items; list != NULL; list = list->next, i++)
2209     item_array[inverted_order[i]] = list->data;
2210
2211   g_free (inverted_order);
2212   for (i = 0; i < length; i++)
2213     {
2214       item_array[i]->index = i;
2215       items = g_list_prepend (items, item_array[i]);
2216     }
2217   
2218   g_free (item_array);
2219   g_list_free (icon_view->priv->items);
2220   icon_view->priv->items = g_list_reverse (items);
2221
2222   verify_items (icon_view);  
2223 }
2224
2225 static void
2226 gtk_icon_view_build_items (GtkIconView *icon_view)
2227 {
2228   GtkTreeIter iter;
2229   int i;
2230   gboolean iters_persist;
2231   GList *items = NULL;
2232
2233   iters_persist = gtk_tree_model_get_flags (icon_view->priv->model) & GTK_TREE_MODEL_ITERS_PERSIST;
2234   
2235   if (!gtk_tree_model_get_iter_first (icon_view->priv->model,
2236                                       &iter))
2237     return;
2238
2239   i = 0;
2240   
2241   do
2242     {
2243       GtkIconViewItem *item = gtk_icon_view_item_new ();
2244
2245       if (iters_persist)
2246         item->iter = iter;
2247
2248       item->index = i;
2249       
2250       i++;
2251
2252       items = g_list_prepend (items, item);
2253       
2254     } while (gtk_tree_model_iter_next (icon_view->priv->model, &iter));
2255
2256   icon_view->priv->items = g_list_reverse (items);
2257 }
2258
2259 static void
2260 gtk_icon_view_add_move_binding (GtkBindingSet  *binding_set,
2261                                 guint           keyval,
2262                                 guint           modmask,
2263                                 GtkMovementStep step,
2264                                 gint            count)
2265 {
2266   
2267   gtk_binding_entry_add_signal (binding_set, keyval, modmask,
2268                                 "move_cursor", 2,
2269                                 G_TYPE_ENUM, step,
2270                                 G_TYPE_INT, count);
2271
2272   gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK,
2273                                 "move_cursor", 2,
2274                                 G_TYPE_ENUM, step,
2275                                 G_TYPE_INT, count);
2276
2277   if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2278    return;
2279
2280   gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
2281                                 "move_cursor", 2,
2282                                 G_TYPE_ENUM, step,
2283                                 G_TYPE_INT, count);
2284
2285   gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK,
2286                                 "move_cursor", 2,
2287                                 G_TYPE_ENUM, step,
2288                                 G_TYPE_INT, count);
2289 }
2290
2291 static gboolean
2292 gtk_icon_view_real_move_cursor (GtkIconView     *icon_view,
2293                                 GtkMovementStep  step,
2294                                 gint             count)
2295 {
2296   GdkModifierType state;
2297
2298   g_return_val_if_fail (GTK_ICON_VIEW (icon_view), FALSE);
2299   g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS ||
2300                         step == GTK_MOVEMENT_VISUAL_POSITIONS ||
2301                         step == GTK_MOVEMENT_DISPLAY_LINES ||
2302                         step == GTK_MOVEMENT_PAGES ||
2303                         step == GTK_MOVEMENT_BUFFER_ENDS, FALSE);
2304
2305   if (!GTK_WIDGET_HAS_FOCUS (GTK_WIDGET (icon_view)))
2306     return FALSE;
2307
2308   gtk_widget_grab_focus (GTK_WIDGET (icon_view));
2309
2310   if (gtk_get_current_event_state (&state))
2311     {
2312       if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2313         icon_view->priv->ctrl_pressed = TRUE;
2314       if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2315         icon_view->priv->shift_pressed = TRUE;
2316     }
2317   /* else we assume not pressed */
2318
2319   switch (step)
2320     {
2321     case GTK_MOVEMENT_LOGICAL_POSITIONS:
2322     case GTK_MOVEMENT_VISUAL_POSITIONS:
2323       gtk_icon_view_move_cursor_left_right (icon_view, count);
2324       break;
2325     case GTK_MOVEMENT_DISPLAY_LINES:
2326       gtk_icon_view_move_cursor_up_down (icon_view, count);
2327       break;
2328     case GTK_MOVEMENT_PAGES:
2329       gtk_icon_view_move_cursor_page_up_down (icon_view, count);
2330       break;
2331     case GTK_MOVEMENT_BUFFER_ENDS:
2332       gtk_icon_view_move_cursor_start_end (icon_view, count);
2333       break;
2334     default:
2335       g_assert_not_reached ();
2336     }
2337
2338   icon_view->priv->ctrl_pressed = FALSE;
2339   icon_view->priv->shift_pressed = FALSE;
2340
2341   return TRUE;
2342 }
2343
2344 static GtkIconViewItem *
2345 find_item (GtkIconView     *icon_view,
2346            GtkIconViewItem *current,
2347            gint             row_ofs,
2348            gint             col_ofs)
2349 {
2350   gint row, col;
2351   GList *items;
2352   GtkIconViewItem *item;
2353
2354   /* FIXME: this could be more efficient 
2355    */
2356   row = current->row + row_ofs;
2357   col = current->col + col_ofs;
2358
2359   for (items = icon_view->priv->items; items; items = items->next)
2360     {
2361       item = items->data;
2362       if (item->row == row && item->col == col)
2363         return item;
2364     }
2365   
2366   return NULL;
2367 }
2368
2369
2370 static GtkIconViewItem *
2371 find_item_page_up_down (GtkIconView     *icon_view,
2372                         GtkIconViewItem *current,
2373                         gint             count)
2374 {
2375   GList *item, *next;
2376   gint y, col;
2377   
2378   col = current->col;
2379   y = current->y + count * icon_view->priv->vadjustment->page_size;
2380
2381   item = g_list_find (icon_view->priv->items, current);
2382   if (count > 0)
2383     {
2384       while (item)
2385         {
2386           for (next = item->next; next; next = next->next)
2387             {
2388               if (((GtkIconViewItem *)next->data)->col == col)
2389                 break;
2390             }
2391           if (!next || ((GtkIconViewItem *)next->data)->y > y)
2392             break;
2393
2394           item = next;
2395         }
2396     }
2397   else 
2398     {
2399       while (item)
2400         {
2401           for (next = item->prev; next; next = next->prev)
2402             {
2403               if (((GtkIconViewItem *)next->data)->col == col)
2404                 break;
2405             }
2406           if (!next || ((GtkIconViewItem *)next->data)->y < y)
2407             break;
2408
2409           item = next;
2410         }
2411     }
2412
2413   if (item)
2414     return item->data;
2415
2416   return NULL;
2417 }
2418
2419 static gboolean
2420 gtk_icon_view_select_all_between (GtkIconView     *icon_view,
2421                                   GtkIconViewItem *anchor,
2422                                   GtkIconViewItem *cursor)
2423 {
2424   GList *items;
2425   GtkIconViewItem *item;
2426   gint row1, row2, col1, col2;
2427   gboolean dirty = FALSE;
2428   
2429   if (anchor->row < cursor->row)
2430     {
2431       row1 = anchor->row;
2432       row2 = cursor->row;
2433     }
2434   else
2435     {
2436       row1 = cursor->row;
2437       row2 = anchor->row;
2438     }
2439
2440   if (anchor->col < cursor->col)
2441     {
2442       col1 = anchor->col;
2443       col2 = cursor->col;
2444     }
2445   else
2446     {
2447       col1 = cursor->col;
2448       col2 = anchor->col;
2449     }
2450
2451   for (items = icon_view->priv->items; items; items = items->next)
2452     {
2453       item = items->data;
2454
2455       if (row1 <= item->row && item->row <= row2 &&
2456           col1 <= item->col && item->col <= col2)
2457         {
2458           if (!item->selected)
2459             dirty = TRUE;
2460
2461           item->selected = TRUE;
2462           
2463           gtk_icon_view_queue_draw_item (icon_view, item);
2464         }
2465     }
2466
2467   return dirty;
2468 }
2469
2470 static void 
2471 gtk_icon_view_move_cursor_up_down (GtkIconView *icon_view,
2472                                    gint         count)
2473 {
2474   GtkIconViewItem *item;
2475   gboolean dirty = FALSE;
2476   
2477   if (!GTK_WIDGET_HAS_FOCUS (icon_view))
2478     return;
2479   
2480   if (!icon_view->priv->cursor_item)
2481     {
2482       GList *list;
2483
2484       if (count > 0)
2485         list = icon_view->priv->items;
2486       else
2487         list = g_list_last (icon_view->priv->items);
2488
2489       item = list->data;
2490     }
2491   else
2492     item = find_item (icon_view, 
2493                       icon_view->priv->cursor_item,
2494                       count, 0);
2495
2496   if (!item)
2497     return;
2498
2499   if (icon_view->priv->ctrl_pressed ||
2500       !icon_view->priv->shift_pressed ||
2501       !icon_view->priv->anchor_item ||
2502       icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
2503     icon_view->priv->anchor_item = item;
2504
2505   gtk_icon_view_set_cursor_item (icon_view, item);
2506
2507   if (!icon_view->priv->ctrl_pressed &&
2508       icon_view->priv->selection_mode != GTK_SELECTION_NONE)
2509     {
2510       gtk_icon_view_unselect_all_internal (icon_view);
2511       dirty = gtk_icon_view_select_all_between (icon_view, 
2512                                                 icon_view->priv->anchor_item,
2513                                                 item);
2514     }
2515
2516   gtk_icon_view_scroll_to_item (icon_view, item);
2517
2518   if (dirty)
2519     g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
2520 }
2521
2522 static void 
2523 gtk_icon_view_move_cursor_page_up_down (GtkIconView *icon_view,
2524                                         gint         count)
2525 {
2526   GtkIconViewItem *item;
2527   gboolean dirty = FALSE;
2528   
2529   if (!GTK_WIDGET_HAS_FOCUS (icon_view))
2530     return;
2531   
2532   if (!icon_view->priv->cursor_item)
2533     {
2534       GList *list;
2535
2536       if (count > 0)
2537         list = icon_view->priv->items;
2538       else
2539         list = g_list_last (icon_view->priv->items);
2540
2541       item = list->data;
2542     }
2543   else
2544     item = find_item_page_up_down (icon_view, 
2545                                    icon_view->priv->cursor_item,
2546                                    count);
2547
2548   if (!item)
2549     return;
2550
2551   if (icon_view->priv->ctrl_pressed ||
2552       !icon_view->priv->shift_pressed ||
2553       !icon_view->priv->anchor_item ||
2554       icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
2555     icon_view->priv->anchor_item = item;
2556
2557   gtk_icon_view_set_cursor_item (icon_view, item);
2558
2559   if (!icon_view->priv->ctrl_pressed &&
2560       icon_view->priv->selection_mode != GTK_SELECTION_NONE)
2561     {
2562       gtk_icon_view_unselect_all_internal (icon_view);
2563       dirty = gtk_icon_view_select_all_between (icon_view, 
2564                                                 icon_view->priv->anchor_item,
2565                                                 item);
2566     }
2567
2568   gtk_icon_view_scroll_to_item (icon_view, item);
2569
2570   if (dirty)
2571     g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);  
2572 }
2573
2574 static void 
2575 gtk_icon_view_move_cursor_left_right (GtkIconView *icon_view,
2576                                       gint         count)
2577 {
2578   GtkIconViewItem *item;
2579   gboolean dirty = FALSE;
2580   
2581   if (!GTK_WIDGET_HAS_FOCUS (icon_view))
2582     return;
2583   
2584   if (!icon_view->priv->cursor_item)
2585     {
2586       GList *list;
2587
2588       if (count > 0)
2589         list = icon_view->priv->items;
2590       else
2591         list = g_list_last (icon_view->priv->items);
2592
2593       item = list->data;
2594     }
2595   else
2596     item = find_item (icon_view, 
2597                       icon_view->priv->cursor_item,
2598                       0, count);
2599
2600   if (!item)
2601     return;
2602
2603   if (icon_view->priv->ctrl_pressed ||
2604       !icon_view->priv->shift_pressed ||
2605       !icon_view->priv->anchor_item ||
2606       icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
2607     icon_view->priv->anchor_item = item;
2608
2609   gtk_icon_view_set_cursor_item (icon_view, item);
2610
2611   if (!icon_view->priv->ctrl_pressed &&
2612       icon_view->priv->selection_mode != GTK_SELECTION_NONE)
2613     {
2614       gtk_icon_view_unselect_all_internal (icon_view);
2615       dirty = gtk_icon_view_select_all_between (icon_view, 
2616                                                 icon_view->priv->anchor_item,
2617                                                 item);
2618     }
2619
2620   gtk_icon_view_scroll_to_item (icon_view, item);
2621
2622   if (dirty)
2623     g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
2624 }
2625
2626 static void 
2627 gtk_icon_view_move_cursor_start_end (GtkIconView *icon_view,
2628                                      gint         count)
2629 {
2630   GtkIconViewItem *item;
2631   GList *list;
2632   gboolean dirty = FALSE;
2633   
2634   if (!GTK_WIDGET_HAS_FOCUS (icon_view))
2635     return;
2636   
2637   if (count < 0)
2638     list = icon_view->priv->items;
2639   else
2640     list = g_list_last (icon_view->priv->items);
2641   
2642   item = list->data;
2643
2644   if (!item)
2645     return;
2646
2647   if (icon_view->priv->ctrl_pressed ||
2648       !icon_view->priv->shift_pressed ||
2649       !icon_view->priv->anchor_item ||
2650       icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
2651     icon_view->priv->anchor_item = item;
2652
2653   gtk_icon_view_set_cursor_item (icon_view, item);
2654
2655   if (!icon_view->priv->ctrl_pressed &&
2656       icon_view->priv->selection_mode != GTK_SELECTION_NONE)
2657     {
2658       gtk_icon_view_unselect_all (icon_view);
2659       dirty = gtk_icon_view_select_all_between (icon_view, 
2660                                                 icon_view->priv->anchor_item,
2661                                                 item);
2662     }
2663
2664   gtk_icon_view_scroll_to_item (icon_view, item);
2665
2666   if (dirty)
2667     g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
2668 }
2669
2670 static void     
2671 gtk_icon_view_scroll_to_item (GtkIconView     *icon_view, 
2672                               GtkIconViewItem *item)
2673 {
2674   gint y, height;
2675   gdouble value;
2676
2677   gdk_window_get_geometry (icon_view->priv->bin_window, NULL, &y, NULL, &height, NULL);
2678
2679   if (y + item->y < 0)
2680     {
2681       value = icon_view->priv->vadjustment->value + y + item->y;
2682       gtk_adjustment_set_value (icon_view->priv->vadjustment, value);
2683     }
2684   else if (y + item->y + item->height > GTK_WIDGET (icon_view)->allocation.height)
2685     {
2686       value = icon_view->priv->vadjustment->value + y + item->y + item->height 
2687         - GTK_WIDGET (icon_view)->allocation.height;
2688       gtk_adjustment_set_value (icon_view->priv->vadjustment, value);
2689     }
2690 }
2691
2692 /* Public API */
2693
2694
2695 /**
2696  * gtk_icon_view_new:
2697  * 
2698  * Creates a new #GtkIconView widget
2699  * 
2700  * Return value: A newly created #GtkIconView widget
2701  *
2702  * Since: 2.6
2703  **/
2704 GtkWidget *
2705 gtk_icon_view_new (void)
2706 {
2707   return g_object_new (GTK_TYPE_ICON_VIEW, NULL);
2708 }
2709
2710 /**
2711  * gtk_icon_view_new_with_model:
2712  * @model: The model.
2713  * 
2714  * Creates a new #GtkIconView widget with the model @model.
2715  * 
2716  * Return value: A newly created #GtkIconView widget.
2717  *
2718  * Since: 2.6 
2719  **/
2720 GtkWidget *
2721 gtk_icon_view_new_with_model (GtkTreeModel *model)
2722 {
2723   return g_object_new (GTK_TYPE_ICON_VIEW, "model", model, NULL);
2724 }
2725
2726
2727 /**
2728  * gtk_icon_view_get_path_at_pos:
2729  * @icon_view: A #GtkIconView.
2730  * @x: The x position to be identified
2731  * @y: The y position to be identified
2732  * 
2733  * Finds the path at the point (@x, @y), relative to widget coordinates.
2734  * 
2735  * Return value: The #GtkTreePath corresponding to the icon or %NULL
2736  * if no icon exists at that position.
2737  *
2738  * Since: 2.6 
2739  **/
2740 GtkTreePath *
2741 gtk_icon_view_get_path_at_pos (GtkIconView *icon_view,
2742                                gint         x,
2743                                gint         y)
2744 {
2745   GtkIconViewItem *item;
2746   GtkTreePath *path;
2747   
2748   g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), NULL);
2749
2750   item = gtk_icon_view_get_item_at_pos (icon_view, x, y);
2751
2752   if (!item)
2753     return NULL;
2754
2755   path = gtk_tree_path_new_from_indices (item->index, -1);
2756
2757   return path;
2758 }
2759
2760 /**
2761  * gtk_icon_view_selected_foreach:
2762  * @icon_view: A #GtkIconView.
2763  * @func: The funcion to call for each selected icon.
2764  * @data: User data to pass to the function.
2765  * 
2766  * Calls a function for each selected icon. Note that the model or
2767  * selection cannot be modified from within this function.
2768  *
2769  * Since: 2.6 
2770  **/
2771 void
2772 gtk_icon_view_selected_foreach (GtkIconView           *icon_view,
2773                                 GtkIconViewForeachFunc func,
2774                                 gpointer               data)
2775 {
2776   GList *list;
2777   
2778   for (list = icon_view->priv->items; list; list = list->next)
2779     {
2780       GtkIconViewItem *item = list->data;
2781       GtkTreePath *path = gtk_tree_path_new_from_indices (item->index, -1);
2782
2783       if (item->selected)
2784         (* func) (icon_view, path, data);
2785
2786       gtk_tree_path_free (path);
2787     }
2788 }
2789
2790 /**
2791  * gtk_icon_view_set_selection_mode:
2792  * @icon_view: A #GtkIconView.
2793  * @mode: The selection mode
2794  * 
2795  * Sets the selection mode of the @icon_view.
2796  *
2797  * Since: 2.6 
2798  **/
2799 void
2800 gtk_icon_view_set_selection_mode (GtkIconView      *icon_view,
2801                                   GtkSelectionMode  mode)
2802 {
2803   g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
2804
2805   if (mode == icon_view->priv->selection_mode)
2806     return;
2807   
2808   if (mode == GTK_SELECTION_NONE ||
2809       icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE)
2810     gtk_icon_view_unselect_all (icon_view);
2811   
2812   icon_view->priv->selection_mode = mode;
2813
2814   g_object_notify (G_OBJECT (icon_view), "selection_mode");
2815 }
2816
2817 /**
2818  * gtk_icon_view_get_selection_mode:
2819  * @icon_view: A #GtkIconView.
2820  * 
2821  * Gets the selection mode of the @icon_view.
2822  *
2823  * Return value: the current selection mode
2824  *
2825  * Since: 2.6 
2826  **/
2827 GtkSelectionMode
2828 gtk_icon_view_get_selection_mode (GtkIconView *icon_view)
2829 {
2830   g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), GTK_SELECTION_SINGLE);
2831
2832   return icon_view->priv->selection_mode;
2833 }
2834
2835 /**
2836  * gtk_icon_view_set_model:
2837  * @icon_view: A #GtkIconView.
2838  * @model: The model.
2839  *
2840  * Sets the model for a #GtkIconView.  
2841  * If the @icon_view already has a model set, it will remove 
2842  * it before setting the new model.  If @model is %NULL, then
2843  * it will unset the old model.
2844  *
2845  * Since: 2.6 
2846  **/
2847 void
2848 gtk_icon_view_set_model (GtkIconView *icon_view,
2849                          GtkTreeModel *model)
2850 {
2851   g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
2852
2853   if (model != NULL)
2854     g_return_if_fail (GTK_IS_TREE_MODEL (model));
2855   
2856   if (icon_view->priv->model == model)
2857     return;
2858
2859   if (model)
2860     {
2861       GType pixbuf_column_type, text_column_type;
2862       
2863       g_return_if_fail (gtk_tree_model_get_flags (model) & GTK_TREE_MODEL_LIST_ONLY);
2864
2865       if (icon_view->priv->pixbuf_column != -1)
2866         {
2867           pixbuf_column_type = gtk_tree_model_get_column_type (icon_view->priv->model,
2868                                                                icon_view->priv->pixbuf_column);   
2869
2870           g_return_if_fail (pixbuf_column_type == GDK_TYPE_PIXBUF);
2871         }
2872
2873       if (icon_view->priv->text_column != -1)
2874         {
2875           text_column_type = gtk_tree_model_get_column_type (icon_view->priv->model,
2876                                                              icon_view->priv->pixbuf_column);     
2877
2878           g_return_if_fail (text_column_type == G_TYPE_STRING);
2879         }
2880       
2881     }
2882   
2883   if (icon_view->priv->model)
2884     {
2885       g_signal_handlers_disconnect_by_func (icon_view->priv->model,
2886                                             gtk_icon_view_row_changed,
2887                                             icon_view);
2888       g_signal_handlers_disconnect_by_func (icon_view->priv->model,
2889                                             gtk_icon_view_row_inserted,
2890                                             icon_view);
2891       g_signal_handlers_disconnect_by_func (icon_view->priv->model,
2892                                             gtk_icon_view_row_deleted,
2893                                             icon_view);
2894       g_signal_handlers_disconnect_by_func (icon_view->priv->model,
2895                                             gtk_icon_view_rows_reordered,
2896                                             icon_view);
2897
2898       g_object_unref (icon_view->priv->model);
2899       
2900       g_list_foreach (icon_view->priv->items, (GFunc)gtk_icon_view_item_free, NULL);
2901       g_list_free (icon_view->priv->items);
2902       icon_view->priv->items = NULL;
2903     }
2904
2905   icon_view->priv->model = model;
2906
2907   if (icon_view->priv->model)
2908     {
2909       g_object_ref (icon_view->priv->model);
2910       g_signal_connect (icon_view->priv->model,
2911                         "row_changed",
2912                         G_CALLBACK (gtk_icon_view_row_changed),
2913                         icon_view);
2914       g_signal_connect (icon_view->priv->model,
2915                         "row_inserted",
2916                         G_CALLBACK (gtk_icon_view_row_inserted),
2917                         icon_view);
2918       g_signal_connect (icon_view->priv->model,
2919                         "row_deleted",
2920                         G_CALLBACK (gtk_icon_view_row_deleted),
2921                         icon_view);
2922       g_signal_connect (icon_view->priv->model,
2923                         "rows_reordered",
2924                         G_CALLBACK (gtk_icon_view_rows_reordered),
2925                         icon_view);
2926
2927       gtk_icon_view_build_items (icon_view);
2928     }
2929
2930   g_object_notify (G_OBJECT (icon_view), "model");  
2931 }
2932
2933 /**
2934  * gtk_icon_view_get_model:
2935  * @icon_view: a #GtkIconView
2936  *
2937  * Returns the model the #GtkIconView is based on.  Returns %NULL if the
2938  * model is unset.
2939  *
2940  * Return value: A #GtkTreeModel, or %NULL if none is currently being used.
2941  *
2942  * Since: 2.6 
2943  **/
2944 GtkTreeModel *
2945 gtk_icon_view_get_model (GtkIconView *icon_view)
2946 {
2947   g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), NULL);
2948
2949   return icon_view->priv->model;
2950 }
2951
2952 /**
2953  * gtk_icon_view_set_text_column:
2954  * @icon_view: A #GtkIconView.
2955  * @column: A column in the currently used model.
2956  * 
2957  * Sets the column with text for @icon_view to be @column. The text
2958  * column must be of type #G_TYPE_STRING.
2959  *
2960  * Since: 2.6 
2961  **/
2962 void
2963 gtk_icon_view_set_text_column (GtkIconView *icon_view,
2964                                gint          column)
2965 {
2966   if (column == icon_view->priv->text_column)
2967     return;
2968   
2969   if (column == -1)
2970     icon_view->priv->text_column = -1;
2971   else
2972     {
2973       if (icon_view->priv->model != NULL)
2974         {
2975           GType column_type;
2976           
2977           column_type = gtk_tree_model_get_column_type (icon_view->priv->model, column);
2978
2979           g_return_if_fail (column_type == G_TYPE_STRING);
2980         }
2981       
2982       icon_view->priv->text_column = column;
2983     }
2984
2985   gtk_icon_view_invalidate_sizes (icon_view);
2986   gtk_icon_view_queue_layout (icon_view);
2987   
2988   g_object_notify (G_OBJECT (icon_view), "text_column");
2989 }
2990
2991 /**
2992  * gtk_icon_view_get_text_column:
2993  * @icon_view: A #GtkIconView.
2994  *
2995  * Returns the column with text for @icon_view.
2996  *
2997  * Returns: the text column, or -1 if it's unset.
2998  *
2999  * Since: 2.6
3000  */
3001 gint
3002 gtk_icon_view_get_text_column (GtkIconView  *icon_view)
3003 {
3004   g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1);
3005
3006   return icon_view->priv->text_column;
3007 }
3008
3009 /**
3010  * gtk_icon_view_set_markup_column:
3011  * @icon_view: A #GtkIconView.
3012  * @column: A column in the currently used model.
3013  * 
3014  * Sets the column with markup information for @icon_view to be
3015  * @column. The markup column must be of type #G_TYPE_STRING.
3016  * If the markup column is set to something, it overrides
3017  * the text column set by gtk_icon_view_set_text_column().
3018  *
3019  * Since: 2.6
3020  **/
3021 void
3022 gtk_icon_view_set_markup_column (GtkIconView *icon_view,
3023                                  gint         column)
3024 {
3025   if (column == icon_view->priv->markup_column)
3026     return;
3027   
3028   if (column == -1)
3029     icon_view->priv->markup_column = -1;
3030   else
3031     {
3032       if (icon_view->priv->model != NULL)
3033         {
3034           GType column_type;
3035           
3036           column_type = gtk_tree_model_get_column_type (icon_view->priv->model, column);
3037
3038           g_return_if_fail (column_type == G_TYPE_STRING);
3039         }
3040       
3041       icon_view->priv->markup_column = column;
3042     }
3043
3044   gtk_icon_view_invalidate_sizes (icon_view);
3045   gtk_icon_view_queue_layout (icon_view);
3046   
3047   g_object_notify (G_OBJECT (icon_view), "markup_column");
3048 }
3049
3050 /**
3051  * gtk_icon_view_get_markup_column:
3052  * @icon_view: A #GtkIconView.
3053  *
3054  * Returns the column with markup text for @icon_view.
3055  *
3056  * Returns: the markup column, or -1 if it's unset.
3057  *
3058  * Since: 2.6
3059  */
3060 gint
3061 gtk_icon_view_get_markup_column (GtkIconView  *icon_view)
3062 {
3063   g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1);
3064
3065   return icon_view->priv->markup_column;
3066 }
3067
3068 /**
3069  * gtk_icon_view_set_pixbuf_column:
3070  * @icon_view: A #GtkIconView.
3071  * @column: A column in the currently used model.
3072  * 
3073  * Sets the column with pixbufs for @icon_view to be @column. The pixbuf
3074  * column must be of type #GDK_TYPE_PIXBUF
3075  *
3076  * Since: 2.6 
3077  **/
3078 void
3079 gtk_icon_view_set_pixbuf_column (GtkIconView *icon_view,
3080                                  gint         column)
3081 {
3082   if (column == icon_view->priv->pixbuf_column)
3083     return;
3084   
3085   if (column == -1)
3086     icon_view->priv->pixbuf_column = -1;
3087   else
3088     {
3089       if (icon_view->priv->model != NULL)
3090         {
3091           GType column_type;
3092           
3093           column_type = gtk_tree_model_get_column_type (icon_view->priv->model, column);
3094
3095           g_return_if_fail (column_type == GDK_TYPE_PIXBUF);
3096         }
3097       
3098       icon_view->priv->pixbuf_column = column;
3099     }
3100
3101   gtk_icon_view_invalidate_sizes (icon_view);
3102   gtk_icon_view_queue_layout (icon_view);
3103   
3104   g_object_notify (G_OBJECT (icon_view), "pixbuf_column");
3105   
3106 }
3107
3108 /**
3109  * gtk_icon_view_get_pixbuf_column:
3110  * @icon_view: A #GtkIconView.
3111  *
3112  * Returns the column with pixbufs for @icon_view.
3113  *
3114  * Returns: the pixbuf column, or -1 if it's unset.
3115  *
3116  * Since: 2.6
3117  */
3118 gint
3119 gtk_icon_view_get_pixbuf_column (GtkIconView  *icon_view)
3120 {
3121   g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1);
3122
3123   return icon_view->priv->pixbuf_column;
3124 }
3125
3126 /**
3127  * gtk_icon_view_select_path:
3128  * @icon_view: A #GtkIconView.
3129  * @path: The #GtkTreePath to be selected.
3130  * 
3131  * Selects the row at @path.
3132  *
3133  * Since: 2.6
3134  **/
3135 void
3136 gtk_icon_view_select_path (GtkIconView *icon_view,
3137                            GtkTreePath *path)
3138 {
3139   GtkIconViewItem *item;
3140   
3141   g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
3142   g_return_if_fail (icon_view->priv->model != NULL);
3143   g_return_if_fail (path != NULL);
3144
3145   item = g_list_nth (icon_view->priv->items,
3146                      gtk_tree_path_get_indices(path)[0])->data;
3147
3148   if (!item)
3149     return;
3150   
3151   gtk_icon_view_select_item (icon_view, item);
3152 }
3153
3154 /**
3155  * gtk_icon_view_unselect_path:
3156  * @icon_view: A #GtkIconView.
3157  * @path: The #GtkTreePath to be unselected.
3158  * 
3159  * Unselects the row at @path.
3160  *
3161  * Since: 2.6
3162  **/
3163 void
3164 gtk_icon_view_unselect_path (GtkIconView *icon_view,
3165                              GtkTreePath *path)
3166 {
3167   GtkIconViewItem *item;
3168   
3169   g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
3170   g_return_if_fail (icon_view->priv->model != NULL);
3171   g_return_if_fail (path != NULL);
3172
3173   item = g_list_nth (icon_view->priv->items,
3174                      gtk_tree_path_get_indices(path)[0])->data;
3175
3176   if (!item)
3177     return;
3178   
3179   gtk_icon_view_unselect_item (icon_view, item);
3180 }
3181
3182 /**
3183  * gtk_icon_view_get_selected_items:
3184  * @icon_view: A #GtkIconView.
3185  *
3186  * Creates a list of path of all selected items. Additionally, if you are
3187  * planning on modifying the model after calling this function, you may
3188  * want to convert the returned list into a list of #GtkTreeRowReference<!-- -->s.
3189  * To do this, you can use gtk_tree_row_reference_new().
3190  *
3191  * To free the return value, use:
3192  * <informalexample><programlisting>
3193  * g_list_foreach (list, gtk_tree_path_free, NULL);
3194  * g_list_free (list);
3195  * </programlisting></informalexample>
3196  *
3197  * Return value: A #GList containing a #GtkTreePath for each selected row.
3198  *
3199  * Since: 2.6
3200  **/
3201 GList *
3202 gtk_icon_view_get_selected_items (GtkIconView *icon_view)
3203 {
3204   GList *list;
3205   GList *selected = NULL;
3206   
3207   g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), NULL);
3208   
3209   for (list = icon_view->priv->items; list != NULL; list = list->next)
3210     {
3211       GtkIconViewItem *item = list->data;
3212
3213       if (item->selected)
3214         {
3215           GtkTreePath *path = gtk_tree_path_new_from_indices (item->index, -1);
3216
3217           selected = g_list_prepend (selected, path);
3218         }
3219     }
3220
3221   return selected;
3222 }
3223
3224 /**
3225  * gtk_icon_view_select_all:
3226  * @icon_view: A #GtkIconView.
3227  * 
3228  * Selects all the icons. @icon_view must has its selection mode set
3229  * to #GTK_SELECTION_MULTIPLE.
3230  *
3231  * Since: 2.6
3232  **/
3233 void
3234 gtk_icon_view_select_all (GtkIconView *icon_view)
3235 {
3236   GList *items;
3237   gboolean dirty = FALSE;
3238   
3239   g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
3240
3241   if (icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
3242     return;
3243
3244   for (items = icon_view->priv->items; items; items = items->next)
3245     {
3246       GtkIconViewItem *item = items->data;
3247       
3248       if (!item->selected)
3249         {
3250           dirty = TRUE;
3251           item->selected = TRUE;
3252           gtk_icon_view_queue_draw_item (icon_view, item);
3253         }
3254     }
3255
3256   if (dirty)
3257     g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
3258 }
3259
3260 /**
3261  * gtk_icon_view_unselect_all:
3262  * @icon_view: A #GtkIconView.
3263  * 
3264  * Unselects all the icons.
3265  *
3266  * Since: 2.6
3267  **/
3268 void
3269 gtk_icon_view_unselect_all (GtkIconView *icon_view)
3270 {
3271   gboolean dirty = FALSE;
3272   
3273   g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
3274
3275   dirty = gtk_icon_view_unselect_all_internal (icon_view);
3276
3277   if (dirty)
3278     g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
3279 }
3280
3281 /**
3282  * gtk_icon_view_path_is_selected:
3283  * @icon_view: A #GtkIconView.
3284  * @path: A #GtkTreePath to check selection on.
3285  * 
3286  * Returns %TRUE if the icon pointed to by @path is currently
3287  * selected. If @icon does not point to a valid location, %FALSE is returned.
3288  * 
3289  * Return value: %TRUE if @path is selected.
3290  *
3291  * Since: 2.6
3292  **/
3293 gboolean
3294 gtk_icon_view_path_is_selected (GtkIconView *icon_view,
3295                                 GtkTreePath *path)
3296 {
3297   GtkIconViewItem *item;
3298   
3299   g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), FALSE);
3300   g_return_val_if_fail (icon_view->priv->model != NULL, FALSE);
3301   g_return_val_if_fail (path != NULL, FALSE);
3302   
3303   item = g_list_nth (icon_view->priv->items,
3304                      gtk_tree_path_get_indices(path)[0])->data;
3305
3306   if (!item)
3307     return FALSE;
3308   
3309   return item->selected;
3310 }
3311
3312 /**
3313  * gtk_icon_view_item_activated:
3314  * @icon_view: A #GtkIconView
3315  * @path: The #GtkTreePath to be activated
3316  * 
3317  * Activates the item determined by @path.
3318  *
3319  * Since: 2.6
3320  **/
3321 void
3322 gtk_icon_view_item_activated (GtkIconView      *icon_view,
3323                               GtkTreePath      *path)
3324 {
3325   g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
3326   g_return_if_fail (path != NULL);
3327   
3328   g_signal_emit (G_OBJECT (icon_view), icon_view_signals[ITEM_ACTIVATED], 0, path);
3329 }