1 /*-
2  * Copyright (c) 2008       Jannis Pohlmann <jannis@xfce.org>
3  * Copyright (c) 2004-2006  os-cillation e.K.
4  * Copyright (c) 2002,2004  Anders Carlsson <andersca@gnu.org>
5  *
6  * Written by Benedikt Meurer <benny@xfce.org>.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23 
24 /* Modified by Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
25  * on 2009-08-30 for use in libfm */
26 
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30 
31 /* #ifdef HAVE_MATH_H */
32 #include <math.h>
33 /* #endif */
34 #ifdef HAVE_MEMORY_H
35 #include <memory.h>
36 #endif
37 #ifdef HAVE_STDLIB_H
38 #include <stdlib.h>
39 #endif
40 #ifdef HAVE_STRING_H
41 #include <string.h>
42 #endif
43 
44 #include <gdk/gdkkeysyms.h>
45 
46 /*
47 #include <exo/exo-config.h>
48 #include <exo/exo-enum-types.h>
49 #include <exo/exo-icon-view.h>
50 #include <exo/exo-marshal.h>
51 #include <exo/exo-private.h>
52 #include <exo/exo-string.h>
53 #include <exo/exo-alias.h>
54 */
55 #include "exo-icon-view.h"
56 #include "exo-string.h"
57 #include "exo-marshal.h"
58 #include "exo-private.h"
59 
60 /* libfm specific */
61 #include "gtk-compat.h"
62 
63 /* from exo/exo-marshal.h */
64 #if defined(G_PARAM_STATIC_NAME) && defined(G_PARAM_STATIC_NICK) && defined(G_PARAM_STATIC_BLURB)
65 #define EXO_PARAM_READABLE  (G_PARAM_READABLE \
66                            | G_PARAM_STATIC_NAME \
67                            | G_PARAM_STATIC_NICK \
68                            | G_PARAM_STATIC_BLURB)
69 #define EXO_PARAM_WRITABLE  (G_PARAM_WRITABLE \
70                            | G_PARAM_STATIC_NAME \
71                            | G_PARAM_STATIC_NICK \
72                            | G_PARAM_STATIC_BLURB)
73 #define EXO_PARAM_READWRITE (G_PARAM_READWRITE \
74                            | G_PARAM_STATIC_NAME \
75                            | G_PARAM_STATIC_NICK \
76                            | G_PARAM_STATIC_BLURB)
77 #else
78 #define EXO_PARAM_READABLE  (G_PARAM_READABLE)
79 #define EXO_PARAM_WRITABLE  (G_PARAM_WRITABLE)
80 #define EXO_PARAM_READWRITE (G_PARAM_READWRITE)
81 #endif
82 
83 #define             I_(string)  g_intern_static_string(string)
84 
85 /* from exo/exo-enum-types.h */
86 GType
exo_icon_view_layout_mode_get_type(void)87 exo_icon_view_layout_mode_get_type (void)
88 {
89     static GType type = 0;
90     if (type == 0) {
91     static const GEnumValue values[] = {
92     { EXO_ICON_VIEW_LAYOUT_ROWS, "EXO_ICON_VIEW_LAYOUT_ROWS", "rows" },
93     { EXO_ICON_VIEW_LAYOUT_COLS, "EXO_ICON_VIEW_LAYOUT_COLS", "cols" },
94     { 0, NULL, NULL }
95     };
96     type = g_enum_register_static ("ExoIconViewLayoutMode", values);
97   }
98     return type;
99 }
100 #define EXO_TYPE_ICON_VIEW_LAYOUT_MODE (exo_icon_view_layout_mode_get_type())
101 /* enumerations from "exo-mount-point.h" */
102 
103 
104 /* the search dialog timeout (in ms) */
105 #define EXO_ICON_VIEW_SEARCH_DIALOG_TIMEOUT (5000)
106 
107 #define SCROLL_EDGE_SIZE 15
108 
109 
110 
111 /* Property identifiers */
112 enum
113 {
114   PROP_0,
115   PROP_PIXBUF_COLUMN,
116   PROP_TEXT_COLUMN,
117   PROP_MARKUP_COLUMN,
118   PROP_SELECTION_MODE,
119   PROP_LAYOUT_MODE,
120   PROP_ORIENTATION,
121   PROP_MODEL,
122   PROP_COLUMNS,
123   PROP_ITEM_WIDTH,
124   PROP_SPACING,
125   PROP_ROW_SPACING,
126   PROP_COLUMN_SPACING,
127   PROP_MARGIN,
128   PROP_REORDERABLE,
129   PROP_SINGLE_CLICK,
130   PROP_SINGLE_CLICK_TIMEOUT,
131   PROP_ENABLE_SEARCH,
132   PROP_SEARCH_COLUMN,
133   /* For scrollable interface */
134   PROP_HADJUSTMENT,
135   PROP_VADJUSTMENT,
136   PROP_HSCROLL_POLICY,
137   PROP_VSCROLL_POLICY
138 };
139 
140 /* Signal identifiers */
141 enum
142 {
143   ITEM_ACTIVATED,
144   SELECTION_CHANGED,
145   SELECT_ALL,
146   UNSELECT_ALL,
147   SELECT_CURSOR_ITEM,
148   TOGGLE_CURSOR_ITEM,
149   MOVE_CURSOR,
150   ACTIVATE_CURSOR_ITEM,
151   START_INTERACTIVE_SEARCH,
152   LAST_SIGNAL
153 };
154 
155 /* Icon view flags */
156 typedef enum
157 {
158   EXO_ICON_VIEW_DRAW_KEYFOCUS = (1l << 0),  /* whether to draw keyboard focus */
159   EXO_ICON_VIEW_ITERS_PERSIST = (1l << 1),  /* whether current model provides persistent iterators */
160 } ExoIconViewFlags;
161 
162 #define EXO_ICON_VIEW_SET_FLAG(icon_view, flag)   G_STMT_START{ (EXO_ICON_VIEW (icon_view)->priv->flags |= flag); }G_STMT_END
163 #define EXO_ICON_VIEW_UNSET_FLAG(icon_view, flag) G_STMT_START{ (EXO_ICON_VIEW (icon_view)->priv->flags &= ~(flag)); }G_STMT_END
164 #define EXO_ICON_VIEW_FLAG_SET(icon_view, flag)   ((EXO_ICON_VIEW (icon_view)->priv->flags & (flag)) == (flag))
165 
166 
167 
168 typedef struct _ExoIconViewCellInfo ExoIconViewCellInfo;
169 typedef struct _ExoIconViewChild    ExoIconViewChild;
170 typedef struct _ExoIconViewItem     ExoIconViewItem;
171 
172 
173 
174 #define EXO_ICON_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EXO_TYPE_ICON_VIEW, ExoIconViewPrivate))
175 #define EXO_ICON_VIEW_CELL_INFO(obj)   ((ExoIconViewCellInfo *) (obj))
176 #define EXO_ICON_VIEW_CHILD(obj)       ((ExoIconViewChild *) (obj))
177 #define EXO_ICON_VIEW_ITEM(obj)        ((ExoIconViewItem *) (obj))
178 
179 
180 
181 static void                 exo_icon_view_cell_layout_init               (GtkCellLayoutIface     *iface);
182 static void                 exo_icon_view_dispose                        (GObject                *object);
183 static void                 exo_icon_view_finalize                       (GObject                *object);
184 static void                 exo_icon_view_get_property                   (GObject                *object,
185                                                                           guint                   prop_id,
186                                                                           GValue                 *value,
187                                                                           GParamSpec             *pspec);
188 static void                 exo_icon_view_set_property                   (GObject                *object,
189                                                                           guint                   prop_id,
190                                                                           const GValue           *value,
191                                                                           GParamSpec             *pspec);
192 static void                 exo_icon_view_realize                        (GtkWidget              *widget);
193 static void                 exo_icon_view_unrealize                      (GtkWidget              *widget);
194 #if GTK_CHECK_VERSION(3, 0, 0)
195 static void                 exo_icon_view_state_flags_changed            (GtkWidget *widget,
196                                                                           GtkStateFlags previous_state);
197 #else
198 static void                 exo_icon_view_state_changed                  (GtkWidget *widget,
199                                                                           GtkStateType previous_state);
200 #endif
201 
202 #if GTK_CHECK_VERSION(3, 0, 0)
203 static void                 exo_icon_view_get_preferred_width            (GtkWidget              *widget,
204                                                                           gint                   *minimal_width,
205                                                                           gint                   *natural_width);
206 static void                 exo_icon_view_get_preferred_height           (GtkWidget              *widget,
207                                                                           gint                   *minimal_height,
208                                                                           gint                   *natural_height);
209 #endif
210 static void                 exo_icon_view_size_request                   (GtkWidget              *widget,
211                                                                           GtkRequisition         *requisition);
212 static void                 exo_icon_view_size_allocate                  (GtkWidget              *widget,
213                                                                           GtkAllocation          *allocation);
214 static void                 exo_icon_view_style_set                      (GtkWidget              *widget,
215                                                                           GtkStyle               *previous_style);
216 #if GTK_CHECK_VERSION(3, 0, 0)
217 static gboolean             exo_icon_view_draw                           (GtkWidget              *widget,
218                                                                           cairo_t                *cr);
219 #else
220 static gboolean             exo_icon_view_expose_event                   (GtkWidget              *widget,
221                                                                           GdkEventExpose         *event);
222 #endif
223 static gboolean             exo_icon_view_motion_notify_event            (GtkWidget              *widget,
224                                                                           GdkEventMotion         *event);
225 static gboolean             exo_icon_view_button_press_event             (GtkWidget              *widget,
226                                                                           GdkEventButton         *event);
227 static gboolean             exo_icon_view_button_release_event           (GtkWidget              *widget,
228                                                                           GdkEventButton         *event);
229 static gboolean             exo_icon_view_scroll_event                   (GtkWidget              *widget,
230                                                                           GdkEventScroll         *event);
231 static gboolean             exo_icon_view_key_press_event                (GtkWidget              *widget,
232                                                                           GdkEventKey            *event);
233 static gboolean             exo_icon_view_focus_out_event                (GtkWidget              *widget,
234                                                                           GdkEventFocus          *event);
235 static gboolean             exo_icon_view_leave_notify_event             (GtkWidget              *widget,
236                                                                           GdkEventCrossing       *event);
237 static void                 exo_icon_view_remove                         (GtkContainer           *container,
238                                                                           GtkWidget              *widget);
239 static void                 exo_icon_view_forall                         (GtkContainer           *container,
240                                                                           gboolean                include_internals,
241                                                                           GtkCallback             callback,
242                                                                           gpointer                callback_data);
243 static AtkObject           *exo_icon_view_get_accessible                 (GtkWidget              *widget);
244 #if !GTK_CHECK_VERSION(3, 0, 0)
245 static void                 exo_icon_view_set_adjustments                (ExoIconView            *icon_view,
246                                                                           GtkAdjustment          *hadj,
247                                                                           GtkAdjustment          *vadj);
248 #else
249 static void                 exo_icon_view_set_hadjustment                (ExoIconView            *icon_view,
250                                                                           GtkAdjustment          *hadj);
251 static void                 exo_icon_view_set_vadjustment                (ExoIconView            *icon_view,
252                                                                           GtkAdjustment          *vadj);
253 #endif
254 static void                 exo_icon_view_real_select_all                (ExoIconView            *icon_view);
255 static void                 exo_icon_view_real_unselect_all              (ExoIconView            *icon_view);
256 static void                 exo_icon_view_real_select_cursor_item        (ExoIconView            *icon_view);
257 static void                 exo_icon_view_real_toggle_cursor_item        (ExoIconView            *icon_view);
258 static gboolean             exo_icon_view_real_activate_cursor_item      (ExoIconView            *icon_view);
259 static gboolean             exo_icon_view_real_start_interactive_search  (ExoIconView            *icon_view);
260 static void                 exo_icon_view_adjustment_changed             (GtkAdjustment          *adjustment,
261                                                                           ExoIconView            *icon_view);
262 static gint                 exo_icon_view_layout_cols                    (ExoIconView            *icon_view,
263                                                                           gint                    item_height,
264                                                                           gint                   *x,
265                                                                           gint                   *maximum_height,
266                                                                           gint                    max_rows);
267 static gint                 exo_icon_view_layout_rows                    (ExoIconView            *icon_view,
268                                                                           gint                    item_width,
269                                                                           gint                   *y,
270                                                                           gint                   *maximum_width,
271                                                                           gint                    max_cols);
272 static void                 exo_icon_view_layout                         (ExoIconView            *icon_view);
273 static void                 exo_icon_view_paint_item                     (ExoIconView            *icon_view,
274                                                                           ExoIconViewItem        *item,
275                                                                           GdkRectangle           *area,
276 #if GTK_CHECK_VERSION(3, 0, 0)
277                                                                           cairo_t                *drawable,
278 #else
279                                                                           GdkDrawable            *drawable,
280 #endif
281                                                                           gint                    x,
282                                                                           gint                    y,
283                                                                           gboolean                draw_focus);
284 static void                 exo_icon_view_queue_draw_item                (ExoIconView            *icon_view,
285                                                                           ExoIconViewItem        *item);
286 static void                 exo_icon_view_queue_layout                   (ExoIconView            *icon_view);
287 static void                 exo_icon_view_set_cursor_item                (ExoIconView            *icon_view,
288                                                                           ExoIconViewItem        *item,
289                                                                           gint                    cursor_cell);
290 static void                 exo_icon_view_start_rubberbanding            (ExoIconView            *icon_view,
291                                                                           gint                    x,
292                                                                           gint                    y);
293 static void                 exo_icon_view_stop_rubberbanding             (ExoIconView            *icon_view);
294 static void                 exo_icon_view_update_rubberband_selection    (ExoIconView            *icon_view);
295 static gboolean             exo_icon_view_item_hit_test                  (ExoIconView            *icon_view,
296                                                                           ExoIconViewItem        *item,
297                                                                           gint                    x,
298                                                                           gint                    y,
299                                                                           gint                    width,
300                                                                           gint                    height);
301 static gboolean             exo_icon_view_unselect_all_internal          (ExoIconView            *icon_view);
302 static void                 exo_icon_view_calculate_item_size            (ExoIconView            *icon_view,
303                                                                           ExoIconViewItem        *item);
304 static void                 exo_icon_view_calculate_item_size2           (ExoIconView            *icon_view,
305                                                                           ExoIconViewItem        *item,
306                                                                           gint                   *max_width,
307                                                                           gint                   *max_height);
308 static void                 exo_icon_view_update_rubberband              (gpointer                data);
309 static void                 exo_icon_view_invalidate_sizes               (ExoIconView            *icon_view);
310 static void                 exo_icon_view_add_move_binding               (GtkBindingSet          *binding_set,
311                                                                           guint                   keyval,
312                                                                           guint                   modmask,
313                                                                           GtkMovementStep         step,
314                                                                           gint                    count);
315 static gboolean             exo_icon_view_real_move_cursor               (ExoIconView            *icon_view,
316                                                                           GtkMovementStep         step,
317                                                                           gint                    count);
318 static void                 exo_icon_view_move_cursor_up_down            (ExoIconView            *icon_view,
319                                                                           gint                    count);
320 static void                 exo_icon_view_move_cursor_page_up_down       (ExoIconView            *icon_view,
321                                                                           gint                    count);
322 static void                 exo_icon_view_move_cursor_left_right         (ExoIconView            *icon_view,
323                                                                           gint                    count);
324 static void                 exo_icon_view_move_cursor_start_end          (ExoIconView            *icon_view,
325                                                                           gint                    count);
326 static void                 exo_icon_view_scroll_to_item                 (ExoIconView            *icon_view,
327                                                                           ExoIconViewItem        *item);
328 static void                 exo_icon_view_select_item                    (ExoIconView            *icon_view,
329                                                                           ExoIconViewItem        *item);
330 static void                 exo_icon_view_unselect_item                  (ExoIconView            *icon_view,
331                                                                           ExoIconViewItem        *item);
332 static gboolean             exo_icon_view_select_all_between             (ExoIconView            *icon_view,
333                                                                           ExoIconViewItem        *anchor,
334                                                                           ExoIconViewItem        *cursor);
335 static ExoIconViewItem *    exo_icon_view_get_item_at_coords             (const ExoIconView      *icon_view,
336                                                                           gint                    x,
337                                                                           gint                    y,
338                                                                           gboolean                only_in_cell,
339                                                                           ExoIconViewCellInfo   **cell_at_pos);
340 static void                 exo_icon_view_get_cell_area                  (ExoIconView            *icon_view,
341                                                                           ExoIconViewItem        *item,
342                                                                           ExoIconViewCellInfo    *cell_info,
343                                                                           GdkRectangle           *cell_area);
344 static ExoIconViewCellInfo *exo_icon_view_get_cell_info                  (ExoIconView            *icon_view,
345                                                                           GtkCellRenderer        *renderer);
346 static void                 exo_icon_view_set_cell_data                  (const ExoIconView      *icon_view,
347                                                                           ExoIconViewItem        *item);
348 static void                 exo_icon_view_cell_layout_pack_start         (GtkCellLayout          *layout,
349                                                                           GtkCellRenderer        *renderer,
350                                                                           gboolean                expand);
351 static void                 exo_icon_view_cell_layout_pack_end           (GtkCellLayout          *layout,
352                                                                           GtkCellRenderer        *renderer,
353                                                                           gboolean                expand);
354 static void                 exo_icon_view_cell_layout_add_attribute      (GtkCellLayout          *layout,
355                                                                           GtkCellRenderer        *renderer,
356                                                                           const gchar            *attribute,
357                                                                           gint                    column);
358 static void                 exo_icon_view_cell_layout_clear              (GtkCellLayout          *layout);
359 static void                 exo_icon_view_cell_layout_clear_attributes   (GtkCellLayout          *layout,
360                                                                           GtkCellRenderer        *renderer);
361 static void                 exo_icon_view_cell_layout_set_cell_data_func (GtkCellLayout          *layout,
362                                                                           GtkCellRenderer        *cell,
363                                                                           GtkCellLayoutDataFunc   func,
364                                                                           gpointer                func_data,
365                                                                           GDestroyNotify          destroy);
366 static void                 exo_icon_view_cell_layout_reorder            (GtkCellLayout          *layout,
367                                                                           GtkCellRenderer        *cell,
368                                                                           gint                    position);
369 static void                 exo_icon_view_item_activate_cell             (ExoIconView            *icon_view,
370                                                                           ExoIconViewItem        *item,
371                                                                           ExoIconViewCellInfo    *cell_info,
372                                                                           GdkEvent               *event);
373 static void                 exo_icon_view_put                            (ExoIconView            *icon_view,
374                                                                           GtkWidget              *widget,
375                                                                           ExoIconViewItem        *item,
376                                                                           gint                    cell);
377 static void                 exo_icon_view_remove_widget                  (GtkCellEditable        *editable,
378                                                                           ExoIconView            *icon_view);
379 static void                 exo_icon_view_start_editing                  (ExoIconView            *icon_view,
380                                                                           ExoIconViewItem        *item,
381                                                                           ExoIconViewCellInfo    *cell_info,
382                                                                           GdkEvent               *event);
383 static void                 exo_icon_view_stop_editing                   (ExoIconView            *icon_view,
384                                                                           gboolean                cancel_editing);
385 
386 /* Source side drag signals */
387 static void exo_icon_view_drag_begin       (GtkWidget        *widget,
388                                             GdkDragContext   *context);
389 static void exo_icon_view_drag_end         (GtkWidget        *widget,
390                                             GdkDragContext   *context);
391 static void exo_icon_view_drag_data_get    (GtkWidget        *widget,
392                                             GdkDragContext   *context,
393                                             GtkSelectionData *selection_data,
394                                             guint             info,
395                                             guint             drag_time);
396 static void exo_icon_view_drag_data_delete (GtkWidget        *widget,
397                                             GdkDragContext   *context);
398 
399 /* Target side drag signals */
400 static void     exo_icon_view_drag_leave         (GtkWidget        *widget,
401                                                   GdkDragContext   *context,
402                                                   guint             drag_time);
403 static gboolean exo_icon_view_drag_motion        (GtkWidget        *widget,
404                                                   GdkDragContext   *context,
405                                                   gint              x,
406                                                   gint              y,
407                                                   guint             drag_time);
408 static gboolean exo_icon_view_drag_drop          (GtkWidget        *widget,
409                                                   GdkDragContext   *context,
410                                                   gint              x,
411                                                   gint              y,
412                                                   guint             drag_time);
413 static void     exo_icon_view_drag_data_received (GtkWidget        *widget,
414                                                   GdkDragContext   *context,
415                                                   gint              x,
416                                                   gint              y,
417                                                   GtkSelectionData *selection_data,
418                                                   guint             info,
419                                                   guint             drag_time);
420 static gboolean exo_icon_view_maybe_begin_drag   (ExoIconView      *icon_view,
421                                                   GdkEventMotion   *event);
422 
423 static void     remove_scroll_timeout            (ExoIconView *icon_view);
424 
425 /* single-click autoselection support */
426 static gboolean exo_icon_view_single_click_timeout          (gpointer user_data);
427 static void     exo_icon_view_single_click_timeout_destroy  (gpointer user_data);
428 
429 /* Interactive search support */
430 static void     exo_icon_view_search_activate           (GtkEntry       *entry,
431                                                          ExoIconView    *icon_view);
432 static void     exo_icon_view_search_dialog_hide        (GtkWidget      *search_dialog,
433                                                          ExoIconView    *icon_view);
434 static void     exo_icon_view_search_ensure_directory   (ExoIconView    *icon_view);
435 static void     exo_icon_view_search_init               (GtkWidget      *search_entry,
436                                                          ExoIconView    *icon_view);
437 static gboolean exo_icon_view_search_iter               (ExoIconView    *icon_view,
438                                                          GtkTreeModel   *model,
439                                                          GtkTreeIter    *iter,
440                                                          const gchar    *text,
441                                                          gint           *count,
442                                                          gint            n);
443 static void     exo_icon_view_search_move               (GtkWidget      *widget,
444                                                          ExoIconView    *icon_view,
445                                                          gboolean        move_up);
446 #if GTK_CHECK_VERSION(2, 20, 0)
447 static void     exo_icon_view_search_preedit_changed    (GtkEntry       *entry,
448                                                          gchar          *preedit,
449                                                          ExoIconView    *icon_view);
450 #else
451 static void     exo_icon_view_search_preedit_changed    (GtkIMContext   *im_context,
452                                                          ExoIconView    *icon_view);
453 #endif
454 static gboolean exo_icon_view_search_start              (ExoIconView    *icon_view,
455                                                          gboolean        keybinding);
456 static gboolean exo_icon_view_search_equal_func         (GtkTreeModel   *model,
457                                                          gint            column,
458                                                          const gchar    *key,
459                                                          GtkTreeIter    *iter,
460                                                          gpointer        user_data);
461 static void     exo_icon_view_search_position_func      (ExoIconView    *icon_view,
462                                                          GtkWidget      *search_dialog,
463                                                          gpointer        user_data);
464 static gboolean exo_icon_view_search_button_press_event (GtkWidget      *widget,
465                                                          GdkEventButton *event,
466                                                          ExoIconView    *icon_view);
467 static gboolean exo_icon_view_search_delete_event       (GtkWidget      *widget,
468                                                          GdkEventAny    *event,
469                                                          ExoIconView    *icon_view);
470 static gboolean exo_icon_view_search_key_press_event    (GtkWidget      *widget,
471                                                          GdkEventKey    *event,
472                                                          ExoIconView    *icon_view);
473 static gboolean exo_icon_view_search_scroll_event       (GtkWidget      *widget,
474                                                          GdkEventScroll *event,
475                                                          ExoIconView    *icon_view);
476 static gboolean exo_icon_view_search_timeout            (gpointer        user_data);
477 static void     exo_icon_view_search_timeout_destroy    (gpointer        user_data);
478 
479 
480 
481 struct _ExoIconViewCellInfo
482 {
483   GtkCellRenderer      *cell;
484   guint                 expand : 1;
485   guint                 pack : 1;
486   guint                 editing : 1;
487   gint                  position;
488   GSList               *attributes;
489   GtkCellLayoutDataFunc func;
490   gpointer              func_data;
491   GDestroyNotify        destroy;
492   gboolean              is_text;
493 };
494 
495 struct _ExoIconViewChild
496 {
497   ExoIconViewItem *item;
498   GtkWidget       *widget;
499   gint             cell;
500 };
501 
502 struct _ExoIconViewItem
503 {
504   GtkTreeIter iter;
505 
506   /* Bounding box (a value of -1 for width indicates
507    * that the item needs to be layouted first)
508    */
509   GdkRectangle area;
510 
511   /* Individual cells.
512    * box[i] is the actual area occupied by cell i,
513    * before, after are used to calculate the cell
514    * area relative to the box.
515    * See exo_icon_view_get_cell_area().
516    */
517   gint n_cells;
518   GdkRectangle *box;
519   gint index;
520   gint *before;
521   gint *after;
522 
523   guint row : ((sizeof (guint) / 2) * 8) - 1;
524   guint col : ((sizeof (guint) / 2) * 8) - 1;
525   guint selected : 1;
526   guint selected_before_rubberbanding : 1;
527 };
528 
529 struct _ExoIconViewPrivate
530 {
531   gint width, height;
532   gint rows, cols;
533 
534   GtkSelectionMode selection_mode;
535 
536   ExoIconViewLayoutMode layout_mode;
537 
538   GdkWindow *bin_window;
539 
540   GList *children;
541 
542   GtkTreeModel *model;
543 
544   GList *items;
545 
546   GtkAdjustment *hadjustment;
547   GtkAdjustment *vadjustment;
548 #if GTK_CHECK_VERSION(3, 0, 0)
549   /* GtkScrollablePolicy needs to be checked when
550    * driving the scrollable adjustment values */
551   guint hscroll_policy : 1;
552   guint vscroll_policy : 1;
553 #endif
554 
555   gint layout_idle_id;
556 
557   gboolean doing_rubberband;
558   gint rubberband_x_1, rubberband_y_1;
559   gint rubberband_x2, rubberband_y2;
560 
561   gint scroll_timeout_id;
562   gint scroll_value_diff;
563   gint event_last_x, event_last_y;
564 
565   ExoIconViewItem *anchor_item;
566   ExoIconViewItem *cursor_item;
567   ExoIconViewItem *edited_item;
568   GtkCellEditable *editable;
569   ExoIconViewItem *prelit_item;
570 
571   ExoIconViewItem *last_single_clicked;
572 
573   GList *cell_list;
574   gint n_cells;
575 
576   gint cursor_cell;
577 
578   GtkOrientation orientation;
579 
580   gint columns;
581   gint item_width;
582   gint spacing;
583   gint row_spacing;
584   gint column_spacing;
585   gint margin;
586 
587   gint text_column;
588   gint markup_column;
589   gint pixbuf_column;
590 
591   gint pixbuf_cell;
592   gint text_cell;
593 
594   /* Drag-and-drop. */
595   GdkModifierType start_button_mask;
596   gint pressed_button;
597   gint press_start_x;
598   gint press_start_y;
599 
600   GtkTargetList *source_targets;
601   GdkDragAction source_actions;
602 
603   GtkTargetList *dest_targets;
604   GdkDragAction dest_actions;
605 
606   GtkTreeRowReference *dest_item;
607   ExoIconViewDropPosition dest_pos;
608 
609   /* delayed scrolling */
610   GtkTreeRowReference          *scroll_to_path;
611   gfloat                        scroll_to_row_align;
612   gfloat                        scroll_to_col_align;
613   guint                         scroll_to_use_align : 1;
614 
615   /* misc flags */
616   guint                         source_set : 1;
617   guint                         dest_set : 1;
618   guint                         reorderable : 1;
619   guint                         empty_view_drop :1;
620 
621   guint                         ctrl_pressed : 1;
622   guint                         shift_pressed : 1;
623 
624   guint                         dnd_locked : 1;
625 
626   /* Single-click support
627    * The single_click_timeout is the timeout after which the
628    * prelited item will be automatically selected in single
629    * click mode (0 to disable).
630    */
631   guint                         single_click : 1;
632   guint                         single_click_timeout;
633   guint                         single_click_timeout_id;
634   guint                         single_click_timeout_state;
635 
636   /* Interactive search support */
637   guint                         enable_search : 1;
638   guint                         search_imcontext_changed : 1;
639   gint                          search_column;
640   gint                          search_selected_iter;
641   gint                          search_timeout_id;
642   gboolean                      search_disable_popdown;
643   ExoIconViewSearchEqualFunc    search_equal_func;
644   gpointer                      search_equal_data;
645   GDestroyNotify                search_equal_destroy;
646   ExoIconViewSearchPositionFunc search_position_func;
647   gpointer                      search_position_data;
648   GDestroyNotify                search_position_destroy;
649   gint                          search_entry_changed_id;
650   GtkWidget                    *search_entry;
651   GtkWidget                    *search_window;
652 
653   /* ExoIconViewFlags */
654   guint flags;
655 };
656 
657 
658 
659 static guint icon_view_signals[LAST_SIGNAL];
660 
661 
662 
G_DEFINE_TYPE_WITH_CODE(ExoIconView,exo_icon_view,GTK_TYPE_CONTAINER,G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE,NULL)G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,exo_icon_view_cell_layout_init))663 G_DEFINE_TYPE_WITH_CODE (ExoIconView, exo_icon_view, GTK_TYPE_CONTAINER,
664 #if GTK_CHECK_VERSION(3, 0, 0)
665     G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL)
666 #endif
667     G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT, exo_icon_view_cell_layout_init))
668 
669 
670 
671 static void
672 exo_icon_view_class_init (ExoIconViewClass *klass)
673 {
674   GtkContainerClass *gtkcontainer_class;
675   GtkWidgetClass    *gtkwidget_class;
676   GtkBindingSet     *gtkbinding_set;
677   GObjectClass      *gobject_class;
678 
679   /* add our private data to the type's instances */
680   g_type_class_add_private (klass, sizeof (ExoIconViewPrivate));
681 
682   gobject_class = G_OBJECT_CLASS (klass);
683   gobject_class->dispose = exo_icon_view_dispose;
684   gobject_class->finalize = exo_icon_view_finalize;
685   gobject_class->set_property = exo_icon_view_set_property;
686   gobject_class->get_property = exo_icon_view_get_property;
687 
688   gtkwidget_class = GTK_WIDGET_CLASS (klass);
689   gtkwidget_class->realize = exo_icon_view_realize;
690   gtkwidget_class->unrealize = exo_icon_view_unrealize;
691   gtkwidget_class->get_accessible = exo_icon_view_get_accessible;
692 #if GTK_CHECK_VERSION(3, 0, 0)
693   gtkwidget_class->state_flags_changed = exo_icon_view_state_flags_changed;
694   gtkwidget_class->get_preferred_width = exo_icon_view_get_preferred_width;
695   gtkwidget_class->get_preferred_height = exo_icon_view_get_preferred_height;
696 #else
697   gtkwidget_class->state_changed = exo_icon_view_state_changed;
698   gtkwidget_class->size_request = exo_icon_view_size_request;
699 #endif
700   gtkwidget_class->size_allocate = exo_icon_view_size_allocate;
701   gtkwidget_class->style_set = exo_icon_view_style_set;
702 #if GTK_CHECK_VERSION(3, 0, 0)
703   gtkwidget_class->draw = exo_icon_view_draw;
704 #else
705   gtkwidget_class->expose_event = exo_icon_view_expose_event;
706 #endif
707   gtkwidget_class->motion_notify_event = exo_icon_view_motion_notify_event;
708   gtkwidget_class->button_press_event = exo_icon_view_button_press_event;
709   gtkwidget_class->button_release_event = exo_icon_view_button_release_event;
710   gtkwidget_class->scroll_event = exo_icon_view_scroll_event;
711   gtkwidget_class->key_press_event = exo_icon_view_key_press_event;
712   gtkwidget_class->focus_out_event = exo_icon_view_focus_out_event;
713   gtkwidget_class->leave_notify_event = exo_icon_view_leave_notify_event;
714   gtkwidget_class->drag_begin = exo_icon_view_drag_begin;
715   gtkwidget_class->drag_end = exo_icon_view_drag_end;
716   gtkwidget_class->drag_data_get = exo_icon_view_drag_data_get;
717   gtkwidget_class->drag_data_delete = exo_icon_view_drag_data_delete;
718   gtkwidget_class->drag_leave = exo_icon_view_drag_leave;
719   gtkwidget_class->drag_motion = exo_icon_view_drag_motion;
720   gtkwidget_class->drag_drop = exo_icon_view_drag_drop;
721   gtkwidget_class->drag_data_received = exo_icon_view_drag_data_received;
722 
723   gtkcontainer_class = GTK_CONTAINER_CLASS (klass);
724   gtkcontainer_class->remove = exo_icon_view_remove;
725   gtkcontainer_class->forall = exo_icon_view_forall;
726 
727 
728 #if !GTK_CHECK_VERSION(3, 0, 0)
729   klass->set_scroll_adjustments = exo_icon_view_set_adjustments;
730 #endif
731   klass->select_all = exo_icon_view_real_select_all;
732   klass->unselect_all = exo_icon_view_real_unselect_all;
733   klass->select_cursor_item = exo_icon_view_real_select_cursor_item;
734   klass->toggle_cursor_item = exo_icon_view_real_toggle_cursor_item;
735   klass->move_cursor = exo_icon_view_real_move_cursor;
736   klass->activate_cursor_item = exo_icon_view_real_activate_cursor_item;
737   klass->start_interactive_search = exo_icon_view_real_start_interactive_search;
738 
739   /**
740    * ExoIconView:column-spacing:
741    *
742    * The column-spacing property specifies the space which is inserted between
743    * the columns of the icon view.
744    *
745    * Since: 0.3.1
746    **/
747   g_object_class_install_property (gobject_class,
748                                    PROP_COLUMN_SPACING,
749                                    g_param_spec_int ("column-spacing",
750                                                      _("Column Spacing"),
751                                                      _("Space which is inserted between grid column"),
752                                                      0, G_MAXINT, 6,
753                                                      EXO_PARAM_READWRITE));
754 
755   /**
756    * ExoIconView:columns:
757    *
758    * The columns property contains the number of the columns in which the
759    * items should be displayed. If it is -1, the number of columns will
760    * be chosen automatically to fill the available area.
761    *
762    * Since: 0.3.1
763    **/
764   g_object_class_install_property (gobject_class,
765                                    PROP_COLUMNS,
766                                    g_param_spec_int ("columns",
767                                                      _("Number of columns"),
768                                                      _("Number of columns to display"),
769                                                      -1, G_MAXINT, -1,
770                                                      EXO_PARAM_READWRITE));
771 
772   /**
773    * ExoIconView:enable-search:
774    *
775    * View allows user to search through columns interactively.
776    *
777    * Since: 0.3.1.3
778    **/
779   g_object_class_install_property (gobject_class,
780                                    PROP_ENABLE_SEARCH,
781                                    g_param_spec_boolean ("enable-search",
782                                                          _("Enable Search"),
783                                                          _("View allows user to search through columns interactively"),
784                                                          TRUE,
785                                                          EXO_PARAM_READWRITE));
786 
787 
788   /**
789    * ExoIconView:item-width:
790    *
791    * The item-width property specifies the width to use for each item.
792    * If it is set to -1, the icon view will automatically determine a
793    * suitable item size.
794    *
795    * Since: 0.3.1
796    **/
797   g_object_class_install_property (gobject_class,
798                                    PROP_ITEM_WIDTH,
799                                    g_param_spec_int ("item-width",
800                                                      _("Width for each item"),
801                                                      _("The width used for each item"),
802                                                      -1, G_MAXINT, -1,
803                                                      EXO_PARAM_READWRITE));
804 
805   /**
806    * ExoIconView:layout-mode:
807    *
808    * The layout-mode property specifies the way items are layed out in
809    * the #ExoIconView. This can be either %EXO_ICON_VIEW_LAYOUT_ROWS,
810    * which is the default, where items are layed out horizontally in
811    * rows from top to bottom, or %EXO_ICON_VIEW_LAYOUT_COLS, where items
812    * are layed out vertically in columns from left to right.
813    *
814    * Since: 0.3.1.5
815    **/
816   g_object_class_install_property (gobject_class,
817                                    PROP_LAYOUT_MODE,
818                                    g_param_spec_enum ("layout-mode",
819                                                       _("Layout mode"),
820                                                       _("The layout mode"),
821                                                       EXO_TYPE_ICON_VIEW_LAYOUT_MODE,
822                                                       EXO_ICON_VIEW_LAYOUT_ROWS,
823                                                       EXO_PARAM_READWRITE));
824 
825   /**
826    * ExoIconView:margin:
827    *
828    * The margin property specifies the space which is inserted
829    * at the edges of the icon view.
830    *
831    * Since: 0.3.1
832    **/
833   g_object_class_install_property (gobject_class,
834                                    PROP_MARGIN,
835                                    g_param_spec_int ("margin",
836                                                      _("Margin"),
837                                                      _("Space which is inserted at the edges of the icon view"),
838                                                      0, G_MAXINT, 6,
839                                                      EXO_PARAM_READWRITE));
840 
841   /**
842    * ExoIconView:markup-column:
843    *
844    * The markup-column property contains the number of the model column
845    * containing markup information to be displayed. The markup column must be
846    * of type #G_TYPE_STRING. If this property and the text-column property
847    * are both set to column numbers, it overrides the text column.
848    * If both are set to -1, no texts are displayed.
849    **/
850   g_object_class_install_property (gobject_class,
851                                    PROP_MARKUP_COLUMN,
852                                    g_param_spec_int ("markup-column",
853                                                      _("Markup column"),
854                                                      _("Model column used to retrieve the text if using Pango markup"),
855                                                      -1, G_MAXINT, -1,
856                                                      EXO_PARAM_READWRITE));
857 
858   /**
859    * ExoIconView:model:
860    *
861    * The model property contains the #GtkTreeModel, which should be
862    * display by this icon view. Setting this property to %NULL turns
863    * off the display of anything.
864    **/
865   g_object_class_install_property (gobject_class,
866                                    PROP_MODEL,
867                                    g_param_spec_object ("model",
868                                                         _("Icon View Model"),
869                                                         _("The model for the icon view"),
870                                                         GTK_TYPE_TREE_MODEL,
871                                                         EXO_PARAM_READWRITE));
872 
873   /**
874    * ExoIconView:orientation:
875    *
876    * The orientation property specifies how the cells (i.e. the icon and
877    * the text) of the item are positioned relative to each other.
878    **/
879   g_object_class_install_property (gobject_class,
880                                    PROP_ORIENTATION,
881                                    g_param_spec_enum ("orientation",
882                                                       _("Orientation"),
883                                                       _("How the text and icon of each item are positioned relative to each other"),
884                                                       GTK_TYPE_ORIENTATION,
885                                                       GTK_ORIENTATION_VERTICAL,
886                                                       EXO_PARAM_READWRITE));
887 
888   /**
889    * ExoIconView:pixbuf-column:
890    *
891    * The ::pixbuf-column property contains the number of the model column
892    * containing the pixbufs which are displayed. The pixbuf column must be
893    * of type #GDK_TYPE_PIXBUF. Setting this property to -1 turns off the
894    * display of pixbufs.
895    **/
896   g_object_class_install_property (gobject_class,
897                                    PROP_PIXBUF_COLUMN,
898                                    g_param_spec_int ("pixbuf-column",
899                                                      _("Pixbuf column"),
900                                                      _("Model column used to retrieve the icon pixbuf from"),
901                                                      -1, G_MAXINT, -1,
902                                                      EXO_PARAM_READWRITE));
903 
904   /**
905    * ExoIconView:reorderable:
906    *
907    * The reorderable property specifies if the items can be reordered
908    * by Drag and Drop.
909    *
910    * Since: 0.3.1
911    **/
912   g_object_class_install_property (gobject_class,
913                                    PROP_REORDERABLE,
914                                    g_param_spec_boolean ("reorderable",
915                                                          _("Reorderable"),
916                                                          _("View is reorderable"),
917                                                          FALSE,
918                                                          EXO_PARAM_READWRITE));
919 
920   /**
921    * ExoIconView:row-spacing:
922    *
923    * The row-spacing property specifies the space which is inserted between
924    * the rows of the icon view.
925    *
926    * Since: 0.3.1
927    **/
928   g_object_class_install_property (gobject_class,
929                                    PROP_ROW_SPACING,
930                                    g_param_spec_int ("row-spacing",
931                                                      _("Row Spacing"),
932                                                      _("Space which is inserted between grid rows"),
933                                                      0, G_MAXINT, 6,
934                                                      EXO_PARAM_READWRITE));
935 
936   /**
937    * ExoIconView:search-column:
938    *
939    * Model column to search through when searching through code.
940    *
941    * Since: 0.3.1.3
942    **/
943   g_object_class_install_property (gobject_class,
944                                    PROP_SEARCH_COLUMN,
945                                    g_param_spec_int ("search-column",
946                                                      _("Search Column"),
947                                                      _("Model column to search through when searching through item"),
948                                                      -1, G_MAXINT, -1,
949                                                      EXO_PARAM_READWRITE));
950 
951   /**
952    * ExoIconView:selection-mode:
953    *
954    * The selection-mode property specifies the selection mode of
955    * icon view. If the mode is #GTK_SELECTION_MULTIPLE, rubberband selection
956    * is enabled, for the other modes, only keyboard selection is possible.
957    **/
958   g_object_class_install_property (gobject_class,
959                                    PROP_SELECTION_MODE,
960                                    g_param_spec_enum ("selection-mode",
961                                                       _("Selection mode"),
962                                                       _("The selection mode"),
963                                                       GTK_TYPE_SELECTION_MODE,
964                                                       GTK_SELECTION_SINGLE,
965                                                       EXO_PARAM_READWRITE));
966 
967   /**
968    * ExoIconView:single-click:
969    *
970    * Determines whether items can be activated by single or double clicks.
971    *
972    * Since: 0.3.1.3
973    **/
974   g_object_class_install_property (gobject_class,
975                                    PROP_SINGLE_CLICK,
976                                    g_param_spec_boolean ("single-click",
977                                                          _("Single Click"),
978                                                          _("Whether the items in the view can be activated with single clicks"),
979                                                          FALSE,
980                                                          EXO_PARAM_READWRITE));
981 
982   /**
983    * ExoIconView:single-click-timeout:
984    *
985    * The amount of time in milliseconds after which a prelited item (an item
986    * which is hovered by the mouse cursor) will be selected automatically in
987    * single click mode. A value of %0 disables the automatic selection.
988    *
989    * Since: 0.3.1.5
990    **/
991   g_object_class_install_property (gobject_class,
992                                    PROP_SINGLE_CLICK_TIMEOUT,
993                                    g_param_spec_uint ("single-click-timeout",
994                                                       _("Single Click Timeout"),
995                                                       _("The amount of time after which the item under the mouse cursor will be selected automatically in single click mode"),
996                                                       0, G_MAXUINT, 0,
997                                                       EXO_PARAM_READWRITE));
998 
999   /**
1000    * ExoIconView:spacing:
1001    *
1002    * The spacing property specifies the space which is inserted between
1003    * the cells (i.e. the icon and the text) of an item.
1004    *
1005    * Since: 0.3.1
1006    **/
1007   g_object_class_install_property (gobject_class,
1008                                    PROP_SPACING,
1009                                    g_param_spec_int ("spacing",
1010                                                      _("Spacing"),
1011                                                      _("Space which is inserted between cells of an item"),
1012                                                      0, G_MAXINT, 0,
1013                                                      EXO_PARAM_READWRITE));
1014 
1015   /**
1016    * ExoIconView:text-column:
1017    *
1018    * The text-column property contains the number of the model column
1019    * containing the texts which are displayed. The text column must be
1020    * of type #G_TYPE_STRING. If this property and the markup-column
1021    * property are both set to -1, no texts are displayed.
1022    **/
1023   g_object_class_install_property (gobject_class,
1024                                    PROP_TEXT_COLUMN,
1025                                    g_param_spec_int ("text-column",
1026                                                      _("Text column"),
1027                                                      _("Model column used to retrieve the text from"),
1028                                                      -1, G_MAXINT, -1,
1029                                                      EXO_PARAM_READWRITE));
1030 
1031 
1032 #if GTK_CHECK_VERSION(3, 0, 0)
1033   /* Scrollable interface properties */
1034   g_object_class_override_property (gobject_class, PROP_HADJUSTMENT,    "hadjustment");
1035   g_object_class_override_property (gobject_class, PROP_VADJUSTMENT,    "vadjustment");
1036   g_object_class_override_property (gobject_class, PROP_HSCROLL_POLICY, "hscroll-policy");
1037   g_object_class_override_property (gobject_class, PROP_VSCROLL_POLICY, "vscroll-policy");
1038 #endif
1039 
1040   gtk_widget_class_install_style_property (gtkwidget_class,
1041                                            g_param_spec_boxed ("selection-box-color",
1042                                                                _("Selection Box Color"),
1043                                                                _("Color of the selection box"),
1044                                                                GDK_TYPE_COLOR,
1045                                                                EXO_PARAM_READABLE));
1046 
1047   gtk_widget_class_install_style_property (gtkwidget_class,
1048                                            g_param_spec_uchar ("selection-box-alpha",
1049                                                                _("Selection Box Alpha"),
1050                                                                _("Opacity of the selection box"),
1051                                                                0, 0xff,
1052                                                                0x40,
1053                                                                EXO_PARAM_READABLE));
1054 
1055   /**
1056    * ExoIconView::item-activated:
1057    * @icon_view : a #ExoIconView.
1058    * @path      :
1059    **/
1060   icon_view_signals[ITEM_ACTIVATED] =
1061     g_signal_new (I_("item-activated"),
1062                   G_TYPE_FROM_CLASS (gobject_class),
1063                   G_SIGNAL_RUN_LAST,
1064                   G_STRUCT_OFFSET (ExoIconViewClass, item_activated),
1065                   NULL, NULL,
1066                   g_cclosure_marshal_VOID__BOXED,
1067                   G_TYPE_NONE, 1,
1068                   GTK_TYPE_TREE_PATH);
1069 
1070   /**
1071    * ExoIconView::selection-changed:
1072    * @icon_view : a #ExoIconView.
1073    **/
1074   icon_view_signals[SELECTION_CHANGED] =
1075     g_signal_new (I_("selection-changed"),
1076                   G_TYPE_FROM_CLASS (gobject_class),
1077                   G_SIGNAL_RUN_FIRST,
1078                   G_STRUCT_OFFSET (ExoIconViewClass, selection_changed),
1079                   NULL, NULL,
1080                   g_cclosure_marshal_VOID__VOID,
1081                   G_TYPE_NONE, 0);
1082 
1083 #if !GTK_CHECK_VERSION(3, 0, 0)
1084   /**
1085    * ExoIconView::set-scroll-adjustments:
1086    * @icon_view   : a #ExoIconView.
1087    * @hadjustment :
1088    * @vadjustment :
1089    **/
1090   gtkwidget_class->set_scroll_adjustments_signal =
1091     g_signal_new (I_("set-scroll-adjustments"),
1092                   G_TYPE_FROM_CLASS (gobject_class),
1093                   G_SIGNAL_RUN_LAST,
1094                   G_STRUCT_OFFSET (ExoIconViewClass, set_scroll_adjustments),
1095                   NULL, NULL,
1096                   _exo_marshal_VOID__OBJECT_OBJECT,
1097                   G_TYPE_NONE, 2,
1098                   GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
1099 #endif
1100 
1101   /**
1102    * ExoIconView::select-all:
1103    * @icon_view : a #ExoIconView.
1104    **/
1105   icon_view_signals[SELECT_ALL] =
1106     g_signal_new (I_("select-all"),
1107                   G_TYPE_FROM_CLASS (gobject_class),
1108                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1109                   G_STRUCT_OFFSET (ExoIconViewClass, select_all),
1110                   NULL, NULL,
1111                   g_cclosure_marshal_VOID__VOID,
1112                   G_TYPE_NONE, 0);
1113 
1114   /**
1115    * ExoIconView::unselect-all:
1116    * @icon_view : a #ExoIconView.
1117    **/
1118   icon_view_signals[UNSELECT_ALL] =
1119     g_signal_new (I_("unselect-all"),
1120                   G_TYPE_FROM_CLASS (gobject_class),
1121                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1122                   G_STRUCT_OFFSET (ExoIconViewClass, unselect_all),
1123                   NULL, NULL,
1124                   g_cclosure_marshal_VOID__VOID,
1125                   G_TYPE_NONE, 0);
1126 
1127   /**
1128    * ExoIconView::select-cursor-item:
1129    * @icon_view : a #ExoIconView.
1130    **/
1131   icon_view_signals[SELECT_CURSOR_ITEM] =
1132     g_signal_new (I_("select-cursor-item"),
1133                   G_TYPE_FROM_CLASS (gobject_class),
1134                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1135                   G_STRUCT_OFFSET (ExoIconViewClass, select_cursor_item),
1136                   NULL, NULL,
1137                   g_cclosure_marshal_VOID__VOID,
1138                   G_TYPE_NONE, 0);
1139 
1140   /**
1141    * ExoIconView::toggle-cursor-item:
1142    * @icon_view : a #ExoIconView.
1143    **/
1144   icon_view_signals[TOGGLE_CURSOR_ITEM] =
1145     g_signal_new (I_("toggle-cursor-item"),
1146                   G_TYPE_FROM_CLASS (gobject_class),
1147                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1148                   G_STRUCT_OFFSET (ExoIconViewClass, toggle_cursor_item),
1149                   NULL, NULL,
1150                   g_cclosure_marshal_VOID__VOID,
1151                   G_TYPE_NONE, 0);
1152 
1153   /**
1154    * ExoIconView::activate-cursor-item:
1155    * @icon_view : a #ExoIconView.
1156    *
1157    * Return value:
1158    **/
1159   icon_view_signals[ACTIVATE_CURSOR_ITEM] =
1160     g_signal_new (I_("activate-cursor-item"),
1161                   G_TYPE_FROM_CLASS (gobject_class),
1162                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1163                   G_STRUCT_OFFSET (ExoIconViewClass, activate_cursor_item),
1164                   NULL, NULL,
1165                   _exo_marshal_BOOLEAN__VOID,
1166                   G_TYPE_BOOLEAN, 0);
1167 
1168   /**
1169    * ExoIconView::start-interactive-search:
1170    * @iconb_view : a #ExoIconView.
1171    *
1172    * Return value:
1173    **/
1174   icon_view_signals[START_INTERACTIVE_SEARCH] =
1175     g_signal_new (I_("start-interactive-search"),
1176                   G_TYPE_FROM_CLASS (gobject_class),
1177                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1178                   G_STRUCT_OFFSET (ExoIconViewClass, start_interactive_search),
1179                   NULL, NULL,
1180                   _exo_marshal_BOOLEAN__VOID,
1181                   G_TYPE_BOOLEAN, 0);
1182 
1183   /**
1184    * ExoIconView::move-cursor:
1185    * @icon_view : a #ExoIconView.
1186    * @step      :
1187    * @count     :
1188    *
1189    * Return value:
1190    **/
1191   icon_view_signals[MOVE_CURSOR] =
1192     g_signal_new (I_("move-cursor"),
1193                   G_TYPE_FROM_CLASS (gobject_class),
1194                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1195                   G_STRUCT_OFFSET (ExoIconViewClass, move_cursor),
1196                   NULL, NULL,
1197                   _exo_marshal_BOOLEAN__ENUM_INT,
1198                   G_TYPE_BOOLEAN, 2,
1199                   GTK_TYPE_MOVEMENT_STEP,
1200                   G_TYPE_INT);
1201 
1202   /* Key bindings */
1203   gtkbinding_set = gtk_binding_set_by_class (klass);
1204   gtk_binding_entry_add_signal (gtkbinding_set, GDK_KEY_a, GDK_CONTROL_MASK, "select-all", 0);
1205   gtk_binding_entry_add_signal (gtkbinding_set, GDK_KEY_a, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "unselect-all", 0);
1206   gtk_binding_entry_add_signal (gtkbinding_set, GDK_KEY_space, GDK_CONTROL_MASK, "toggle-cursor-item", 0);
1207   gtk_binding_entry_add_signal (gtkbinding_set, GDK_KEY_space, 0, "activate-cursor-item", 0);
1208   gtk_binding_entry_add_signal (gtkbinding_set, GDK_KEY_Return, 0, "activate-cursor-item", 0);
1209   gtk_binding_entry_add_signal (gtkbinding_set, GDK_KEY_ISO_Enter, 0, "activate-cursor-item", 0);
1210   gtk_binding_entry_add_signal (gtkbinding_set, GDK_KEY_KP_Enter, 0, "activate-cursor-item", 0);
1211   gtk_binding_entry_add_signal (gtkbinding_set, GDK_KEY_f, GDK_CONTROL_MASK, "start-interactive-search", 0);
1212   gtk_binding_entry_add_signal (gtkbinding_set, GDK_KEY_F, GDK_CONTROL_MASK, "start-interactive-search", 0);
1213 
1214   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_Up, 0, GTK_MOVEMENT_DISPLAY_LINES, -1);
1215   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_KP_Up, 0, GTK_MOVEMENT_DISPLAY_LINES, -1);
1216   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_Down, 0, GTK_MOVEMENT_DISPLAY_LINES, 1);
1217   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_KP_Down, 0, GTK_MOVEMENT_DISPLAY_LINES, 1);
1218   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_p, GDK_CONTROL_MASK, GTK_MOVEMENT_DISPLAY_LINES, -1);
1219   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_n, GDK_CONTROL_MASK, GTK_MOVEMENT_DISPLAY_LINES, 1);
1220   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_Home, 0, GTK_MOVEMENT_BUFFER_ENDS, -1);
1221   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_KP_Home, 0, GTK_MOVEMENT_BUFFER_ENDS, -1);
1222   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_End, 0, GTK_MOVEMENT_BUFFER_ENDS, 1);
1223   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_KP_End, 0, GTK_MOVEMENT_BUFFER_ENDS, 1);
1224   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_Page_Up, 0, GTK_MOVEMENT_PAGES, -1);
1225   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_KP_Page_Up, 0, GTK_MOVEMENT_PAGES, -1);
1226   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_Page_Down, 0, GTK_MOVEMENT_PAGES, 1);
1227   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_KP_Page_Down, 0, GTK_MOVEMENT_PAGES, 1);
1228   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_Right, 0, GTK_MOVEMENT_VISUAL_POSITIONS, 1);
1229   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_Left, 0, GTK_MOVEMENT_VISUAL_POSITIONS, -1);
1230   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_KP_Right, 0, GTK_MOVEMENT_VISUAL_POSITIONS, 1);
1231   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_KP_Left, 0, GTK_MOVEMENT_VISUAL_POSITIONS, -1);
1232 }
1233 
1234 
1235 
1236 static void
exo_icon_view_cell_layout_init(GtkCellLayoutIface * iface)1237 exo_icon_view_cell_layout_init (GtkCellLayoutIface *iface)
1238 {
1239   iface->pack_start = exo_icon_view_cell_layout_pack_start;
1240   iface->pack_end = exo_icon_view_cell_layout_pack_end;
1241   iface->clear = exo_icon_view_cell_layout_clear;
1242   iface->add_attribute = exo_icon_view_cell_layout_add_attribute;
1243   iface->set_cell_data_func = exo_icon_view_cell_layout_set_cell_data_func;
1244   iface->clear_attributes = exo_icon_view_cell_layout_clear_attributes;
1245   iface->reorder = exo_icon_view_cell_layout_reorder;
1246 }
1247 
1248 
1249 
1250 static void
exo_icon_view_init(ExoIconView * icon_view)1251 exo_icon_view_init (ExoIconView *icon_view)
1252 {
1253   icon_view->priv = EXO_ICON_VIEW_GET_PRIVATE (icon_view);
1254 
1255   icon_view->priv->selection_mode = GTK_SELECTION_SINGLE;
1256   icon_view->priv->pressed_button = -1;
1257   icon_view->priv->press_start_x = -1;
1258   icon_view->priv->press_start_y = -1;
1259   icon_view->priv->text_column = -1;
1260   icon_view->priv->markup_column = -1;
1261   icon_view->priv->pixbuf_column = -1;
1262   icon_view->priv->text_cell = -1;
1263   icon_view->priv->pixbuf_cell = -1;
1264 
1265   gtk_widget_set_can_focus (GTK_WIDGET (icon_view), TRUE);
1266 
1267 #if !GTK_CHECK_VERSION(3, 0, 0)
1268   exo_icon_view_set_adjustments (icon_view, NULL, NULL);
1269 #endif
1270 
1271   icon_view->priv->cursor_cell = -1;
1272 
1273   icon_view->priv->orientation = GTK_ORIENTATION_VERTICAL;
1274 
1275   icon_view->priv->columns = -1;
1276   icon_view->priv->item_width = -1;
1277   icon_view->priv->row_spacing = 6;
1278   icon_view->priv->column_spacing = 6;
1279   icon_view->priv->margin = 6;
1280 
1281   icon_view->priv->enable_search = TRUE;
1282   icon_view->priv->search_column = -1;
1283   icon_view->priv->search_equal_func = exo_icon_view_search_equal_func;
1284   icon_view->priv->search_position_func = exo_icon_view_search_position_func;
1285 
1286   icon_view->priv->flags = EXO_ICON_VIEW_DRAW_KEYFOCUS;
1287 }
1288 
1289 
1290 
1291 static void
exo_icon_view_dispose(GObject * object)1292 exo_icon_view_dispose (GObject *object)
1293 {
1294   ExoIconView *icon_view = EXO_ICON_VIEW (object);
1295 
1296   /* cancel any pending search timeout */
1297   if (G_UNLIKELY (icon_view->priv->search_timeout_id != 0))
1298     g_source_remove (icon_view->priv->search_timeout_id);
1299 
1300   /* destroy the interactive search dialog */
1301   if (G_UNLIKELY (icon_view->priv->search_window != NULL))
1302     {
1303       gtk_widget_destroy (icon_view->priv->search_window);
1304       icon_view->priv->search_entry = NULL;
1305       icon_view->priv->search_window = NULL;
1306     }
1307 
1308   /* drop search equal and position functions (if any) */
1309   exo_icon_view_set_search_equal_func (icon_view, NULL, NULL, NULL);
1310   exo_icon_view_set_search_position_func (icon_view, NULL, NULL, NULL);
1311 
1312   /* reset the drag dest item */
1313   exo_icon_view_set_drag_dest_item (icon_view, NULL, EXO_ICON_VIEW_NO_DROP);
1314 
1315   /* drop the scroll to path (if any) */
1316   if (G_UNLIKELY (icon_view->priv->scroll_to_path != NULL))
1317     {
1318       gtk_tree_row_reference_free (icon_view->priv->scroll_to_path);
1319       icon_view->priv->scroll_to_path = NULL;
1320     }
1321 
1322   /* reset the model (also stops any active editing) */
1323   exo_icon_view_set_model (icon_view, NULL);
1324 
1325   /* drop the scroll timer */
1326   remove_scroll_timeout (icon_view);
1327 
1328   (*G_OBJECT_CLASS (exo_icon_view_parent_class)->dispose) (object);
1329 }
1330 
1331 
1332 
1333 static void
exo_icon_view_finalize(GObject * object)1334 exo_icon_view_finalize (GObject *object)
1335 {
1336   ExoIconView *icon_view = EXO_ICON_VIEW (object);
1337 
1338   /* drop the scroll adjustments */
1339   g_object_unref (G_OBJECT (icon_view->priv->hadjustment));
1340   g_object_unref (G_OBJECT (icon_view->priv->vadjustment));
1341 
1342   /* drop the cell renderers */
1343   exo_icon_view_cell_layout_clear (GTK_CELL_LAYOUT (icon_view));
1344 
1345   /* be sure to cancel the single click timeout */
1346   if (G_UNLIKELY (icon_view->priv->single_click_timeout_id != 0))
1347     g_source_remove (icon_view->priv->single_click_timeout_id);
1348 
1349   /* kill the layout idle source (it's important to have this last!) */
1350   if (G_UNLIKELY (icon_view->priv->layout_idle_id != 0))
1351     g_source_remove (icon_view->priv->layout_idle_id);
1352 
1353   (*G_OBJECT_CLASS (exo_icon_view_parent_class)->finalize) (object);
1354 }
1355 
1356 
1357 static void
exo_icon_view_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1358 exo_icon_view_get_property (GObject      *object,
1359                             guint         prop_id,
1360                             GValue       *value,
1361                             GParamSpec   *pspec)
1362 {
1363   const ExoIconViewPrivate *priv = EXO_ICON_VIEW (object)->priv;
1364 
1365   switch (prop_id)
1366     {
1367     case PROP_COLUMN_SPACING:
1368       g_value_set_int (value, priv->column_spacing);
1369       break;
1370 
1371     case PROP_COLUMNS:
1372       g_value_set_int (value, priv->columns);
1373       break;
1374 
1375     case PROP_ENABLE_SEARCH:
1376       g_value_set_boolean (value, priv->enable_search);
1377       break;
1378 
1379     case PROP_ITEM_WIDTH:
1380       g_value_set_int (value, priv->item_width);
1381       break;
1382 
1383     case PROP_MARGIN:
1384       g_value_set_int (value, priv->margin);
1385       break;
1386 
1387     case PROP_MARKUP_COLUMN:
1388       g_value_set_int (value, priv->markup_column);
1389       break;
1390 
1391     case PROP_MODEL:
1392       g_value_set_object (value, priv->model);
1393       break;
1394 
1395     case PROP_ORIENTATION:
1396       g_value_set_enum (value, priv->orientation);
1397       break;
1398 
1399     case PROP_PIXBUF_COLUMN:
1400       g_value_set_int (value, priv->pixbuf_column);
1401       break;
1402 
1403     case PROP_REORDERABLE:
1404       g_value_set_boolean (value, priv->reorderable);
1405       break;
1406 
1407     case PROP_ROW_SPACING:
1408       g_value_set_int (value, priv->row_spacing);
1409       break;
1410 
1411     case PROP_SEARCH_COLUMN:
1412       g_value_set_int (value, priv->search_column);
1413       break;
1414 
1415     case PROP_SELECTION_MODE:
1416       g_value_set_enum (value, priv->selection_mode);
1417       break;
1418 
1419     case PROP_SINGLE_CLICK:
1420       g_value_set_boolean (value, priv->single_click);
1421       break;
1422 
1423     case PROP_SINGLE_CLICK_TIMEOUT:
1424       g_value_set_uint (value, priv->single_click_timeout);
1425       break;
1426 
1427     case PROP_SPACING:
1428       g_value_set_int (value, priv->spacing);
1429       break;
1430 
1431     case PROP_TEXT_COLUMN:
1432       g_value_set_int (value, priv->text_column);
1433       break;
1434 
1435     case PROP_LAYOUT_MODE:
1436       g_value_set_enum (value, priv->layout_mode);
1437       break;
1438 
1439 #if GTK_CHECK_VERSION(3, 0, 0)
1440     case PROP_HADJUSTMENT:
1441       g_value_set_object (value, priv->hadjustment);
1442       break;
1443     case PROP_VADJUSTMENT:
1444       g_value_set_object (value, priv->vadjustment);
1445       break;
1446     case PROP_HSCROLL_POLICY:
1447       g_value_set_enum (value, priv->hscroll_policy);
1448       break;
1449     case PROP_VSCROLL_POLICY:
1450       g_value_set_enum (value, priv->vscroll_policy);
1451       break;
1452 #endif
1453 
1454     default:
1455       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1456       break;
1457     }
1458 }
1459 
1460 
1461 
1462 static void
exo_icon_view_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1463 exo_icon_view_set_property (GObject      *object,
1464                             guint         prop_id,
1465                             const GValue *value,
1466                             GParamSpec   *pspec)
1467 {
1468   ExoIconView *icon_view = EXO_ICON_VIEW (object);
1469 
1470   switch (prop_id)
1471     {
1472     case PROP_COLUMN_SPACING:
1473       exo_icon_view_set_column_spacing (icon_view, g_value_get_int (value));
1474       break;
1475 
1476     case PROP_COLUMNS:
1477       exo_icon_view_set_columns (icon_view, g_value_get_int (value));
1478       break;
1479 
1480     case PROP_ENABLE_SEARCH:
1481       exo_icon_view_set_enable_search (icon_view, g_value_get_boolean (value));
1482       break;
1483 
1484     case PROP_ITEM_WIDTH:
1485       exo_icon_view_set_item_width (icon_view, g_value_get_int (value));
1486       break;
1487 
1488     case PROP_MARGIN:
1489       exo_icon_view_set_margin (icon_view, g_value_get_int (value));
1490       break;
1491 
1492     case PROP_MODEL:
1493       exo_icon_view_set_model (icon_view, g_value_get_object (value));
1494       break;
1495 
1496     case PROP_ORIENTATION:
1497       exo_icon_view_set_orientation (icon_view, g_value_get_enum (value));
1498       break;
1499 
1500     case PROP_REORDERABLE:
1501       exo_icon_view_set_reorderable (icon_view, g_value_get_boolean (value));
1502       break;
1503 
1504     case PROP_ROW_SPACING:
1505       exo_icon_view_set_row_spacing (icon_view, g_value_get_int (value));
1506       break;
1507 
1508     case PROP_SEARCH_COLUMN:
1509       exo_icon_view_set_search_column (icon_view, g_value_get_int (value));
1510       break;
1511 
1512     case PROP_SELECTION_MODE:
1513       exo_icon_view_set_selection_mode (icon_view, g_value_get_enum (value));
1514       break;
1515 
1516     case PROP_SINGLE_CLICK:
1517       exo_icon_view_set_single_click (icon_view, g_value_get_boolean (value));
1518       break;
1519 
1520     case PROP_SINGLE_CLICK_TIMEOUT:
1521       exo_icon_view_set_single_click_timeout (icon_view, g_value_get_uint (value));
1522       break;
1523 
1524     case PROP_SPACING:
1525       exo_icon_view_set_spacing (icon_view, g_value_get_int (value));
1526       break;
1527 
1528     case PROP_LAYOUT_MODE:
1529       exo_icon_view_set_layout_mode (icon_view, g_value_get_enum (value));
1530       break;
1531 
1532 #if GTK_CHECK_VERSION(3, 0, 0)
1533     case PROP_HADJUSTMENT:
1534       exo_icon_view_set_hadjustment (icon_view, g_value_get_object (value));
1535       break;
1536     case PROP_VADJUSTMENT:
1537       exo_icon_view_set_vadjustment (icon_view, g_value_get_object (value));
1538       break;
1539     case PROP_HSCROLL_POLICY:
1540       icon_view->priv->hscroll_policy = g_value_get_enum (value);
1541       gtk_widget_queue_resize (GTK_WIDGET (icon_view));
1542       break;
1543     case PROP_VSCROLL_POLICY:
1544       icon_view->priv->vscroll_policy = g_value_get_enum (value);
1545       gtk_widget_queue_resize (GTK_WIDGET (icon_view));
1546       break;
1547 #endif
1548 
1549     default:
1550       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1551       break;
1552     }
1553 }
1554 
1555 
1556 
1557 static void
exo_icon_view_realize(GtkWidget * widget)1558 exo_icon_view_realize (GtkWidget *widget)
1559 {
1560   ExoIconViewPrivate *priv = EXO_ICON_VIEW (widget)->priv;
1561   GdkWindow          *window;
1562 #if GTK_CHECK_VERSION(3, 0, 0)
1563   GtkStyleContext    *style;
1564 #else
1565   GtkStyle           *style;
1566 #endif
1567   GdkWindowAttr       attributes;
1568   GtkAllocation       allocation;
1569   gint                attributes_mask;
1570 
1571 #if GTK_CHECK_VERSION(2, 20, 0)
1572   gtk_widget_set_realized (widget, TRUE);
1573 #else
1574   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1575 #endif
1576 
1577   /* Allocate the clipping window */
1578   gtk_widget_get_allocation (widget, &allocation);
1579   attributes.window_type = GDK_WINDOW_CHILD;
1580   attributes.x = allocation.x;
1581   attributes.y = allocation.y;
1582   attributes.width = allocation.width;
1583   attributes.height = allocation.height;
1584   attributes.wclass = GDK_INPUT_OUTPUT;
1585   attributes.visual = gtk_widget_get_visual (widget);
1586   attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
1587 #if GTK_CHECK_VERSION(3, 0, 0)
1588   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
1589 #else
1590   attributes.colormap = gtk_widget_get_colormap (widget);
1591   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1592 #endif
1593   window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1594   gtk_widget_set_window (widget, window);
1595   gdk_window_set_user_data (window, widget);
1596 
1597   /* Allocate the icons window */
1598   attributes.x = 0;
1599   attributes.y = 0;
1600   attributes.width = MAX (priv->width, allocation.width);
1601   attributes.height = MAX (priv->height, allocation.height);
1602   attributes.event_mask = GDK_EXPOSURE_MASK
1603                         | GDK_SCROLL_MASK
1604                         | GDK_POINTER_MOTION_MASK
1605                         | GDK_BUTTON_PRESS_MASK
1606                         | GDK_BUTTON_RELEASE_MASK
1607                         | GDK_KEY_PRESS_MASK
1608                         | GDK_KEY_RELEASE_MASK
1609                         | gtk_widget_get_events (widget);
1610   priv->bin_window = gdk_window_new (window, &attributes, attributes_mask);
1611   gdk_window_set_user_data (priv->bin_window, widget);
1612 
1613   /* Attach style/background */
1614 #if GTK_CHECK_VERSION(3, 0, 0)
1615   style = gtk_widget_get_style_context (widget);
1616   gtk_style_context_set_background (style, priv->bin_window);
1617   gtk_style_context_set_background (style, window);
1618 #else
1619   style = gtk_style_attach (gtk_widget_get_style (widget), window);
1620   gtk_widget_set_style (widget, style);
1621   gdk_window_set_background (priv->bin_window, &style->base[gtk_widget_get_state(widget)]);
1622   gdk_window_set_background (window, &style->base[gtk_widget_get_state(widget)]);
1623 #endif
1624 
1625   /* map the icons window */
1626   gdk_window_show (priv->bin_window);
1627 }
1628 
1629 
1630 
1631 static void
exo_icon_view_unrealize(GtkWidget * widget)1632 exo_icon_view_unrealize (GtkWidget *widget)
1633 {
1634   ExoIconViewPrivate *priv = EXO_ICON_VIEW (widget)->priv;
1635 
1636   /* drop the icons window */
1637   gdk_window_set_user_data (priv->bin_window, NULL);
1638   gdk_window_destroy (priv->bin_window);
1639   priv->bin_window = NULL;
1640 
1641   /* let GtkWidget destroy children and widget->window */
1642   if (GTK_WIDGET_CLASS (exo_icon_view_parent_class)->unrealize)
1643     (*GTK_WIDGET_CLASS (exo_icon_view_parent_class)->unrealize) (widget);
1644 }
1645 
1646 
1647 #if GTK_CHECK_VERSION(3, 0, 0)
exo_icon_view_state_flags_changed(GtkWidget * widget,GtkStateFlags previous_state)1648 static void                 exo_icon_view_state_flags_changed            (GtkWidget *widget,
1649                                                                           GtkStateFlags previous_state)
1650 #else
1651 static void                 exo_icon_view_state_changed                  (GtkWidget *widget,
1652                                                                           GtkStateType previous_state)
1653 #endif
1654 {
1655   ExoIconViewPrivate *priv = EXO_ICON_VIEW (widget)->priv;
1656   GdkWindow *window = gtk_widget_get_window (widget);
1657 
1658   if (gtk_widget_get_realized (widget))
1659     {
1660 #if GTK_CHECK_VERSION(3, 0, 0)
1661       GtkStyleContext *style = gtk_widget_get_style_context (widget);
1662       gtk_style_context_save (style);
1663       gtk_style_context_add_class (style, GTK_STYLE_CLASS_VIEW);
1664       gtk_style_context_set_background (style, priv->bin_window);
1665       gtk_style_context_set_background (style, window);
1666       gtk_style_context_restore (style);
1667 #else
1668       GtkStyle *style = gtk_widget_get_style (widget);
1669       gdk_window_set_background (priv->bin_window, &style->base[gtk_widget_get_state(widget)]);
1670       gdk_window_set_background (window, &style->base[gtk_widget_get_state(widget)]);
1671 #endif
1672     }
1673 
1674   gtk_widget_queue_draw (widget);
1675 }
1676 
1677 
1678 static void
exo_icon_view_size_request(GtkWidget * widget,GtkRequisition * requisition)1679 exo_icon_view_size_request (GtkWidget      *widget,
1680                             GtkRequisition *requisition)
1681 {
1682   const ExoIconViewPrivate *priv = EXO_ICON_VIEW (widget)->priv;
1683   ExoIconViewChild         *child;
1684   GtkRequisition            child_requisition;
1685   GList                    *lp;
1686 
1687   /* well, this is easy */
1688   requisition->width = priv->width;
1689   requisition->height = priv->height;
1690 
1691   /* handle the child widgets */
1692   for (lp = priv->children; lp != NULL; lp = lp->next)
1693     {
1694       child = lp->data;
1695       if (gtk_widget_get_visible (child->widget))
1696 #if GTK_CHECK_VERSION(3, 0, 0)
1697         gtk_widget_get_preferred_size (child->widget, NULL, &child_requisition);
1698 #else
1699         gtk_widget_size_request (child->widget, &child_requisition);
1700 #endif
1701     }
1702 }
1703 
1704 #if GTK_CHECK_VERSION(3, 0, 0)
1705 static void
exo_icon_view_get_preferred_width(GtkWidget * widget,gint * minimal_width,gint * natural_width)1706 exo_icon_view_get_preferred_width (GtkWidget *widget,
1707                                    gint *minimal_width,
1708                                    gint *natural_width)
1709 {
1710   GtkRequisition requisition;
1711 
1712   exo_icon_view_size_request (widget, &requisition);
1713 
1714   *minimal_width = *natural_width = requisition.width;
1715 }
1716 
1717 static void
exo_icon_view_get_preferred_height(GtkWidget * widget,gint * minimal_height,gint * natural_height)1718 exo_icon_view_get_preferred_height (GtkWidget *widget,
1719                                     gint *minimal_height,
1720                                     gint *natural_height)
1721 {
1722   GtkRequisition requisition;
1723 
1724   exo_icon_view_size_request (widget, &requisition);
1725 
1726   *minimal_height = *natural_height = requisition.height;
1727 }
1728 #endif
1729 
1730 
1731 static void
exo_icon_view_allocate_children(ExoIconView * icon_view)1732 exo_icon_view_allocate_children (ExoIconView *icon_view)
1733 {
1734   const ExoIconViewPrivate *priv = icon_view->priv;
1735   const ExoIconViewChild   *child;
1736   GtkAllocation             allocation;
1737   const GList              *lp;
1738   gint                      focus_line_width;
1739   gint                      focus_padding;
1740 
1741   for (lp = priv->children; lp != NULL; lp = lp->next)
1742     {
1743       child = EXO_ICON_VIEW_CHILD (lp->data);
1744 
1745       /* totally ignore our child's requisition */
1746       if (child->cell < 0)
1747         allocation = child->item->area;
1748       else
1749         allocation = child->item->box[child->cell];
1750 
1751       /* increase the item area by focus width/padding */
1752       gtk_widget_style_get (GTK_WIDGET (icon_view), "focus-line-width", &focus_line_width, "focus-padding", &focus_padding, NULL);
1753       allocation.x = MAX (0, allocation.x - (focus_line_width + focus_padding));
1754       allocation.y = MAX (0, allocation.y - (focus_line_width + focus_padding));
1755       allocation.width = MIN (priv->width - allocation.x, allocation.width + 2 * (focus_line_width + focus_padding));
1756       allocation.height = MIN (priv->height - allocation.y, allocation.height + 2 * (focus_line_width + focus_padding));
1757 
1758       /* allocate the area to the child */
1759       gtk_widget_size_allocate (child->widget, &allocation);
1760     }
1761 }
1762 
1763 
1764 
1765 static void
exo_icon_view_size_allocate(GtkWidget * widget,GtkAllocation * allocation)1766 exo_icon_view_size_allocate (GtkWidget     *widget,
1767                              GtkAllocation *allocation)
1768 {
1769   GtkAdjustment *hadjustment;
1770   GtkAdjustment *vadjustment;
1771   ExoIconView   *icon_view = EXO_ICON_VIEW (widget);
1772 
1773   /* FIXME: gtk 3.4.2 calls this with weird values sometimes.
1774      Don't know if that is a bug of gtk or something else.
1775      Since that will break the view a workaround is required. */
1776   if (allocation->x < 0)
1777     allocation->x = 0;
1778   if (allocation->y < 0)
1779     allocation->y = 0;
1780 
1781   /* apply the new size allocation */
1782   gtk_widget_set_allocation (widget, allocation);
1783 
1784   /* move/resize the clipping window, the icons window
1785    * will be handled by exo_icon_view_layout().
1786    */
1787   if (gtk_widget_get_realized (widget))
1788     gdk_window_move_resize (gtk_widget_get_window(widget), allocation->x, allocation->y, allocation->width, allocation->height);
1789 
1790   /* layout the items */
1791   exo_icon_view_layout (icon_view);
1792 
1793   /* allocate space to the widgets (editing) */
1794   exo_icon_view_allocate_children (icon_view);
1795 
1796   /* update the horizontal scroll adjustment accordingly */
1797   hadjustment = icon_view->priv->hadjustment;
1798   gtk_adjustment_set_page_size(hadjustment, allocation->width);
1799   gtk_adjustment_set_page_increment(hadjustment, allocation->width * 0.9);
1800   gtk_adjustment_set_step_increment(hadjustment, allocation->width * 0.1);
1801   gtk_adjustment_set_lower(hadjustment, 0);
1802   gtk_adjustment_set_upper(hadjustment, MAX (allocation->width, icon_view->priv->width));
1803   if (gtk_adjustment_get_value(hadjustment) > gtk_adjustment_get_upper(hadjustment) - gtk_adjustment_get_page_size(hadjustment))
1804     gtk_adjustment_set_value (hadjustment, MAX (0, gtk_adjustment_get_upper(hadjustment) - gtk_adjustment_get_page_size(hadjustment)));
1805 
1806   /* update the vertical scroll adjustment accordingly */
1807   vadjustment = icon_view->priv->vadjustment;
1808   gtk_adjustment_set_page_size(vadjustment, allocation->height);
1809   gtk_adjustment_set_page_increment(vadjustment, allocation->height * 0.9);
1810   gtk_adjustment_set_step_increment(vadjustment, allocation->height * 0.1);
1811   gtk_adjustment_set_lower(vadjustment, 0);
1812   gtk_adjustment_set_upper(vadjustment, MAX (allocation->height, icon_view->priv->height));
1813   if (gtk_adjustment_get_value(vadjustment) > gtk_adjustment_get_upper(vadjustment) - gtk_adjustment_get_page_size(vadjustment))
1814     gtk_adjustment_set_value (vadjustment, MAX (0, gtk_adjustment_get_upper(vadjustment) - gtk_adjustment_get_page_size(vadjustment)));
1815 
1816   /* we need to emit "changed" ourselves */
1817   gtk_adjustment_changed (hadjustment);
1818   gtk_adjustment_changed (vadjustment);
1819 #if GTK_CHECK_VERSION(3, 0, 0)
1820   g_object_notify (G_OBJECT (icon_view), "hadjustment");
1821   g_object_notify (G_OBJECT (icon_view), "vadjustment");
1822 #endif
1823 }
1824 
1825 
1826 
1827 static void
exo_icon_view_style_set(GtkWidget * widget,GtkStyle * previous_style)1828 exo_icon_view_style_set (GtkWidget *widget,
1829                          GtkStyle  *previous_style)
1830 {
1831   ExoIconView *icon_view = EXO_ICON_VIEW (widget);
1832   GtkStyle *style;
1833 
1834   /* let GtkWidget do its work */
1835   (*GTK_WIDGET_CLASS (exo_icon_view_parent_class)->style_set) (widget, previous_style);
1836 
1837   /* apply the new style for the bin_window if we're realized */
1838   style = gtk_widget_get_style (widget);
1839   if (gtk_widget_get_realized (widget))
1840     gdk_window_set_background (icon_view->priv->bin_window, &style->base[gtk_widget_get_state(widget)]);
1841 }
1842 
1843 
1844 
1845 static gboolean
1846 #if GTK_CHECK_VERSION(3, 0, 0)
exo_icon_view_draw(GtkWidget * widget,cairo_t * cr)1847 exo_icon_view_draw (GtkWidget      *widget,
1848                     cairo_t        *cr)
1849 #else
1850 exo_icon_view_expose_event (GtkWidget      *widget,
1851                             GdkEventExpose *event)
1852 #endif
1853 {
1854   ExoIconViewDropPosition dest_pos;
1855   ExoIconViewPrivate     *priv = EXO_ICON_VIEW (widget)->priv;
1856   ExoIconViewItem        *dest_item = NULL;
1857   ExoIconViewItem        *item;
1858   ExoIconView            *icon_view = EXO_ICON_VIEW (widget);
1859   GtkTreePath            *path;
1860   GdkRectangle            rubber_rect;
1861   GdkRectangle            rect;
1862   const GList            *lp;
1863   gint                    dest_index = -1;
1864 #if !GTK_CHECK_VERSION(3, 0, 0)
1865   GdkColor               *fill_color_gdk;
1866   guchar                  fill_color_alpha = 0;
1867   gboolean                rtl;
1868   gint                    event_area_last;
1869   GdkRectangle            event_area;
1870   cairo_t                *cr;
1871   GtkStyle               *style;
1872 
1873   /* verify that the expose happened on the icon window */
1874   if (G_UNLIKELY (event->window != priv->bin_window))
1875     return FALSE;
1876 #else
1877   GtkStyleContext        *style;
1878 
1879   if (!gtk_cairo_should_draw_window (cr, priv->bin_window))
1880     return FALSE;
1881 #endif
1882 
1883   /* don't handle expose if the layout isn't done yet; the layout
1884    * method will schedule a redraw when done.
1885    */
1886   if (G_UNLIKELY (priv->layout_idle_id != 0))
1887     return FALSE;
1888 
1889   /* scroll to the previously remembered path (if any) */
1890   if (G_UNLIKELY (priv->scroll_to_path != NULL))
1891     {
1892       /* grab the path from the reference and invalidate the reference */
1893       path = gtk_tree_row_reference_get_path (priv->scroll_to_path);
1894       gtk_tree_row_reference_free (priv->scroll_to_path);
1895       priv->scroll_to_path = NULL;
1896 
1897       /* check if the reference was still valid */
1898       if (G_LIKELY (path != NULL))
1899         {
1900           /* try to scroll again */
1901           exo_icon_view_scroll_to_path (icon_view, path,
1902                                         priv->scroll_to_use_align,
1903                                         priv->scroll_to_row_align,
1904                                         priv->scroll_to_col_align);
1905 
1906           /* release the path */
1907           gtk_tree_path_free (path);
1908         }
1909     }
1910 
1911   /* check if we need to draw a drag indicator */
1912   exo_icon_view_get_drag_dest_item (icon_view, &path, &dest_pos);
1913   if (G_UNLIKELY (path != NULL))
1914     {
1915       dest_index = gtk_tree_path_get_indices (path)[0];
1916       gtk_tree_path_free (path);
1917     }
1918 
1919 #if GTK_CHECK_VERSION(3, 0, 0)
1920   cairo_save (cr);
1921   gtk_cairo_transform_to_window (cr, widget, priv->bin_window);
1922 #else
1923   rtl = (gtk_widget_get_direction (GTK_WIDGET (icon_view)) == GTK_TEXT_DIR_RTL);
1924   event_area = event->area;
1925 
1926   /* determine the last interesting coordinate (depending on the layout mode) */
1927   event_area_last = (priv->layout_mode == EXO_ICON_VIEW_LAYOUT_ROWS)
1928                   ? event_area.y + event_area.height
1929                   : event_area.x + event_area.width;
1930 #endif
1931 
1932   /* paint all items that are affected by the expose event */
1933   for (lp = priv->items; lp != NULL; lp = lp->next)
1934     {
1935       /* check if this item is in the visible area */
1936       item = EXO_ICON_VIEW_ITEM (lp->data);
1937 #if !GTK_CHECK_VERSION(3, 0, 0)
1938       if (G_LIKELY (priv->layout_mode == EXO_ICON_VIEW_LAYOUT_ROWS))
1939         {
1940           if (item->area.y > event_area_last)
1941             break;
1942           else if (item->area.y + item->area.height < event_area.y)
1943             continue;
1944         }
1945       else if (rtl)
1946         {
1947           if (item->area.x > event_area_last)
1948             continue;
1949           else if (item->area.x + item->area.width < event_area.x)
1950             break;
1951         }
1952       else
1953         {
1954           if (item->area.x > event_area_last)
1955             break;
1956           else if (item->area.x + item->area.width < event_area.x)
1957             continue;
1958         }
1959 
1960       /* check if this item needs an update */
1961       if (G_LIKELY (gdk_region_rect_in (event->region, &item->area) != GDK_OVERLAP_RECTANGLE_OUT))
1962         {
1963           exo_icon_view_paint_item (icon_view, item, &event_area, event->window, item->area.x, item->area.y, TRUE);
1964 #else
1965       cairo_save (cr);
1966       cairo_rectangle (cr, item->area.x, item->area.y, item->area.width, item->area.height);
1967       cairo_clip (cr);
1968 
1969       if (gdk_cairo_get_clip_rectangle (cr, NULL))
1970         {
1971           exo_icon_view_paint_item (icon_view, item, &item->area, cr, item->area.x, item->area.y, TRUE);
1972 #endif
1973           if (G_UNLIKELY (dest_index == item->index))
1974             dest_item = item;
1975         }
1976 #if GTK_CHECK_VERSION(3, 0, 0)
1977       cairo_restore (cr);
1978 #endif
1979     }
1980 
1981   if (G_UNLIKELY (dest_item != NULL || priv->doing_rubberband))
1982 #if GTK_CHECK_VERSION(3, 0, 0)
1983       style = gtk_widget_get_style_context (widget);
1984 #else
1985       style = gtk_widget_get_style (widget);
1986 #endif
1987 
1988   /* draw the drag indicator */
1989   if (G_UNLIKELY (dest_item != NULL))
1990     {
1991       switch (dest_pos)
1992         {
1993         case EXO_ICON_VIEW_DROP_INTO:
1994           rect = dest_item->area;
1995           break;
1996         case GTK_ICON_VIEW_DROP_ABOVE:
1997           rect.x = dest_item->area.x;
1998           rect.y = dest_item->area.y - 1;
1999           rect.width = dest_item->area.width;
2000           rect.height = 2;
2001           break;
2002         case GTK_ICON_VIEW_DROP_LEFT:
2003           rect.x = dest_item->area.x - 1;
2004           rect.y = dest_item->area.y;
2005           rect.width = 2;
2006           rect.height = dest_item->area.height;
2007           break;
2008         case GTK_ICON_VIEW_DROP_BELOW:
2009           rect.x = dest_item->area.x;
2010           rect.y = dest_item->area.y + dest_item->area.height - 1;
2011           rect.width = dest_item->area.width;
2012           rect.height = 2;
2013           break;
2014         case GTK_ICON_VIEW_DROP_RIGHT:
2015           rect.x = dest_item->area.x + dest_item->area.width - 1;
2016           rect.y = dest_item->area.y;
2017           rect.width = 2;
2018           rect.height = dest_item->area.height;
2019         case EXO_ICON_VIEW_NO_DROP:
2020           rect.x = rect.y = rect.width = rect.height = 0;
2021           break;
2022 
2023         default:
2024           g_assert_not_reached ();
2025         }
2026 #if GTK_CHECK_VERSION(3, 0, 0)
2027       gtk_render_focus (style, cr,
2028 #else
2029       gtk_paint_focus (style, priv->bin_window,
2030                        gtk_widget_get_state (widget), NULL, widget,
2031                        "iconview-drop-indicator",
2032 #endif
2033                        rect.x, rect.y, rect.width, rect.height);
2034     }
2035 
2036   /* draw the rubberband border */
2037   if (G_UNLIKELY (priv->doing_rubberband))
2038     {
2039       /* calculate the rubberband area */
2040       rubber_rect.x = MIN (priv->rubberband_x_1, priv->rubberband_x2);
2041       rubber_rect.y = MIN (priv->rubberband_y_1, priv->rubberband_y2);
2042       rubber_rect.width = ABS (priv->rubberband_x_1 - priv->rubberband_x2) + 1;
2043       rubber_rect.height = ABS (priv->rubberband_y_1 - priv->rubberband_y2) + 1;
2044 
2045 #if !GTK_CHECK_VERSION(3, 0, 0)
2046       if (gdk_rectangle_intersect (&rubber_rect, &event_area, &rect))
2047         {
2048           cr = gdk_cairo_create (event->window);
2049           gtk_widget_style_get (widget,
2050                                 "selection-box-color", &fill_color_gdk,
2051                                 "selection-box-alpha", &fill_color_alpha,
2052                                 NULL);
2053           if (!fill_color_gdk)
2054             {
2055               style = gtk_widget_get_style (widget);
2056               fill_color_gdk = gdk_color_copy (&style->base[GTK_STATE_SELECTED]);
2057             }
2058 
2059           /* draw the area */
2060           cairo_set_source_rgba (cr,
2061                                  fill_color_gdk->red / 65535.,
2062                                  fill_color_gdk->green / 65535.,
2063                                  fill_color_gdk->blue / 65535.,
2064                                  fill_color_alpha / 255.);
2065           gdk_cairo_rectangle (cr, &rect);
2066           cairo_clip (cr);
2067           cairo_paint (cr);
2068           /* draw the border */
2069           cairo_set_source_rgb (cr,
2070                                 fill_color_gdk->red / 65535.,
2071                                 fill_color_gdk->green / 65535.,
2072                                 fill_color_gdk->blue / 65535.);
2073           cairo_rectangle (cr, rubber_rect.x + 0.5, rubber_rect.y + 0.5, rubber_rect.width - 1, rubber_rect.height - 1);
2074           cairo_set_line_width (cr, 1);
2075           cairo_stroke (cr);
2076           gdk_color_free (fill_color_gdk);
2077           cairo_destroy (cr);
2078         }
2079 #else
2080       gtk_style_context_save (style);
2081       gtk_style_context_add_class (style, GTK_STYLE_CLASS_RUBBERBAND);
2082 
2083       gdk_cairo_rectangle (cr, &rubber_rect);
2084       cairo_clip (cr);
2085 
2086       gtk_render_background (style, cr,
2087                              rubber_rect.x, rubber_rect.y,
2088                              rubber_rect.width, rubber_rect.height);
2089       gtk_render_frame (style, cr,
2090                         rubber_rect.x, rubber_rect.y,
2091                         rubber_rect.width, rubber_rect.height);
2092 
2093       gtk_style_context_restore (style);
2094 #endif
2095     }
2096 
2097   /* let the GtkContainer forward the expose event to all children */
2098 #if GTK_CHECK_VERSION(3, 0, 0)
2099   cairo_restore (cr);
2100   (*GTK_WIDGET_CLASS (exo_icon_view_parent_class)->draw) (widget, cr);
2101 #else
2102   (*GTK_WIDGET_CLASS (exo_icon_view_parent_class)->expose_event) (widget, event);
2103 #endif
2104 
2105   return FALSE;
2106 }
2107 
2108 
2109 
2110 static gboolean
2111 rubberband_scroll_timeout (gpointer user_data)
2112 {
2113   GtkAdjustment *adjustment;
2114   ExoIconView   *icon_view = EXO_ICON_VIEW (user_data);
2115   gdouble        value;
2116 
2117   /* ensure that source isn't removed yet */
2118   if(g_source_is_destroyed(g_main_current_source()))
2119     {
2120       return FALSE;
2121     }
2122 
2123   /* determine the adjustment for the scroll direction */
2124   adjustment = (icon_view->priv->layout_mode == EXO_ICON_VIEW_LAYOUT_ROWS)
2125              ? icon_view->priv->vadjustment
2126              : icon_view->priv->hadjustment;
2127 
2128   /* determine the new scroll value */
2129   value = MIN (gtk_adjustment_get_value(adjustment) + icon_view->priv->scroll_value_diff, gtk_adjustment_get_upper(adjustment) - gtk_adjustment_get_page_size(adjustment));
2130 
2131   /* apply the new value */
2132   gtk_adjustment_set_value (adjustment, value);
2133 
2134   /* update the rubberband */
2135   exo_icon_view_update_rubberband (icon_view);
2136 
2137   return TRUE;
2138 }
2139 
2140 
2141 static gboolean
2142 exo_icon_view_motion_notify_event (GtkWidget      *widget,
2143                                    GdkEventMotion *event)
2144 {
2145   ExoIconViewItem *item;
2146   ExoIconView     *icon_view = EXO_ICON_VIEW (widget);
2147   GdkCursor       *cursor;
2148   gint             size;
2149   gint             abso;
2150   GtkAllocation    allocation;
2151 
2152   exo_icon_view_maybe_begin_drag (icon_view, event);
2153   gtk_widget_get_allocation (widget, &allocation);
2154 
2155   if (icon_view->priv->doing_rubberband)
2156     {
2157       exo_icon_view_update_rubberband (widget);
2158 
2159       if (icon_view->priv->layout_mode == EXO_ICON_VIEW_LAYOUT_ROWS)
2160         {
2161           abso = event->y - icon_view->priv->height *
2162              (gtk_adjustment_get_value(icon_view->priv->vadjustment) /
2163              (gtk_adjustment_get_upper(icon_view->priv->vadjustment) -
2164               gtk_adjustment_get_lower(icon_view->priv->vadjustment)));
2165 
2166           size = allocation.height;
2167         }
2168       else
2169         {
2170           abso = event->x - icon_view->priv->width *
2171              (gtk_adjustment_get_value(icon_view->priv->hadjustment) /
2172              (gtk_adjustment_get_upper(icon_view->priv->hadjustment) -
2173               gtk_adjustment_get_lower(icon_view->priv->hadjustment)));
2174 
2175           size = allocation.width;
2176         }
2177 
2178       if (abso < 0 || abso > size)
2179         {
2180           if (abso < 0)
2181             icon_view->priv->scroll_value_diff = abso;
2182           else
2183             icon_view->priv->scroll_value_diff = abso - size;
2184           icon_view->priv->event_last_x = event->x;
2185           icon_view->priv->event_last_y = event->y;
2186 
2187           if (icon_view->priv->scroll_timeout_id == 0)
2188             icon_view->priv->scroll_timeout_id = gdk_threads_add_timeout (30, rubberband_scroll_timeout,
2189                                                                 icon_view);
2190         }
2191       else
2192         {
2193           remove_scroll_timeout (icon_view);
2194         }
2195     }
2196   else
2197     {
2198       item = exo_icon_view_get_item_at_coords (icon_view, event->x, event->y, TRUE, NULL);
2199       if (item != icon_view->priv->prelit_item)
2200         {
2201           if (G_LIKELY (icon_view->priv->prelit_item != NULL))
2202             exo_icon_view_queue_draw_item (icon_view, icon_view->priv->prelit_item);
2203           icon_view->priv->prelit_item = item;
2204           if (G_LIKELY (item != NULL))
2205             exo_icon_view_queue_draw_item (icon_view, item);
2206 
2207           /* check if we are in single click mode right now */
2208           if (G_UNLIKELY (icon_view->priv->single_click))
2209             {
2210               /* display a hand cursor when pointer is above an item */
2211               if (G_LIKELY (item != NULL))
2212                 {
2213                   /* hand2 seems to be what we should use */
2214                   cursor = gdk_cursor_new (GDK_HAND2);
2215                   gdk_window_set_cursor (event->window, cursor);
2216                   gdk_cursor_unref (cursor);
2217                 }
2218               else
2219                 {
2220                   /* reset the cursor */
2221                   gdk_window_set_cursor (event->window, NULL);
2222                 }
2223 
2224               /* check if autoselection is enabled */
2225               if (G_LIKELY (icon_view->priv->single_click_timeout > 0))
2226                 {
2227                   /* drop any running timeout */
2228                   if (G_LIKELY (icon_view->priv->single_click_timeout_id != 0))
2229                     g_source_remove (icon_view->priv->single_click_timeout_id);
2230 
2231                   /* remember the current event state */
2232                   icon_view->priv->single_click_timeout_state = event->state;
2233 
2234                   /* schedule a new timeout */
2235                   icon_view->priv->single_click_timeout_id = gdk_threads_add_timeout_full (G_PRIORITY_LOW, icon_view->priv->single_click_timeout,
2236                                                                                  exo_icon_view_single_click_timeout, icon_view,
2237                                                                                  exo_icon_view_single_click_timeout_destroy);
2238                 }
2239             }
2240         }
2241     }
2242 
2243   return TRUE;
2244 }
2245 
2246 
2247 
2248 static void
2249 exo_icon_view_remove (GtkContainer *container,
2250                       GtkWidget    *widget)
2251 {
2252   ExoIconViewChild *child;
2253   ExoIconView      *icon_view = EXO_ICON_VIEW (container);
2254   GList            *lp;
2255 
2256   for (lp = icon_view->priv->children; lp != NULL; lp = lp->next)
2257     {
2258       child = lp->data;
2259       if (G_LIKELY (child->widget == widget))
2260         {
2261           icon_view->priv->children = g_list_delete_link (icon_view->priv->children, lp);
2262           gtk_widget_unparent (widget);
2263           g_slice_free (ExoIconViewChild, child);
2264           return;
2265         }
2266     }
2267 }
2268 
2269 
2270 
2271 static void
2272 exo_icon_view_forall (GtkContainer *container,
2273                       gboolean      include_internals,
2274                       GtkCallback   callback,
2275                       gpointer      callback_data)
2276 {
2277   ExoIconView *icon_view = EXO_ICON_VIEW (container);
2278   GList       *lp;
2279 
2280   for (lp = icon_view->priv->children; lp != NULL; lp = lp->next)
2281     (*callback) (((ExoIconViewChild *) lp->data)->widget, callback_data);
2282 }
2283 
2284 static void
2285 exo_icon_view_item_selected_changed (ExoIconView      *icon_view,
2286                                      ExoIconViewItem  *item)
2287 {
2288   AtkObject *obj;
2289   AtkObject *item_obj;
2290 
2291   obj = gtk_widget_get_accessible (GTK_WIDGET (icon_view));
2292   if (obj != NULL)
2293     {
2294       item_obj = atk_object_ref_accessible_child (obj, item->index);
2295       if (item_obj != NULL)
2296         {
2297           atk_object_notify_state_change (item_obj, ATK_STATE_SELECTED, item->selected);
2298           g_object_unref (item_obj);
2299         }
2300     }
2301 }
2302 
2303 
2304 static void
2305 exo_icon_view_item_activate_cell (ExoIconView         *icon_view,
2306                                   ExoIconViewItem     *item,
2307                                   ExoIconViewCellInfo *info,
2308                                   GdkEvent            *event)
2309 {
2310   GtkCellRendererMode mode;
2311   GdkRectangle        cell_area;
2312   GtkTreePath        *path;
2313   gboolean            visible;
2314   gchar              *path_string;
2315 
2316   exo_icon_view_set_cell_data (icon_view, item);
2317 
2318   g_object_get (G_OBJECT (info->cell), "visible", &visible, "mode", &mode, NULL);
2319 
2320   if (G_UNLIKELY (visible && mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE))
2321     {
2322       exo_icon_view_get_cell_area (icon_view, item, info, &cell_area);
2323 
2324       path = gtk_tree_path_new_from_indices (item->index, -1);
2325       path_string = gtk_tree_path_to_string (path);
2326       gtk_tree_path_free (path);
2327 
2328       gtk_cell_renderer_activate (info->cell, event, GTK_WIDGET (icon_view), path_string, &cell_area, &cell_area, 0);
2329 
2330       g_free (path_string);
2331     }
2332 }
2333 
2334 
2335 
2336 static void
2337 exo_icon_view_put (ExoIconView     *icon_view,
2338                    GtkWidget       *widget,
2339                    ExoIconViewItem *item,
2340                    gint             cell)
2341 {
2342   ExoIconViewChild *child;
2343 
2344   /* allocate the new child */
2345   child = g_slice_new (ExoIconViewChild);
2346   child->widget = widget;
2347   child->item = item;
2348   child->cell = cell;
2349 
2350   /* hook up the child */
2351   icon_view->priv->children = g_list_append (icon_view->priv->children, child);
2352 
2353   /* setup the parent for the child */
2354   if (gtk_widget_get_realized (GTK_WIDGET (icon_view)))
2355     gtk_widget_set_parent_window (child->widget, icon_view->priv->bin_window);
2356   gtk_widget_set_parent (widget, GTK_WIDGET (icon_view));
2357 }
2358 
2359 
2360 
2361 static void
2362 exo_icon_view_remove_widget (GtkCellEditable *editable,
2363                              ExoIconView     *icon_view)
2364 {
2365   ExoIconViewItem *item;
2366   GList           *lp;
2367 
2368   if (G_LIKELY (icon_view->priv->edited_item != NULL))
2369     {
2370       item = icon_view->priv->edited_item;
2371       icon_view->priv->edited_item = NULL;
2372       icon_view->priv->editable = NULL;
2373 
2374       for (lp = icon_view->priv->cell_list; lp != NULL; lp = lp->next)
2375         ((ExoIconViewCellInfo *) lp->data)->editing = FALSE;
2376 
2377       if (gtk_widget_has_focus (GTK_WIDGET (editable)))
2378         gtk_widget_grab_focus (GTK_WIDGET (icon_view));
2379 
2380       g_signal_handlers_disconnect_by_func (editable, exo_icon_view_remove_widget, icon_view);
2381       gtk_container_remove (GTK_CONTAINER (icon_view), GTK_WIDGET (editable));
2382 
2383       exo_icon_view_queue_draw_item (icon_view, item);
2384     }
2385 }
2386 
2387 
2388 
2389 static void
2390 exo_icon_view_start_editing (ExoIconView         *icon_view,
2391                              ExoIconViewItem     *item,
2392                              ExoIconViewCellInfo *info,
2393                              GdkEvent            *event)
2394 {
2395   GtkCellRendererMode mode;
2396   GtkCellEditable    *editable;
2397   GdkRectangle        cell_area;
2398   GtkTreePath        *path;
2399   gboolean            visible;
2400   gchar              *path_string;
2401 
2402   /* setup cell data for the given item */
2403   exo_icon_view_set_cell_data (icon_view, item);
2404 
2405   /* check if the cell is visible and editable (given the updated cell data) */
2406   g_object_get (info->cell, "visible", &visible, "mode", &mode, NULL);
2407   if (G_LIKELY (visible && mode == GTK_CELL_RENDERER_MODE_EDITABLE))
2408     {
2409       /* draw keyboard focus while editing */
2410       EXO_ICON_VIEW_SET_FLAG (icon_view, EXO_ICON_VIEW_DRAW_KEYFOCUS);
2411 
2412       /* determine the cell area */
2413       exo_icon_view_get_cell_area (icon_view, item, info, &cell_area);
2414 
2415       /* determine the tree path */
2416       path = gtk_tree_path_new_from_indices (item->index, -1);
2417       path_string = gtk_tree_path_to_string (path);
2418       gtk_tree_path_free (path);
2419 
2420       /* allocate the editable from the cell renderer */
2421       editable = gtk_cell_renderer_start_editing (info->cell, event, GTK_WIDGET (icon_view), path_string, &cell_area, &cell_area, 0);
2422 
2423       /* ugly hack, but works */
2424       if (g_object_class_find_property (G_OBJECT_GET_CLASS (editable), "has-frame") != NULL)
2425         g_object_set (editable, "has-frame", TRUE, NULL);
2426 
2427       /* setup the editing widget */
2428       icon_view->priv->edited_item = item;
2429       icon_view->priv->editable = editable;
2430       info->editing = TRUE;
2431 
2432       exo_icon_view_put (icon_view, GTK_WIDGET (editable), item, info->position);
2433       gtk_cell_editable_start_editing (GTK_CELL_EDITABLE (editable), (GdkEvent *)event);
2434       gtk_widget_grab_focus (GTK_WIDGET (editable));
2435       g_signal_connect (G_OBJECT (editable), "remove-widget", G_CALLBACK (exo_icon_view_remove_widget), icon_view);
2436 
2437       /* cleanup */
2438       g_free (path_string);
2439     }
2440 }
2441 
2442 
2443 
2444 static void
2445 exo_icon_view_stop_editing (ExoIconView *icon_view,
2446                             gboolean     cancel_editing)
2447 {
2448   ExoIconViewItem *item;
2449   GtkCellRenderer *cell = NULL;
2450   GList           *lp;
2451 
2452   if (icon_view->priv->edited_item == NULL)
2453     return;
2454 
2455   /*
2456    * This is very evil. We need to do this, because
2457    * gtk_cell_editable_editing_done may trigger exo_icon_view_row_changed
2458    * later on. If exo_icon_view_row_changed notices
2459    * icon_view->priv->edited_item != NULL, it'll call
2460    * exo_icon_view_stop_editing again. Bad things will happen then.
2461    *
2462    * Please read that again if you intend to modify anything here.
2463    */
2464 
2465   item = icon_view->priv->edited_item;
2466   icon_view->priv->edited_item = NULL;
2467 
2468   for (lp = icon_view->priv->cell_list; lp != NULL; lp = lp->next)
2469     {
2470       ExoIconViewCellInfo *info = lp->data;
2471       if (info->editing)
2472         {
2473           cell = info->cell;
2474           break;
2475         }
2476     }
2477 
2478   if (G_UNLIKELY (cell == NULL))
2479     return;
2480 
2481   gtk_cell_renderer_stop_editing (cell, cancel_editing);
2482   if (G_LIKELY (!cancel_editing))
2483     gtk_cell_editable_editing_done (icon_view->priv->editable);
2484 
2485   icon_view->priv->edited_item = item;
2486 
2487   gtk_cell_editable_remove_widget (icon_view->priv->editable);
2488 }
2489 
2490 
2491 
2492 static gboolean
2493 exo_icon_view_button_press_event (GtkWidget      *widget,
2494                                   GdkEventButton *event)
2495 {
2496   ExoIconViewCellInfo *info = NULL;
2497   GtkCellRendererMode  mode;
2498   ExoIconViewItem     *item;
2499   ExoIconView         *icon_view;
2500   GtkTreePath         *path;
2501   gboolean             dirty = FALSE;
2502   gint                 cursor_cell;
2503   gpointer             drag_data;
2504 
2505   icon_view = EXO_ICON_VIEW (widget);
2506 
2507   if (event->window != icon_view->priv->bin_window)
2508     return FALSE;
2509 
2510   /* the widget can be destroyed by click so let keep reference on it */
2511   g_object_ref(widget);
2512 
2513   /* stop any pending "single-click-timeout" */
2514   if (G_UNLIKELY (icon_view->priv->single_click_timeout_id != 0))
2515     g_source_remove (icon_view->priv->single_click_timeout_id);
2516 
2517   if (G_UNLIKELY (!gtk_widget_has_focus (widget)))
2518     gtk_widget_grab_focus (widget);
2519 
2520   if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
2521     {
2522       if (G_LIKELY (icon_view->priv->dnd_locked))
2523         {
2524           /* re-enable Gtk+ DnD callbacks if they were disabled by a
2525              double click before, otherwise DnD will not work */
2526           drag_data = g_object_get_data (G_OBJECT (icon_view), I_("gtk-site-data"));
2527           if (G_LIKELY (drag_data != NULL))
2528             {
2529               g_signal_handlers_unblock_matched (G_OBJECT (icon_view),
2530                                                  G_SIGNAL_MATCH_DATA,
2531                                                  0, 0, NULL, NULL,
2532                                                  drag_data);
2533             }
2534           icon_view->priv->dnd_locked = FALSE;
2535         }
2536       item = exo_icon_view_get_item_at_coords (icon_view,
2537                                                event->x, event->y,
2538                                                TRUE,
2539                                                &info);
2540       if (item != NULL)
2541         {
2542           g_object_get (info->cell, "mode", &mode, NULL);
2543 
2544           if (mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE ||
2545               mode == GTK_CELL_RENDERER_MODE_EDITABLE)
2546             cursor_cell = g_list_index (icon_view->priv->cell_list, info);
2547           else
2548             cursor_cell = -1;
2549 
2550           exo_icon_view_scroll_to_item (icon_view, item);
2551 
2552           if (icon_view->priv->selection_mode == GTK_SELECTION_NONE)
2553             {
2554               exo_icon_view_set_cursor_item (icon_view, item, cursor_cell);
2555             }
2556           else if (icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE &&
2557                    (event->state & GDK_SHIFT_MASK))
2558             {
2559               if (!(event->state & GDK_CONTROL_MASK))
2560                 exo_icon_view_unselect_all_internal (icon_view);
2561 
2562               exo_icon_view_set_cursor_item (icon_view, item, cursor_cell);
2563               if (!icon_view->priv->anchor_item)
2564                 icon_view->priv->anchor_item = item;
2565               else
2566                 exo_icon_view_select_all_between (icon_view,
2567                                                   icon_view->priv->anchor_item,
2568                                                   item);
2569               dirty = TRUE;
2570             }
2571           else
2572             {
2573               if ((icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE ||
2574                   ((icon_view->priv->selection_mode == GTK_SELECTION_SINGLE) && item->selected)) &&
2575                   (event->state & GDK_CONTROL_MASK))
2576                 {
2577                   item->selected = !item->selected;
2578                   exo_icon_view_queue_draw_item (icon_view, item);
2579                   dirty = TRUE;
2580                 }
2581               else
2582                 {
2583                   if (!item->selected)
2584                     {
2585                       exo_icon_view_unselect_all_internal (icon_view);
2586 
2587                       item->selected = TRUE;
2588                       exo_icon_view_queue_draw_item (icon_view, item);
2589                       dirty = TRUE;
2590                     }
2591                 }
2592               exo_icon_view_set_cursor_item (icon_view, item, cursor_cell);
2593               icon_view->priv->anchor_item = item;
2594             }
2595 
2596           /* Save press to possibly begin a drag */
2597           if (icon_view->priv->pressed_button < 0)
2598             {
2599               icon_view->priv->pressed_button = event->button;
2600               icon_view->priv->press_start_x = event->x;
2601               icon_view->priv->press_start_y = event->y;
2602             }
2603 
2604           if (G_LIKELY (icon_view->priv->last_single_clicked == NULL))
2605             icon_view->priv->last_single_clicked = item;
2606 
2607           /* cancel the current editing, if it exists */
2608           exo_icon_view_stop_editing (icon_view, TRUE);
2609 
2610           if (mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE)
2611             exo_icon_view_item_activate_cell (icon_view, item, info,
2612                                               (GdkEvent *)event);
2613           else if (mode == GTK_CELL_RENDERER_MODE_EDITABLE)
2614             exo_icon_view_start_editing (icon_view, item, info,
2615                                          (GdkEvent *)event);
2616         }
2617       else
2618         {
2619           /* cancel the current editing, if it exists */
2620           exo_icon_view_stop_editing (icon_view, TRUE);
2621 
2622           if (icon_view->priv->selection_mode != GTK_SELECTION_BROWSE &&
2623               !(event->state & GDK_CONTROL_MASK))
2624             {
2625               dirty = exo_icon_view_unselect_all_internal (icon_view);
2626             }
2627 
2628           if (icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE)
2629             exo_icon_view_start_rubberbanding (icon_view, event->x, event->y);
2630         }
2631     }
2632   else if (event->button == 1 && event->type == GDK_2BUTTON_PRESS)
2633     {
2634       /* ignore double-click events in single-click mode */
2635       if (G_LIKELY (!icon_view->priv->single_click))
2636         {
2637           item = exo_icon_view_get_item_at_coords (icon_view,
2638                                                    event->x, event->y,
2639                                                    TRUE,
2640                                                    NULL);
2641           if (G_LIKELY (item != NULL))
2642             {
2643               path = gtk_tree_path_new_from_indices (item->index, -1);
2644               exo_icon_view_item_activated (icon_view, path);
2645               gtk_tree_path_free (path);
2646               /* bug #3615031: don't start DnD by double click */
2647               if (icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE &&
2648                   gtk_widget_get_realized(widget))
2649                 {
2650                   drag_data = g_object_get_data (G_OBJECT (icon_view), I_("gtk-site-data"));
2651                   if (G_LIKELY (drag_data != NULL))
2652                     {
2653                       g_signal_handlers_block_matched (G_OBJECT (icon_view),
2654                                                        G_SIGNAL_MATCH_DATA,
2655                                                        0, 0, NULL, NULL,
2656                                                        drag_data);
2657                       icon_view->priv->dnd_locked = TRUE;
2658                     }
2659 
2660                 }
2661             }
2662         }
2663 
2664       icon_view->priv->last_single_clicked = NULL;
2665       icon_view->priv->pressed_button = -1;
2666     }
2667 
2668   /* grab focus and stop drawing the keyboard focus indicator on single clicks */
2669   if (G_LIKELY (event->type != GDK_2BUTTON_PRESS && event->type != GDK_3BUTTON_PRESS))
2670     {
2671       if (!gtk_widget_has_focus (GTK_WIDGET (icon_view)))
2672         gtk_widget_grab_focus (GTK_WIDGET (icon_view));
2673       EXO_ICON_VIEW_UNSET_FLAG (icon_view, EXO_ICON_VIEW_DRAW_KEYFOCUS);
2674     }
2675 
2676   if (dirty)
2677     g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
2678 
2679   /* SF bug #929: we have to drop prelit state to drop tooltip, see text renderer */
2680   icon_view->priv->prelit_item = NULL;
2681 
2682   /* release reference that was taken above */
2683   g_object_unref(widget);
2684 
2685   return event->button == 1;
2686 }
2687 
2688 
2689 
2690 static gboolean
2691 exo_icon_view_button_release_event (GtkWidget      *widget,
2692                                     GdkEventButton *event)
2693 {
2694   ExoIconViewItem *item;
2695   ExoIconView     *icon_view = EXO_ICON_VIEW (widget);
2696   GtkTreePath     *path;
2697 
2698   /* the widget can be destroyed by click so let keep reference on it */
2699   g_object_ref(widget);
2700 
2701   if (icon_view->priv->pressed_button == (gint) event->button)
2702     {
2703       /* check if we're in single click mode */
2704       if (G_UNLIKELY (icon_view->priv->single_click && (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == 0))
2705         {
2706           /* determine the item at the mouse coords and check if this is the last single clicked one */
2707           item = exo_icon_view_get_item_at_coords (icon_view, event->x, event->y, TRUE, NULL);
2708           if (G_LIKELY (item != NULL && item == icon_view->priv->last_single_clicked))
2709             {
2710               /* emit an "item-activated" signal for this item */
2711               path = gtk_tree_path_new_from_indices (item->index, -1);
2712               exo_icon_view_item_activated (icon_view, path);
2713               gtk_tree_path_free (path);
2714             }
2715 
2716           /* reset the last single clicked item */
2717           icon_view->priv->last_single_clicked = NULL;
2718         }
2719 
2720       /* reset the pressed_button state */
2721       icon_view->priv->pressed_button = -1;
2722     }
2723 
2724   exo_icon_view_stop_rubberbanding (icon_view);
2725 
2726   remove_scroll_timeout (icon_view);
2727 
2728   /* release reference that was taken above */
2729   g_object_unref(widget);
2730 
2731   return TRUE;
2732 }
2733 
2734 
2735 
2736 static gboolean
2737 exo_icon_view_scroll_event (GtkWidget      *widget,
2738                             GdkEventScroll *event)
2739 {
2740   GtkAdjustment *adjustment;
2741   ExoIconView   *icon_view = EXO_ICON_VIEW (widget);
2742   gdouble        delta;
2743   gdouble        value;
2744 
2745   /* we don't care for scroll events in "rows" layout mode, as
2746    * that's completely handled by GtkScrolledWindow.
2747    */
2748   if (icon_view->priv->layout_mode != EXO_ICON_VIEW_LAYOUT_COLS)
2749     return FALSE;
2750 
2751   /* also, we don't care for anything but Up/Down, as
2752    * everything else will be handled by GtkScrolledWindow.
2753    */
2754   if (event->direction != GDK_SCROLL_UP && event->direction != GDK_SCROLL_DOWN)
2755     return FALSE;
2756 
2757   /* we also don't care for scroll events with Shift/Ctrl/Alt pressed */
2758   if ((event->state & gtk_accelerator_get_default_mod_mask()) != 0)
2759     return FALSE;
2760 
2761   /* determine the horizontal adjustment */
2762   adjustment = icon_view->priv->hadjustment;
2763 
2764   /* determine the scroll delta */
2765   delta = pow (gtk_adjustment_get_page_size(adjustment), 2.0 / 3.0);
2766   delta = (event->direction == GDK_SCROLL_UP) ? -delta : delta;
2767 
2768   /* apply the new adjustment value */
2769   value = CLAMP (gtk_adjustment_get_value(adjustment) + delta, gtk_adjustment_get_lower(adjustment), gtk_adjustment_get_upper(adjustment) - gtk_adjustment_get_page_size(adjustment));
2770   gtk_adjustment_set_value (adjustment, value);
2771 
2772   return TRUE;
2773 }
2774 
2775 
2776 
2777 static gboolean
2778 exo_icon_view_key_press_event (GtkWidget   *widget,
2779                                GdkEventKey *event)
2780 {
2781   ExoIconView *icon_view = EXO_ICON_VIEW (widget);
2782   GdkScreen   *screen;
2783   GdkEvent    *new_event;
2784   gboolean     retval;
2785   gulong       popup_menu_id;
2786   gchar       *new_text;
2787   gchar       *old_text;
2788 
2789   /* let the parent class handle the key bindings and stuff */
2790   if ((*GTK_WIDGET_CLASS (exo_icon_view_parent_class)->key_press_event) (widget, event))
2791     return TRUE;
2792 
2793   /* 'space' keypress should not start search even if there is no selection */
2794   if (G_UNLIKELY (event->keyval == GDK_KEY_space))
2795     return FALSE;
2796 
2797   /* check if typeahead search is enabled */
2798   if (G_UNLIKELY (!icon_view->priv->enable_search))
2799     return FALSE;
2800 
2801   exo_icon_view_search_ensure_directory (icon_view);
2802 
2803   /* make sure the search window is realized */
2804   gtk_widget_realize (icon_view->priv->search_window);
2805 
2806   /* make a copy of the current text */
2807   old_text = gtk_editable_get_chars (GTK_EDITABLE (icon_view->priv->search_entry), 0, -1);
2808 
2809   /* make sure we don't accidently popup the context menu */
2810   popup_menu_id = g_signal_connect (G_OBJECT (icon_view->priv->search_entry), "popup-menu", G_CALLBACK (gtk_true), NULL);
2811 
2812   /* move the search window offscreen */
2813   screen = gtk_widget_get_screen (GTK_WIDGET (icon_view));
2814   gtk_window_move (GTK_WINDOW (icon_view->priv->search_window),
2815                    gdk_screen_get_width (screen) + 1,
2816                    gdk_screen_get_height (screen) + 1);
2817   gtk_widget_show (icon_view->priv->search_window);
2818 
2819   /* allocate a new event to forward */
2820   new_event = gdk_event_copy ((GdkEvent *) event);
2821   g_object_unref (G_OBJECT (new_event->key.window));
2822   new_event->key.window = g_object_ref (G_OBJECT (gtk_widget_get_window (icon_view->priv->search_entry)));
2823 
2824   /* send the event to the search entry. If the "preedit-changed" signal is
2825    * emitted during this event, priv->search_imcontext_changed will be set.
2826    */
2827   icon_view->priv->search_imcontext_changed = FALSE;
2828   retval = gtk_widget_event (icon_view->priv->search_entry, new_event);
2829   gtk_widget_hide (icon_view->priv->search_window);
2830 
2831   /* release the temporary event */
2832   gdk_event_free (new_event);
2833 
2834   /* disconnect the popup menu prevention */
2835   g_signal_handler_disconnect (G_OBJECT (icon_view->priv->search_entry), popup_menu_id);
2836 
2837   /* we check to make sure that the entry tried to handle the,
2838    * and that the text has actually changed.
2839    */
2840   new_text = gtk_editable_get_chars (GTK_EDITABLE (icon_view->priv->search_entry), 0, -1);
2841   retval = retval && (strcmp (new_text, old_text) != 0);
2842   g_free (old_text);
2843   g_free (new_text);
2844 
2845   /* if we're in a preedit or the text was modified */
2846   if (icon_view->priv->search_imcontext_changed || retval)
2847     {
2848       if (exo_icon_view_search_start (icon_view, FALSE))
2849         {
2850           gtk_widget_grab_focus (GTK_WIDGET (icon_view));
2851           return TRUE;
2852         }
2853       else
2854         {
2855           gtk_entry_set_text (GTK_ENTRY (icon_view->priv->search_entry), "");
2856           return FALSE;
2857         }
2858     }
2859 
2860   return FALSE;
2861 }
2862 
2863 
2864 
2865 static gboolean
2866 exo_icon_view_focus_out_event (GtkWidget     *widget,
2867                                GdkEventFocus *event)
2868 {
2869   ExoIconView *icon_view = EXO_ICON_VIEW (widget);
2870 
2871   /* be sure to cancel any single-click timeout */
2872   if (G_UNLIKELY (icon_view->priv->single_click_timeout_id != 0))
2873     g_source_remove (icon_view->priv->single_click_timeout_id);
2874 
2875   /* reset the cursor if we're still realized */
2876   if (G_LIKELY (icon_view->priv->bin_window != NULL))
2877     gdk_window_set_cursor (icon_view->priv->bin_window, NULL);
2878 
2879   /* destroy the interactive search dialog */
2880   if (G_UNLIKELY (icon_view->priv->search_window != NULL))
2881     exo_icon_view_search_dialog_hide (icon_view->priv->search_window, icon_view);
2882 
2883   /* schedule a redraw with the new focus state */
2884   gtk_widget_queue_draw (widget);
2885 
2886   return FALSE;
2887 }
2888 
2889 
2890 
2891 static gboolean
2892 exo_icon_view_leave_notify_event (GtkWidget        *widget,
2893                                   GdkEventCrossing *event)
2894 {
2895   /* reset cursor to default */
2896   if (gtk_widget_get_realized (widget))
2897     gdk_window_set_cursor (gtk_widget_get_window(widget), NULL);
2898 
2899   /* call the parent's leave_notify_event (if any) */
2900   if (GTK_WIDGET_CLASS (exo_icon_view_parent_class)->leave_notify_event != NULL)
2901     return (*GTK_WIDGET_CLASS (exo_icon_view_parent_class)->leave_notify_event) (widget, event);
2902 
2903   /* other signal handlers may be invoked */
2904   return FALSE;
2905 }
2906 
2907 
2908 static void
2909 exo_icon_view_update_rubberband (gpointer data)
2910 {
2911   ExoIconView *icon_view;
2912   gint x, y;
2913   GdkRectangle old_area;
2914   GdkRectangle new_area;
2915   GdkRectangle common;
2916 #if GTK_CHECK_VERSION(3, 0, 0)
2917   cairo_region_t *invalid_region;
2918 #else
2919   GdkRegion *invalid_region;
2920 #endif
2921 
2922   icon_view = EXO_ICON_VIEW (data);
2923 
2924   gdk_window_get_device_position (icon_view->priv->bin_window,
2925                                   gtk_get_current_event_device(), &x, &y, NULL);
2926 
2927   x = MAX (x, 0);
2928   y = MAX (y, 0);
2929 
2930   old_area.x = MIN (icon_view->priv->rubberband_x_1,
2931                     icon_view->priv->rubberband_x2);
2932   old_area.y = MIN (icon_view->priv->rubberband_y_1,
2933                     icon_view->priv->rubberband_y2);
2934   old_area.width = ABS (icon_view->priv->rubberband_x2 -
2935                         icon_view->priv->rubberband_x_1) + 1;
2936   old_area.height = ABS (icon_view->priv->rubberband_y2 -
2937                          icon_view->priv->rubberband_y_1) + 1;
2938 
2939   new_area.x = MIN (icon_view->priv->rubberband_x_1, x);
2940   new_area.y = MIN (icon_view->priv->rubberband_y_1, y);
2941   new_area.width = ABS (x - icon_view->priv->rubberband_x_1) + 1;
2942   new_area.height = ABS (y - icon_view->priv->rubberband_y_1) + 1;
2943 
2944 #if GTK_CHECK_VERSION(3, 0, 0)
2945   invalid_region = cairo_region_create_rectangle (&old_area);
2946   cairo_region_union_rectangle (invalid_region, &new_area);
2947 #else
2948   invalid_region = gdk_region_rectangle (&old_area);
2949   gdk_region_union_with_rect (invalid_region, &new_area);
2950 #endif
2951 
2952   gdk_rectangle_intersect (&old_area, &new_area, &common);
2953   if (common.width > 2 && common.height > 2)
2954     {
2955 #if GTK_CHECK_VERSION(3, 0, 0)
2956       cairo_region_t *common_region;
2957 #else
2958       GdkRegion *common_region;
2959 #endif
2960 
2961        /* make sure the border is invalidated */
2962       common.x += 1;
2963       common.y += 1;
2964       common.width -= 2;
2965       common.height -= 2;
2966 
2967 #if GTK_CHECK_VERSION(3, 0, 0)
2968       common_region = cairo_region_create_rectangle (&common);
2969 
2970       cairo_region_subtract (invalid_region, common_region);
2971       cairo_region_destroy (common_region);
2972 #else
2973       common_region = gdk_region_rectangle (&common);
2974 
2975       gdk_region_subtract (invalid_region, common_region);
2976       gdk_region_destroy (common_region);
2977 #endif
2978     }
2979 
2980   gdk_window_invalidate_region (icon_view->priv->bin_window, invalid_region, TRUE);
2981 
2982 #if GTK_CHECK_VERSION(3, 0, 0)
2983   cairo_region_destroy (invalid_region);
2984 #else
2985   gdk_region_destroy (invalid_region);
2986 #endif
2987 
2988   icon_view->priv->rubberband_x2 = x;
2989   icon_view->priv->rubberband_y2 = y;
2990 
2991   exo_icon_view_update_rubberband_selection (icon_view);
2992 }
2993 
2994 
2995 static void
2996 exo_icon_view_start_rubberbanding (ExoIconView  *icon_view,
2997                                    gint          x,
2998                                    gint          y)
2999 {
3000   gpointer        drag_data;
3001   GList          *items;
3002 
3003   /* be sure to disable any previously active rubberband */
3004   exo_icon_view_stop_rubberbanding (icon_view);
3005 
3006   for (items = icon_view->priv->items; items; items = items->next)
3007     {
3008       ExoIconViewItem *item = items->data;
3009       item->selected_before_rubberbanding = item->selected;
3010     }
3011 
3012   icon_view->priv->rubberband_x_1 = x;
3013   icon_view->priv->rubberband_y_1 = y;
3014   icon_view->priv->rubberband_x2 = x;
3015   icon_view->priv->rubberband_y2 = y;
3016 
3017   icon_view->priv->doing_rubberband = TRUE;
3018 
3019   gtk_grab_add (GTK_WIDGET (icon_view));
3020 
3021   /* be sure to disable Gtk+ DnD callbacks, because else rubberbanding will be interrupted */
3022   drag_data = g_object_get_data (G_OBJECT (icon_view), I_("gtk-site-data"));
3023   if (G_LIKELY (drag_data != NULL))
3024     {
3025       g_signal_handlers_block_matched (G_OBJECT (icon_view),
3026                                        G_SIGNAL_MATCH_DATA,
3027                                        0, 0, NULL, NULL,
3028                                        drag_data);
3029       icon_view->priv->dnd_locked = TRUE;
3030     }
3031 }
3032 
3033 
3034 
3035 static void
3036 exo_icon_view_stop_rubberbanding (ExoIconView *icon_view)
3037 {
3038   gpointer drag_data;
3039 
3040   if (G_LIKELY (icon_view->priv->doing_rubberband))
3041     {
3042       icon_view->priv->doing_rubberband = FALSE;
3043       gtk_grab_remove (GTK_WIDGET (icon_view));
3044       gtk_widget_queue_draw (GTK_WIDGET (icon_view));
3045     }
3046 
3047   if (G_LIKELY (icon_view->priv->dnd_locked))
3048     {
3049       /* re-enable Gtk+ DnD callbacks again */
3050       drag_data = g_object_get_data (G_OBJECT (icon_view), I_("gtk-site-data"));
3051       if (G_LIKELY (drag_data != NULL))
3052         {
3053           g_signal_handlers_unblock_matched (G_OBJECT (icon_view),
3054                                              G_SIGNAL_MATCH_DATA,
3055                                              0, 0, NULL, NULL,
3056                                              drag_data);
3057         }
3058       icon_view->priv->dnd_locked = FALSE;
3059     }
3060 }
3061 
3062 
3063 
3064 static void
3065 exo_icon_view_update_rubberband_selection (ExoIconView *icon_view)
3066 {
3067   ExoIconViewItem *item;
3068   gboolean         selected;
3069   gboolean         changed = FALSE;
3070   gboolean         is_in;
3071   GList           *lp;
3072   gint             x, y;
3073   gint             width;
3074   gint             height;
3075 
3076   /* determine the new rubberband area */
3077   x = MIN (icon_view->priv->rubberband_x_1, icon_view->priv->rubberband_x2);
3078   y = MIN (icon_view->priv->rubberband_y_1, icon_view->priv->rubberband_y2);
3079   width = ABS (icon_view->priv->rubberband_x_1 - icon_view->priv->rubberband_x2);
3080   height = ABS (icon_view->priv->rubberband_y_1 - icon_view->priv->rubberband_y2);
3081 
3082   /* check all items */
3083   for (lp = icon_view->priv->items; lp != NULL; lp = lp->next)
3084     {
3085       item = EXO_ICON_VIEW_ITEM (lp->data);
3086 
3087       is_in = exo_icon_view_item_hit_test (icon_view, item, x, y, width, height);
3088 
3089       selected = is_in ^ item->selected_before_rubberbanding;
3090 
3091       if (G_UNLIKELY (item->selected != selected))
3092         {
3093           changed = TRUE;
3094           item->selected = selected;
3095           exo_icon_view_queue_draw_item (icon_view, item);
3096         }
3097     }
3098 
3099   if (G_LIKELY (changed))
3100     g_signal_emit (G_OBJECT (icon_view), icon_view_signals[SELECTION_CHANGED], 0);
3101 }
3102 
3103 
3104 
3105 static gboolean
3106 exo_icon_view_item_hit_test (ExoIconView      *icon_view,
3107                              ExoIconViewItem  *item,
3108                              gint              x,
3109                              gint              y,
3110                              gint              width,
3111                              gint              height)
3112 {
3113   GList *l;
3114   GdkRectangle box;
3115 
3116   for (l = icon_view->priv->cell_list; l; l = l->next)
3117     {
3118       ExoIconViewCellInfo *info = (ExoIconViewCellInfo *)l->data;
3119 
3120       if (!gtk_cell_renderer_get_visible(info->cell))
3121         continue;
3122 
3123       /* libfm: bug #3390778: item->box isn't allocated yet here! bad design! */
3124       if (!item->box)
3125         continue;
3126 
3127       box = item->box[info->position];
3128 
3129       if (MIN (x + width, box.x + box.width) - MAX (x, box.x) > 0 &&
3130         MIN (y + height, box.y + box.height) - MAX (y, box.y) > 0)
3131         return TRUE;
3132     }
3133 
3134   return FALSE;
3135 }
3136 
3137 
3138 
3139 static gboolean
3140 exo_icon_view_unselect_all_internal (ExoIconView  *icon_view)
3141 {
3142   ExoIconViewItem *item;
3143   gboolean         dirty = FALSE;
3144   GList           *lp;
3145 
3146   if (G_LIKELY (icon_view->priv->selection_mode != GTK_SELECTION_NONE))
3147     {
3148       for (lp = icon_view->priv->items; lp != NULL; lp = lp->next)
3149         {
3150           item = EXO_ICON_VIEW_ITEM (lp->data);
3151           if (item->selected)
3152             {
3153               dirty = TRUE;
3154               item->selected = FALSE;
3155               exo_icon_view_queue_draw_item (icon_view, item);
3156               exo_icon_view_item_selected_changed (icon_view, item);
3157             }
3158         }
3159     }
3160 
3161   return dirty;
3162 }
3163 
3164 
3165 #if !GTK_CHECK_VERSION(3, 0, 0)
3166 static void
3167 exo_icon_view_set_adjustments (ExoIconView   *icon_view,
3168                                GtkAdjustment *hadj,
3169                                GtkAdjustment *vadj)
3170 {
3171   gboolean need_adjust = FALSE;
3172 
3173   if (hadj)
3174     _exo_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
3175   else
3176     hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
3177   if (vadj)
3178     _exo_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
3179   else
3180     vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
3181 
3182   if (icon_view->priv->hadjustment && (icon_view->priv->hadjustment != hadj))
3183     {
3184       g_signal_handlers_disconnect_matched (icon_view->priv->hadjustment, G_SIGNAL_MATCH_DATA,
3185                                            0, 0, NULL, NULL, icon_view);
3186       g_object_unref (icon_view->priv->hadjustment);
3187     }
3188 
3189   if (icon_view->priv->vadjustment && (icon_view->priv->vadjustment != vadj))
3190     {
3191       g_signal_handlers_disconnect_matched (icon_view->priv->vadjustment, G_SIGNAL_MATCH_DATA,
3192                                             0, 0, NULL, NULL, icon_view);
3193       g_object_unref (icon_view->priv->vadjustment);
3194     }
3195 
3196   if (icon_view->priv->hadjustment != hadj)
3197     {
3198       icon_view->priv->hadjustment = hadj;
3199       g_object_ref_sink (icon_view->priv->hadjustment);
3200 
3201       g_signal_connect (icon_view->priv->hadjustment, "value-changed",
3202                         G_CALLBACK (exo_icon_view_adjustment_changed),
3203                         icon_view);
3204       need_adjust = TRUE;
3205     }
3206 
3207   if (icon_view->priv->vadjustment != vadj)
3208     {
3209       icon_view->priv->vadjustment = vadj;
3210       g_object_ref_sink (icon_view->priv->vadjustment);
3211 
3212       g_signal_connect (icon_view->priv->vadjustment, "value-changed",
3213                         G_CALLBACK (exo_icon_view_adjustment_changed),
3214                         icon_view);
3215       need_adjust = TRUE;
3216     }
3217 
3218   if (need_adjust)
3219     exo_icon_view_adjustment_changed (NULL, icon_view);
3220 }
3221 #else
3222 static void
3223 exo_icon_view_set_hadjustment (ExoIconView *icon_view,
3224                                GtkAdjustment *hadj)
3225 {
3226   gboolean need_adjust = FALSE;
3227 
3228   if (hadj)
3229     _exo_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
3230   else
3231     hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
3232 
3233   if (icon_view->priv->hadjustment && (icon_view->priv->hadjustment != hadj))
3234     {
3235       g_signal_handlers_disconnect_matched (icon_view->priv->hadjustment, G_SIGNAL_MATCH_DATA,
3236                                            0, 0, NULL, NULL, icon_view);
3237       g_object_unref (icon_view->priv->hadjustment);
3238     }
3239 
3240   if (icon_view->priv->hadjustment != hadj)
3241     {
3242       icon_view->priv->hadjustment = hadj;
3243       g_object_ref_sink (icon_view->priv->hadjustment);
3244 
3245       g_signal_connect (icon_view->priv->hadjustment, "value-changed",
3246                         G_CALLBACK (exo_icon_view_adjustment_changed),
3247                         icon_view);
3248       need_adjust = TRUE;
3249     }
3250 
3251   if (need_adjust)
3252     exo_icon_view_adjustment_changed (NULL, icon_view);
3253   g_object_notify (G_OBJECT (icon_view), "hadjustment");
3254 }
3255 
3256 
3257 static void
3258 exo_icon_view_set_vadjustment (ExoIconView *icon_view,
3259                                GtkAdjustment *vadj)
3260 {
3261   gboolean need_adjust = FALSE;
3262 
3263   if (vadj)
3264     _exo_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
3265   else
3266     vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
3267 
3268   if (icon_view->priv->vadjustment && (icon_view->priv->vadjustment != vadj))
3269     {
3270       g_signal_handlers_disconnect_matched (icon_view->priv->vadjustment, G_SIGNAL_MATCH_DATA,
3271                                             0, 0, NULL, NULL, icon_view);
3272       g_object_unref (icon_view->priv->vadjustment);
3273     }
3274 
3275   if (icon_view->priv->vadjustment != vadj)
3276     {
3277       icon_view->priv->vadjustment = vadj;
3278       g_object_ref_sink (icon_view->priv->vadjustment);
3279 
3280       g_signal_connect (icon_view->priv->vadjustment, "value-changed",
3281                         G_CALLBACK (exo_icon_view_adjustment_changed),
3282                         icon_view);
3283       need_adjust = TRUE;
3284     }
3285 
3286   if (need_adjust)
3287     exo_icon_view_adjustment_changed (NULL, icon_view);
3288   g_object_notify (G_OBJECT (icon_view), "vadjustment");
3289 }
3290 #endif
3291 
3292 
3293 static void
3294 exo_icon_view_real_select_all (ExoIconView *icon_view)
3295 {
3296   exo_icon_view_select_all (icon_view);
3297 }
3298 
3299 
3300 
3301 static void
3302 exo_icon_view_real_unselect_all (ExoIconView *icon_view)
3303 {
3304   exo_icon_view_unselect_all (icon_view);
3305 }
3306 
3307 
3308 
3309 static void
3310 exo_icon_view_real_select_cursor_item (ExoIconView *icon_view)
3311 {
3312   exo_icon_view_unselect_all (icon_view);
3313 
3314   if (icon_view->priv->cursor_item != NULL)
3315     exo_icon_view_select_item (icon_view, icon_view->priv->cursor_item);
3316 }
3317 
3318 
3319 
3320 static gboolean
3321 exo_icon_view_real_activate_cursor_item (ExoIconView *icon_view)
3322 {
3323   GtkTreePath *path;
3324   GtkCellRendererMode mode;
3325   ExoIconViewCellInfo *info = NULL;
3326 
3327   if (!icon_view->priv->cursor_item)
3328     return FALSE;
3329 
3330   info = g_list_nth_data (icon_view->priv->cell_list,
3331                           icon_view->priv->cursor_cell);
3332 
3333   if (info)
3334     {
3335       g_object_get (info->cell, "mode", &mode, NULL);
3336 
3337       if (mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE)
3338         {
3339           exo_icon_view_item_activate_cell (icon_view,
3340                                             icon_view->priv->cursor_item,
3341                                             info, NULL);
3342           return TRUE;
3343         }
3344       else if (mode == GTK_CELL_RENDERER_MODE_EDITABLE)
3345         {
3346           exo_icon_view_start_editing (icon_view,
3347                                        icon_view->priv->cursor_item,
3348                                        info, NULL);
3349           return TRUE;
3350         }
3351     }
3352 
3353   path = gtk_tree_path_new_from_indices (icon_view->priv->cursor_item->index, -1);
3354   exo_icon_view_item_activated (icon_view, path);
3355   gtk_tree_path_free (path);
3356 
3357   return TRUE;
3358 }
3359 
3360 
3361 
3362 static gboolean
3363 exo_icon_view_real_start_interactive_search (ExoIconView *icon_view)
3364 {
3365   return exo_icon_view_search_start (icon_view, TRUE);
3366 }
3367 
3368 
3369 
3370 static void
3371 exo_icon_view_real_toggle_cursor_item (ExoIconView *icon_view)
3372 {
3373   if (G_LIKELY (icon_view->priv->cursor_item != NULL))
3374     {
3375       switch (icon_view->priv->selection_mode)
3376         {
3377         case GTK_SELECTION_NONE:
3378           break;
3379 
3380         case GTK_SELECTION_BROWSE:
3381           exo_icon_view_select_item (icon_view, icon_view->priv->cursor_item);
3382           break;
3383 
3384         case GTK_SELECTION_SINGLE:
3385           if (icon_view->priv->cursor_item->selected)
3386             exo_icon_view_unselect_item (icon_view, icon_view->priv->cursor_item);
3387           else
3388             exo_icon_view_select_item (icon_view, icon_view->priv->cursor_item);
3389           break;
3390 
3391         case GTK_SELECTION_MULTIPLE:
3392           icon_view->priv->cursor_item->selected = !icon_view->priv->cursor_item->selected;
3393           g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
3394           exo_icon_view_item_selected_changed (icon_view, icon_view->priv->cursor_item);
3395           exo_icon_view_queue_draw_item (icon_view, icon_view->priv->cursor_item);
3396           break;
3397 
3398         default:
3399           g_assert_not_reached ();
3400         }
3401     }
3402 }
3403 
3404 
3405 
3406 static void
3407 exo_icon_view_adjustment_changed (GtkAdjustment *adjustment,
3408                                   ExoIconView   *icon_view)
3409 {
3410   if (gtk_widget_get_realized (GTK_WIDGET(icon_view)))
3411     {
3412       gdk_window_move (icon_view->priv->bin_window, -gtk_adjustment_get_value(icon_view->priv->hadjustment), -gtk_adjustment_get_value(icon_view->priv->vadjustment));
3413 
3414       if (G_UNLIKELY (icon_view->priv->doing_rubberband))
3415         exo_icon_view_update_rubberband (GTK_WIDGET (icon_view));
3416 
3417       gdk_window_process_updates (icon_view->priv->bin_window, TRUE);
3418     }
3419 }
3420 
3421 
3422 static GList*
3423 exo_icon_view_layout_single_row (ExoIconView *icon_view,
3424                                  GList       *first_item,
3425                                  gint         item_width,
3426                                  gint         row,
3427                                  gint        *y,
3428                                  gint        *maximum_width,
3429                                  gint         max_cols)
3430 {
3431   ExoIconViewPrivate *priv = icon_view->priv;
3432   ExoIconViewItem    *item;
3433   gboolean            rtl;
3434   GList              *last_item;
3435   GList              *items = first_item;
3436   gint               *max_width;
3437   gint               *max_height;
3438   gint                focus_width;
3439   gint                current_width;
3440   gint                colspan;
3441   gint                col = 0;
3442   gint                x;
3443   gint                i;
3444   GtkAllocation       allocation;
3445 
3446   rtl = (gtk_widget_get_direction (GTK_WIDGET (icon_view)) == GTK_TEXT_DIR_RTL);
3447   gtk_widget_get_allocation (GTK_WIDGET (icon_view), &allocation);
3448 
3449   max_width = g_newa (gint, priv->n_cells);
3450   max_height = g_newa (gint, priv->n_cells);
3451   for (i = priv->n_cells; --i >= 0; )
3452     {
3453       max_width[i] = 0;
3454       max_height[i] = 0;
3455     }
3456 
3457   gtk_widget_style_get (GTK_WIDGET (icon_view),
3458                         "focus-line-width", &focus_width,
3459                         NULL);
3460 
3461   x = priv->margin + focus_width;
3462   current_width = 2 * (priv->margin + focus_width);
3463 
3464   for (items = first_item; items != NULL; items = items->next)
3465     {
3466       item = EXO_ICON_VIEW_ITEM (items->data);
3467 
3468       exo_icon_view_calculate_item_size (icon_view, item);
3469       colspan = 1 + (item->area.width - 1) / (item_width + priv->column_spacing);
3470 
3471       item->area.width = colspan * item_width + (colspan - 1) * priv->column_spacing;
3472 
3473       current_width += item->area.width + priv->column_spacing + 2 * focus_width;
3474 
3475       if (G_LIKELY (items != first_item))
3476         {
3477           if ((priv->columns <= 0 && current_width > allocation.width) ||
3478               (priv->columns > 0 && col >= priv->columns) ||
3479               (max_cols > 0 && col >= max_cols))
3480             break;
3481         }
3482 
3483       item->area.y = *y + focus_width;
3484       item->area.x = rtl ? allocation.width - item->area.width - x : x;
3485 
3486       x = current_width - (priv->margin + focus_width);
3487 
3488       for (i = 0; i < priv->n_cells; i++)
3489         {
3490           max_width[i] = MAX (max_width[i], item->box[i].width);
3491           max_height[i] = MAX (max_height[i], item->box[i].height);
3492         }
3493 
3494       if (current_width > *maximum_width)
3495         *maximum_width = current_width;
3496 
3497       item->row = row;
3498       item->col = col;
3499 
3500       col += colspan;
3501     }
3502 
3503   last_item = items;
3504 
3505   /* Now go through the row again and align the icons */
3506   for (items = first_item; items != last_item; items = items->next)
3507     {
3508       item = EXO_ICON_VIEW_ITEM (items->data);
3509 
3510       exo_icon_view_calculate_item_size2 (icon_view, item, max_width, max_height);
3511 
3512       /* We may want to readjust the new y coordinate. */
3513       if (item->area.y + item->area.height + focus_width + priv->row_spacing > *y)
3514         *y = item->area.y + item->area.height + focus_width + priv->row_spacing;
3515 
3516       if (G_UNLIKELY (rtl))
3517         item->col = col - 1 - item->col;
3518     }
3519 
3520   return last_item;
3521 }
3522 
3523 
3524 
3525 static GList*
3526 exo_icon_view_layout_single_col (ExoIconView *icon_view,
3527                                  GList       *first_item,
3528                                  gint         item_height,
3529                                  gint         col,
3530                                  gint        *x,
3531                                  gint        *maximum_height,
3532                                  gint         max_rows)
3533 {
3534   ExoIconViewPrivate *priv = icon_view->priv;
3535   ExoIconViewItem    *item;
3536   GList              *items = first_item;
3537   GList              *last_item;
3538   gint               *max_width;
3539   gint               *max_height;
3540   gint                focus_width;
3541   gint                current_height;
3542   gint                rowspan;
3543   gint                row = 0;
3544   gint                y;
3545   gint                i;
3546   GtkAllocation       allocation;
3547 
3548   max_width = g_newa (gint, priv->n_cells);
3549   max_height = g_newa (gint, priv->n_cells);
3550   for (i = priv->n_cells; --i >= 0; )
3551     {
3552       max_width[i] = 0;
3553       max_height[i] = 0;
3554     }
3555 
3556   gtk_widget_style_get (GTK_WIDGET (icon_view),
3557                         "focus-line-width", &focus_width,
3558                         NULL);
3559   gtk_widget_get_allocation (GTK_WIDGET (icon_view), &allocation);
3560 
3561   y = priv->margin + focus_width;
3562   current_height = 2 * (priv->margin + focus_width);
3563 
3564   for (items = first_item; items != NULL; items = items->next)
3565     {
3566       item = EXO_ICON_VIEW_ITEM (items->data);
3567 
3568       exo_icon_view_calculate_item_size (icon_view, item);
3569 
3570       rowspan = 1 + (item->area.height - 1) / (item_height + priv->row_spacing);
3571 
3572       item->area.height = rowspan * item_height + (rowspan - 1) * priv->row_spacing;
3573 
3574       current_height += item->area.height + priv->row_spacing + 2 * focus_width;
3575 
3576       if (G_LIKELY (items != first_item))
3577         {
3578           if (current_height >= allocation.height ||
3579              (max_rows > 0 && row >= max_rows))
3580             break;
3581         }
3582 
3583       item->area.y = y + focus_width;
3584       item->area.x = *x;
3585 
3586       y = current_height - (priv->margin + focus_width);
3587 
3588       for (i = 0; i < priv->n_cells; i++)
3589         {
3590           max_width[i] = MAX (max_width[i], item->box[i].width);
3591           max_height[i] = MAX (max_height[i], item->box[i].height);
3592         }
3593 
3594       if (current_height > *maximum_height)
3595         *maximum_height = current_height;
3596 
3597       item->row = row;
3598       item->col = col;
3599 
3600       row += rowspan;
3601     }
3602 
3603   last_item = items;
3604 
3605   /* Now go through the column again and align the icons */
3606   for (items = first_item; items != last_item; items = items->next)
3607     {
3608       item = EXO_ICON_VIEW_ITEM (items->data);
3609 
3610       exo_icon_view_calculate_item_size2 (icon_view, item, max_width, max_height);
3611 
3612       /* We may want to readjust the new x coordinate. */
3613       if (item->area.x + item->area.width + focus_width + priv->column_spacing > *x)
3614         *x = item->area.x + item->area.width + focus_width + priv->column_spacing;
3615     }
3616 
3617   return last_item;
3618 }
3619 
3620 
3621 
3622 static void
3623 exo_icon_view_set_adjustment_upper (GtkAdjustment *adj,
3624                                     gdouble        upper)
3625 {
3626   if (upper != gtk_adjustment_get_upper(adj))
3627     {
3628       gdouble min = MAX (0.0, upper - gtk_adjustment_get_page_size(adj));
3629       gboolean value_changed = FALSE;
3630 
3631       gtk_adjustment_set_upper(adj, upper);
3632 
3633       if (gtk_adjustment_get_value(adj) > min)
3634         {
3635           gtk_adjustment_set_value(adj, min);
3636           value_changed = TRUE;
3637         }
3638 
3639       gtk_adjustment_changed (adj);
3640 
3641       if (value_changed)
3642         gtk_adjustment_value_changed (adj);
3643     }
3644 }
3645 
3646 
3647 
3648 static gint
3649 exo_icon_view_layout_cols (ExoIconView *icon_view,
3650                            gint         item_height,
3651                            gint        *x,
3652                            gint        *maximum_height,
3653                            gint         max_rows)
3654 {
3655   GList *icons = icon_view->priv->items;
3656   GList *items;
3657   gboolean rtl;
3658   gint   col = 0;
3659   gint   rows = 0;
3660   gint   shift;
3661 
3662   rtl = (gtk_widget_get_direction (GTK_WIDGET (icon_view)) == GTK_TEXT_DIR_RTL);
3663 
3664   shift = icon_view->priv->margin;
3665   *x = shift;
3666 
3667   do
3668     {
3669       items = icons; /* it will be used below */
3670       icons = exo_icon_view_layout_single_col (icon_view, icons,
3671                                                item_height, col,
3672                                                &shift, maximum_height, max_rows);
3673 
3674       if (rtl)
3675       {
3676           GList *items2;
3677           ExoIconViewItem *item;
3678           gint i;
3679 
3680           /* update width */
3681           shift -= icon_view->priv->margin; /* width of the new column */
3682           *x += shift;
3683           /* shift all previous items to right so new column will be left one */
3684           if (items) for (items2 = icon_view->priv->items; items2 != items; items2 = items2->next)
3685           {
3686               item = EXO_ICON_VIEW_ITEM (items2->data);
3687 
3688               item->area.x += shift;
3689               for (i = 0; i < icon_view->priv->n_cells; i++)
3690                   item->box[i].x += shift;
3691           }
3692           /* prepare for the next col */
3693           shift = icon_view->priv->margin;
3694       }
3695       else
3696           *x = shift;
3697       /* count the number of rows in the first column */
3698       if (G_UNLIKELY (col == 0))
3699         {
3700           for (items = icon_view->priv->items, rows = 0; items != icons; items = items->next, ++rows)
3701             ;
3702         }
3703 
3704       col++;
3705     }
3706   while (icons != NULL);
3707 
3708   *x += icon_view->priv->margin;
3709   icon_view->priv->cols = col;
3710 
3711   return rows;
3712 }
3713 
3714 
3715 
3716 static gint
3717 exo_icon_view_layout_rows (ExoIconView *icon_view,
3718                            gint         item_width,
3719                            gint        *y,
3720                            gint        *maximum_width,
3721                            gint         max_cols)
3722 {
3723   GList *icons = icon_view->priv->items;
3724   GList *items;
3725   gint   row = 0;
3726   gint   cols = 0;
3727 
3728   *y = icon_view->priv->margin;
3729 
3730   do
3731     {
3732       icons = exo_icon_view_layout_single_row (icon_view, icons,
3733                                                item_width, row,
3734                                                y, maximum_width, max_cols);
3735 
3736       /* count the number of columns in the first row */
3737       if (G_UNLIKELY (row == 0))
3738         {
3739           for (items = icon_view->priv->items, cols = 0; items != icons; items = items->next, ++cols)
3740             ;
3741         }
3742 
3743       row++;
3744     }
3745   while (icons != NULL);
3746 
3747   *y += icon_view->priv->margin;
3748   icon_view->priv->rows = row;
3749 
3750   return cols;
3751 }
3752 
3753 
3754 
3755 static void
3756 exo_icon_view_layout (ExoIconView *icon_view)
3757 {
3758   ExoIconViewPrivate *priv = icon_view->priv;
3759   ExoIconViewItem    *item;
3760   GList              *icons;
3761   gint                maximum_height = 0;
3762   gint                maximum_width = 0;
3763   gint                item_height;
3764   gint                item_width;
3765   gint                rows, cols;
3766   gint                x, y;
3767   GtkAllocation       allocation;
3768 #if GTK_CHECK_VERSION(2, 20, 0)
3769   GtkRequisition      requisition;
3770 #endif
3771 
3772   /* verify that we still have a valid model */
3773   if (G_UNLIKELY (priv->model == NULL))
3774     return;
3775 
3776   gtk_widget_get_allocation (GTK_WIDGET (icon_view), &allocation);
3777 
3778   /* determine the layout mode */
3779   if (G_LIKELY (priv->layout_mode == EXO_ICON_VIEW_LAYOUT_ROWS))
3780     {
3781       /* calculate item sizes on-demand */
3782       item_width = priv->item_width;
3783       if (item_width < 0)
3784         {
3785           for (icons = priv->items; icons != NULL; icons = icons->next)
3786             {
3787               item = icons->data;
3788               exo_icon_view_calculate_item_size (icon_view, item);
3789               item_width = MAX (item_width, item->area.width);
3790             }
3791         }
3792 
3793       cols = exo_icon_view_layout_rows (icon_view, item_width, &y, &maximum_width, 0);
3794 
3795       /* If, by adding another column, we increase the height of the icon view, thus forcing a
3796        * vertical scrollbar to appear that would prevent the last column from being able to fit,
3797        * we need to relayout the icons with one less column.
3798        */
3799       if (cols == priv->cols + 1 && y > allocation.height &&
3800           priv->height <= allocation.height)
3801         {
3802           cols = exo_icon_view_layout_rows (icon_view, item_width, &y, &maximum_width, priv->cols);
3803         }
3804 
3805       priv->width = maximum_width;
3806       priv->height = y;
3807       priv->cols = cols;
3808     }
3809   else
3810     {
3811       /* calculate item sizes on-demand */
3812       for (icons = priv->items, item_height = 0; icons != NULL; icons = icons->next)
3813         {
3814           item = icons->data;
3815           exo_icon_view_calculate_item_size (icon_view, item);
3816           item_height = MAX (item_height, item->area.height);
3817         }
3818 
3819       rows = exo_icon_view_layout_cols (icon_view, item_height, &x, &maximum_height, 0);
3820 
3821       /* If, by adding another row, we increase the width of the icon view, thus forcing a
3822        * horizontal scrollbar to appear that would prevent the last row from being able to fit,
3823        * we need to relayout the icons with one less row.
3824        */
3825       if (rows == priv->rows + 1 && x > allocation.width &&
3826           priv->width <= allocation.width)
3827         {
3828           rows = exo_icon_view_layout_cols (icon_view, item_height, &x, &maximum_height, priv->rows);
3829         }
3830       else if (x < allocation.width &&
3831                gtk_widget_get_direction (GTK_WIDGET (icon_view)) == GTK_TEXT_DIR_RTL)
3832         {
3833           /* shift items to align right border */
3834           gint shift = allocation.width - x, i;
3835           for (icons = priv->items; icons != NULL; icons = icons->next)
3836           {
3837               item = icons->data;
3838               item->area.x += shift;
3839               for (i = 0; i < icon_view->priv->n_cells; i++)
3840                   item->box[i].x += shift;
3841           }
3842         }
3843 
3844       priv->height = maximum_height;
3845       priv->width = x;
3846       priv->rows = rows;
3847     }
3848 
3849   exo_icon_view_set_adjustment_upper (priv->hadjustment, priv->width);
3850   exo_icon_view_set_adjustment_upper (priv->vadjustment, priv->height);
3851 
3852 #if GTK_CHECK_VERSION(2, 20, 0)
3853   gtk_widget_get_requisition (GTK_WIDGET (icon_view), &requisition);
3854   if (priv->width != requisition.width
3855       || priv->height != requisition.height)
3856 #else
3857   if (priv->width != GTK_WIDGET (icon_view)->requisition.width
3858       || priv->height != GTK_WIDGET (icon_view)->requisition.height)
3859 #endif
3860     gtk_widget_queue_resize_no_redraw (GTK_WIDGET (icon_view));
3861 
3862   if (gtk_widget_get_realized (GTK_WIDGET(icon_view)))
3863     {
3864       gdk_window_resize (priv->bin_window,
3865                          MAX (priv->width, allocation.width),
3866                          MAX (priv->height, allocation.height));
3867     }
3868 
3869   /* drop any pending layout idle source */
3870   if (priv->layout_idle_id != 0)
3871     g_source_remove (priv->layout_idle_id);
3872 
3873   gtk_widget_queue_draw (GTK_WIDGET (icon_view));
3874 }
3875 
3876 
3877 
3878 static void
3879 exo_icon_view_get_cell_area (ExoIconView         *icon_view,
3880                              ExoIconViewItem     *item,
3881                              ExoIconViewCellInfo *info,
3882                              GdkRectangle        *cell_area)
3883 {
3884   if (icon_view->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
3885     {
3886       cell_area->x = item->box[info->position].x - item->before[info->position];
3887       cell_area->y = item->area.y;
3888       cell_area->width = item->box[info->position].width + item->before[info->position] + item->after[info->position];
3889       cell_area->height = item->area.height;
3890     }
3891   else
3892     {
3893       cell_area->x = item->area.x;
3894       cell_area->y = item->box[info->position].y - item->before[info->position];
3895       cell_area->width = item->area.width;
3896       cell_area->height = item->box[info->position].height + item->before[info->position] + item->after[info->position];
3897     }
3898 }
3899 
3900 
3901 
3902 static void
3903 exo_icon_view_calculate_item_size (ExoIconView     *icon_view,
3904                                    ExoIconViewItem *item)
3905 {
3906   ExoIconViewCellInfo *info;
3907   GList               *lp;
3908   gchar               *buffer;
3909 #if GTK_CHECK_VERSION(3, 0, 0)
3910   GtkRequisition       requisition;
3911 #endif
3912 
3913   if (G_LIKELY (item->area.width != -1))
3914     return;
3915 
3916   if (G_UNLIKELY (item->n_cells != icon_view->priv->n_cells))
3917     {
3918       /* apply the new cell size */
3919       item->n_cells = icon_view->priv->n_cells;
3920 
3921       /* release the memory chunk (if any) */
3922       g_free (item->box);
3923 
3924       /* allocate a single memory chunk for box, after and before */
3925       buffer = g_malloc0 (item->n_cells * (sizeof (GdkRectangle) + 2 * sizeof (gint)));
3926 
3927       /* assign the memory */
3928       item->box = (GdkRectangle *) buffer;
3929       item->after = (gint *) (buffer + item->n_cells * sizeof (GdkRectangle));
3930       item->before = item->after + item->n_cells;
3931     }
3932 
3933   exo_icon_view_set_cell_data (icon_view, item);
3934 
3935   item->area.width = 0;
3936   item->area.height = 0;
3937   for (lp = icon_view->priv->cell_list; lp != NULL; lp = lp->next)
3938     {
3939       info = EXO_ICON_VIEW_CELL_INFO (lp->data);
3940       if (G_UNLIKELY (!gtk_cell_renderer_get_visible(info->cell)))
3941         continue;
3942 
3943 #if GTK_CHECK_VERSION(3, 0, 0)
3944       gtk_cell_renderer_get_preferred_size (info->cell, GTK_WIDGET (icon_view),
3945                                             NULL, &requisition);
3946       item->box[info->position].width = requisition.width;
3947       item->box[info->position].height = requisition.height;
3948 #else
3949       gtk_cell_renderer_get_size (info->cell, GTK_WIDGET (icon_view),
3950                                   NULL, NULL, NULL,
3951                                   &item->box[info->position].width,
3952                                   &item->box[info->position].height);
3953 #endif
3954 
3955       if (icon_view->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
3956         {
3957           item->area.width += item->box[info->position].width + (info->position > 0 ? icon_view->priv->spacing : 0);
3958           item->area.height = MAX (item->area.height, item->box[info->position].height);
3959         }
3960       else
3961         {
3962           item->area.width = MAX (item->area.width, item->box[info->position].width);
3963           item->area.height += item->box[info->position].height + (info->position > 0 ? icon_view->priv->spacing : 0);
3964         }
3965     }
3966 }
3967 
3968 
3969 
3970 static void
3971 exo_icon_view_calculate_item_size2 (ExoIconView     *icon_view,
3972                                     ExoIconViewItem *item,
3973                                     gint            *max_width,
3974                                     gint            *max_height)
3975 {
3976   ExoIconViewCellInfo *info;
3977   GdkRectangle        *box;
3978   GdkRectangle         cell_area;
3979   gboolean             rtl;
3980   GList               *lp;
3981   gint                 spacing;
3982   gint                 i, k;
3983   gint                 xpad, ypad;
3984   gfloat               xalign, yalign;
3985 
3986   rtl = (gtk_widget_get_direction (GTK_WIDGET (icon_view)) == GTK_TEXT_DIR_RTL);
3987 
3988   spacing = icon_view->priv->spacing;
3989 
3990   if (G_LIKELY (icon_view->priv->layout_mode == EXO_ICON_VIEW_LAYOUT_ROWS))
3991     {
3992       item->area.height = 0;
3993       for (i = 0; i < icon_view->priv->n_cells; ++i)
3994         {
3995           if (icon_view->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
3996             item->area.height = MAX (item->area.height, max_height[i]);
3997           else
3998             item->area.height += max_height[i] + (i > 0 ? spacing : 0);
3999         }
4000     }
4001   else
4002     {
4003       item->area.width = 0;
4004       for (i = 0; i < icon_view->priv->n_cells; ++i)
4005         {
4006           if (icon_view->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
4007             item->area.width += max_width[i] + (i > 0 ? spacing : 0);
4008           else
4009             item->area.width = MAX (item->area.width, max_width[i]);
4010         }
4011     }
4012 
4013   cell_area.x = item->area.x;
4014   cell_area.y = item->area.y;
4015 
4016   for (k = 0; k < 2; ++k)
4017     {
4018       for (lp = icon_view->priv->cell_list, i = 0; lp != NULL; lp = lp->next, ++i)
4019         {
4020           info = EXO_ICON_VIEW_CELL_INFO (lp->data);
4021           if (G_UNLIKELY (!gtk_cell_renderer_get_visible(info->cell) || info->pack == (k ? GTK_PACK_START : GTK_PACK_END)))
4022             continue;
4023 
4024           if (icon_view->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
4025             {
4026               cell_area.width = item->box[info->position].width;
4027               cell_area.height = item->area.height;
4028             }
4029           else
4030             {
4031               cell_area.width = item->area.width;
4032               cell_area.height = max_height[i];
4033             }
4034 
4035           box = item->box + info->position;
4036           gtk_cell_renderer_get_alignment (info->cell, &xalign, &yalign);
4037           gtk_cell_renderer_get_padding (info->cell, &xpad, &ypad);
4038           box->x = cell_area.x + (rtl ? (1.0 - xalign) : xalign) * (cell_area.width - box->width - (2 * xpad));
4039           box->x = MAX (box->x, 0);
4040           box->y = cell_area.y + yalign * (cell_area.height - box->height - (2 * ypad));
4041           box->y = MAX (box->y, 0);
4042 
4043           if (icon_view->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
4044             {
4045               item->before[info->position] = item->box[info->position].x - cell_area.x;
4046               item->after[info->position] = cell_area.width - item->box[info->position].width - item->before[info->position];
4047               cell_area.x += cell_area.width + spacing;
4048             }
4049           else
4050             {
4051               if (item->box[info->position].width > item->area.width)
4052                 {
4053                   item->area.width = item->box[info->position].width;
4054                   cell_area.width = item->area.width;
4055                 }
4056               item->before[info->position] = item->box[info->position].y - cell_area.y;
4057               item->after[info->position] = cell_area.height - item->box[info->position].height - item->before[info->position];
4058               cell_area.y += cell_area.height + spacing;
4059             }
4060         }
4061     }
4062 
4063   if (G_UNLIKELY (rtl && icon_view->priv->orientation == GTK_ORIENTATION_HORIZONTAL))
4064     {
4065       for (i = 0; i < icon_view->priv->n_cells; i++)
4066         item->box[i].x = item->area.x + item->area.width - (item->box[i].x + item->box[i].width - item->area.x);
4067     }
4068 }
4069 
4070 
4071 
4072 static void
4073 exo_icon_view_invalidate_sizes (ExoIconView *icon_view)
4074 {
4075   GList *lp;
4076 
4077   for (lp = icon_view->priv->items; lp != NULL; lp = lp->next)
4078     EXO_ICON_VIEW_ITEM (lp->data)->area.width = -1;
4079   exo_icon_view_queue_layout (icon_view);
4080 }
4081 
4082 
4083 
4084 static void
4085 exo_icon_view_paint_item (ExoIconView     *icon_view,
4086                           ExoIconViewItem *item,
4087                           GdkRectangle    *area,
4088 #if GTK_CHECK_VERSION(3, 0, 0)
4089                           cairo_t         *drawable,
4090 #else
4091                           GdkDrawable     *drawable,
4092 #endif
4093                           gint             x,
4094                           gint             y,
4095                           gboolean         draw_focus)
4096 {
4097   GtkCellRendererState flags;
4098   ExoIconViewCellInfo *info;
4099   //GtkStateType         state;
4100   GdkRectangle         cell_area;
4101   //gboolean             rtl;
4102   GList               *lp;
4103 
4104   if (G_UNLIKELY (icon_view->priv->model == NULL))
4105     return;
4106 
4107   exo_icon_view_set_cell_data (icon_view, item);
4108 
4109   //rtl = gtk_widget_get_direction (GTK_WIDGET (icon_view)) == GTK_TEXT_DIR_RTL;
4110 
4111   if (item->selected)
4112     {
4113       flags = GTK_CELL_RENDERER_SELECTED;
4114       //state = gtk_widget_has_focus (icon_view) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE;
4115 #if 0
4116       /* FIXME We hardwire background drawing behind text cell renderers
4117        * here. This is ugly, but it's done to be consistent with GtkIconView.
4118        * The additional info->is_text attribute is used for performance
4119        * optimization and should be removed alongside the following code. */
4120 
4121       cr = gdk_cairo_create (drawable);
4122 
4123       for (lp = icon_view->priv->cell_list; lp != NULL; lp = lp->next)
4124         {
4125           info = EXO_ICON_VIEW_CELL_INFO (lp->data);
4126 
4127           if (G_UNLIKELY (!gtk_cell_renderer_get_visible(info->cell)))
4128             continue;
4129 
4130           if (info->is_text)
4131             {
4132               exo_icon_view_get_cell_area (icon_view, item, info, &cell_area);
4133 
4134               x_0 = x - item->area.x + cell_area.x;
4135               y_0 = x - item->area.x + cell_area.y;
4136               x_1 = x_0 + cell_area.width;
4137               y_1 = y_0 + cell_area.height;
4138 
4139               cairo_move_to (cr, x_0 + 5, y_0);
4140               cairo_line_to (cr, x_1 - 5, y_0);
4141               cairo_curve_to (cr, x_1 - 5, y_0, x_1, y_0, x_1, y_0 + 5);
4142               cairo_line_to (cr, x_1, y_1 - 5);
4143               cairo_curve_to (cr, x_1, y_1 - 5, x_1, y_1, x_1 - 5, y_1);
4144               cairo_line_to (cr, x_0 + 5, y_1);
4145               cairo_curve_to (cr, x_0 + 5, y_1, x_0, y_1, x_0, y_1 - 5);
4146               cairo_line_to (cr, x_0, y_0 + 5);
4147               cairo_curve_to (cr, x_0, y_0 + 5, x_0, y_0, x_0 + 5, y_0);
4148 
4149               gdk_cairo_set_source_color (cr, &GTK_WIDGET (icon_view)->style->base[state]);
4150 
4151               cairo_fill (cr);
4152             }
4153         }
4154 
4155       cairo_destroy (cr);
4156 
4157       /* FIXME Ugly code ends here */
4158 #endif
4159     }
4160   else
4161     {
4162       flags = 0;
4163       //state = GTK_STATE_NORMAL;
4164     }
4165 
4166   if (G_UNLIKELY (icon_view->priv->prelit_item == item))
4167     flags |= GTK_CELL_RENDERER_PRELIT;
4168   if (G_UNLIKELY (EXO_ICON_VIEW_FLAG_SET (icon_view, EXO_ICON_VIEW_DRAW_KEYFOCUS) && icon_view->priv->cursor_item == item))
4169     flags |= GTK_CELL_RENDERER_FOCUSED;
4170 
4171 #ifdef DEBUG_ICON_VIEW
4172   gdk_draw_rectangle (drawable,
4173                       GTK_WIDGET (icon_view)->style->black_gc,
4174                       FALSE,
4175                       x, y,
4176                       item->area.width, item->area.height);
4177 #endif
4178 
4179   for (lp = icon_view->priv->cell_list; lp != NULL; lp = lp->next)
4180     {
4181       info = EXO_ICON_VIEW_CELL_INFO (lp->data);
4182 
4183       if (G_UNLIKELY (!gtk_cell_renderer_get_visible(info->cell)))
4184         continue;
4185 
4186       exo_icon_view_get_cell_area (icon_view, item, info, &cell_area);
4187 
4188 #ifdef DEBUG_ICON_VIEW
4189       gdk_draw_rectangle (drawable,
4190                           GTK_WIDGET (icon_view)->style->black_gc,
4191                           FALSE,
4192                           x - item->area.x + cell_area.x,
4193                           y - item->area.y + cell_area.y,
4194                           cell_area.width, cell_area.height);
4195 
4196       gdk_draw_rectangle (drawable,
4197                           GTK_WIDGET (icon_view)->style->black_gc,
4198                           FALSE,
4199                           x - item->area.x + item->box[info->position].x,
4200                           y - item->area.y + item->box[info->position].y,
4201                           item->box[info->position].width, item->box[info->position].height);
4202 #endif
4203 
4204       cell_area.x = x - item->area.x + cell_area.x;
4205       cell_area.y = y - item->area.y + cell_area.y;
4206 
4207       gtk_cell_renderer_render (info->cell,
4208                                 drawable,
4209                                 GTK_WIDGET (icon_view),
4210                                 &cell_area, &cell_area,
4211 #if !GTK_CHECK_VERSION(3, 0, 0)
4212                                 area,
4213 #endif
4214                                 flags);
4215 
4216     }
4217 }
4218 
4219 
4220 
4221 static void
4222 exo_icon_view_queue_draw_item (ExoIconView     *icon_view,
4223                                ExoIconViewItem *item)
4224 {
4225   GdkRectangle rect;
4226   gint         focus_width;
4227 
4228   gtk_widget_style_get (GTK_WIDGET (icon_view),
4229                         "focus-line-width", &focus_width,
4230                         NULL);
4231 
4232   rect.x = item->area.x - focus_width;
4233   rect.y = item->area.y - focus_width;
4234   rect.width = item->area.width + 2 * focus_width;
4235   rect.height = item->area.height + 2 * focus_width;
4236 
4237   if (icon_view->priv->bin_window)
4238     gdk_window_invalidate_rect (icon_view->priv->bin_window, &rect, TRUE);
4239 }
4240 
4241 
4242 
4243 static gboolean
4244 layout_callback (gpointer user_data)
4245 {
4246   ExoIconView *icon_view = EXO_ICON_VIEW (user_data);
4247 
4248   if(!g_source_is_destroyed(g_main_current_source()))
4249     exo_icon_view_layout (icon_view);
4250 
4251   return FALSE;
4252 }
4253 
4254 
4255 
4256 static void
4257 layout_destroy (gpointer user_data)
4258 {
4259   EXO_ICON_VIEW (user_data)->priv->layout_idle_id = 0;
4260 }
4261 
4262 
4263 
4264 static void
4265 exo_icon_view_queue_layout (ExoIconView *icon_view)
4266 {
4267   if (G_UNLIKELY (icon_view->priv->layout_idle_id == 0))
4268     icon_view->priv->layout_idle_id = gdk_threads_add_idle_full (G_PRIORITY_DEFAULT_IDLE, layout_callback, icon_view, layout_destroy);
4269 }
4270 
4271 
4272 
4273 static void
4274 exo_icon_view_set_cursor_item (ExoIconView     *icon_view,
4275                                ExoIconViewItem *item,
4276                                gint             cursor_cell)
4277 {
4278   AtkObject *obj;
4279   AtkObject *item_obj;
4280   AtkObject *cursor_item_obj;
4281 
4282   /* When hitting this path from keynav, the focus cell is
4283    * already set, we dont need to notify the atk object
4284    * but we still need to queue the draw here (in the case
4285    * that the focus cell changes but not the cursor item).
4286    */
4287   exo_icon_view_queue_draw_item (icon_view, item);
4288 
4289   if (icon_view->priv->cursor_item == item &&
4290       (cursor_cell < 0 || cursor_cell == icon_view->priv->cursor_cell))
4291     return;
4292 
4293   obj = gtk_widget_get_accessible (GTK_WIDGET (icon_view));
4294   if (icon_view->priv->cursor_item != NULL)
4295     {
4296       exo_icon_view_queue_draw_item (icon_view, icon_view->priv->cursor_item);
4297       if (obj != NULL)
4298         {
4299           cursor_item_obj = atk_object_ref_accessible_child (obj, icon_view->priv->cursor_item->index);
4300           if (cursor_item_obj != NULL)
4301             atk_object_notify_state_change (cursor_item_obj, ATK_STATE_FOCUSED, FALSE);
4302         }
4303     }
4304   icon_view->priv->cursor_item = item;
4305   if (cursor_cell >= 0)
4306     icon_view->priv->cursor_cell = cursor_cell;
4307 
4308   /* Notify that accessible focus object has changed */
4309   item_obj = atk_object_ref_accessible_child (obj, item->index);
4310 
4311   if (item_obj != NULL)
4312     {
4313 #if !ATK_CHECK_VERSION(2, 9, 4)
4314       atk_focus_tracker_notify (item_obj);
4315 #endif
4316       atk_object_notify_state_change (item_obj, ATK_STATE_FOCUSED, TRUE);
4317       g_object_unref (item_obj);
4318     }
4319 }
4320 
4321 
4322 
4323 static ExoIconViewItem*
4324 exo_icon_view_get_item_at_coords (const ExoIconView    *icon_view,
4325                                   gint                  x,
4326                                   gint                  y,
4327                                   gboolean              only_in_cell,
4328                                   ExoIconViewCellInfo **cell_at_pos)
4329 {
4330   const ExoIconViewPrivate *priv = icon_view->priv;
4331   ExoIconViewCellInfo      *info;
4332   ExoIconViewItem          *item;
4333   GdkRectangle              box;
4334   const GList              *items;
4335   const GList              *lp;
4336 
4337   for (items = priv->items; items != NULL; items = items->next)
4338     {
4339       item = items->data;
4340       if (x >= item->area.x - priv->row_spacing / 2 && x <= item->area.x + item->area.width + priv->row_spacing / 2 &&
4341           y >= item->area.y - priv->column_spacing / 2 && y <= item->area.y + item->area.height + priv->column_spacing / 2)
4342         {
4343           if (only_in_cell || cell_at_pos)
4344             {
4345               exo_icon_view_set_cell_data (icon_view, item);
4346               for (lp = priv->cell_list; lp != NULL; lp = lp->next)
4347                 {
4348                   /* check if the cell is visible */
4349                   info = (ExoIconViewCellInfo *) lp->data;
4350                   if (!gtk_cell_renderer_get_visible(info->cell))
4351                     continue;
4352 
4353                   box = item->box[info->position];
4354                   if ((x >= box.x && x <= box.x + box.width &&
4355                        y >= box.y && y <= box.y + box.height) ||
4356                       (x >= box.x  &&
4357                        x <= box.x + box.width &&
4358                        y >= box.y &&
4359                        y <= box.y + box.height))
4360                     {
4361                       if (cell_at_pos != NULL)
4362                         *cell_at_pos = info;
4363 
4364                       return item;
4365                     }
4366                 }
4367 
4368               if (only_in_cell)
4369                 return NULL;
4370 
4371               if (cell_at_pos != NULL)
4372                 *cell_at_pos = NULL;
4373             }
4374 
4375           return item;
4376         }
4377     }
4378 
4379   return NULL;
4380 }
4381 
4382 
4383 
4384 static void
4385 exo_icon_view_select_item (ExoIconView      *icon_view,
4386                            ExoIconViewItem  *item)
4387 {
4388   if (item->selected || icon_view->priv->selection_mode == GTK_SELECTION_NONE)
4389     return;
4390   else if (icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
4391     exo_icon_view_unselect_all_internal (icon_view);
4392 
4393   item->selected = TRUE;
4394 
4395   exo_icon_view_queue_draw_item (icon_view, item);
4396 
4397   exo_icon_view_item_selected_changed (icon_view, item);
4398   g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
4399 }
4400 
4401 
4402 
4403 static void
4404 exo_icon_view_unselect_item (ExoIconView      *icon_view,
4405                              ExoIconViewItem  *item)
4406 {
4407   if (!item->selected)
4408     return;
4409 
4410   if (icon_view->priv->selection_mode == GTK_SELECTION_NONE ||
4411       icon_view->priv->selection_mode == GTK_SELECTION_BROWSE)
4412     return;
4413 
4414   item->selected = FALSE;
4415 
4416   exo_icon_view_item_selected_changed (icon_view, item);
4417   g_signal_emit (G_OBJECT (icon_view), icon_view_signals[SELECTION_CHANGED], 0);
4418 
4419   exo_icon_view_queue_draw_item (icon_view, item);
4420 }
4421 
4422 
4423 static void
4424 verify_items (ExoIconView *icon_view)
4425 {
4426   GList *items;
4427   int i = 0;
4428 
4429   for (items = icon_view->priv->items; items; items = items->next)
4430     {
4431       ExoIconViewItem *item = items->data;
4432 
4433       if (item->index != i)
4434         g_error ("List item does not match its index: "
4435                  "item index %d and list index %d\n", item->index, i);
4436 
4437       i++;
4438     }
4439 }
4440 
4441 
4442 static void
4443 exo_icon_view_row_changed (GtkTreeModel *model,
4444                            GtkTreePath  *path,
4445                            GtkTreeIter  *iter,
4446                            ExoIconView  *icon_view)
4447 {
4448   ExoIconViewItem *item;
4449 
4450   item = g_list_nth_data (icon_view->priv->items, gtk_tree_path_get_indices(path)[0]);
4451 
4452   /* stop editing this item */
4453   if (G_UNLIKELY (item == icon_view->priv->edited_item))
4454     exo_icon_view_stop_editing (icon_view, TRUE);
4455 
4456   /* emit "selection-changed" if the item is selected */
4457   if (G_UNLIKELY (item->selected))
4458     g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
4459 
4460   /* recalculate layout (a value of -1 for width
4461    * indicates that the item needs to be layouted).
4462    */
4463   item->area.width = -1;
4464   exo_icon_view_queue_layout (icon_view);
4465   verify_items (icon_view);
4466 }
4467 
4468 
4469 
4470 static void
4471 exo_icon_view_row_inserted (GtkTreeModel *model,
4472                             GtkTreePath  *path,
4473                             GtkTreeIter  *iter,
4474                             ExoIconView  *icon_view)
4475 {
4476   ExoIconViewItem *item;
4477   GList           *list;
4478   gint             idx;
4479 
4480   idx = gtk_tree_path_get_indices (path)[0];
4481 
4482   /* allocate the new item */
4483   item = g_slice_new0 (ExoIconViewItem);
4484   item->iter = *iter;
4485   item->area.width = -1;
4486   item->index = idx;
4487   icon_view->priv->items = g_list_insert (icon_view->priv->items, item, idx);
4488 
4489   list = g_list_nth (icon_view->priv->items, idx + 1);
4490   for (; list; list = list->next)
4491     {
4492       item = list->data;
4493 
4494       item->index++;
4495     }
4496   verify_items (icon_view);
4497 
4498   /* recalculate the layout */
4499   exo_icon_view_queue_layout (icon_view);
4500 }
4501 
4502 
4503 
4504 static void
4505 exo_icon_view_row_deleted (GtkTreeModel *model,
4506                            GtkTreePath  *path,
4507                            ExoIconView  *icon_view)
4508 {
4509   ExoIconViewItem *item;
4510   gboolean         changed = FALSE;
4511   GList           *list, *next;
4512 
4513   /* determine the position and the item for the path */
4514   list = g_list_nth (icon_view->priv->items, gtk_tree_path_get_indices (path)[0]);
4515   next = list->next;
4516   item = list->data;
4517 
4518   if (G_UNLIKELY (item == icon_view->priv->edited_item))
4519     exo_icon_view_stop_editing (icon_view, TRUE);
4520 
4521   /* use the next item (if any) as anchor, else use prev, otherwise reset anchor */
4522   if (G_UNLIKELY (item == icon_view->priv->anchor_item))
4523     icon_view->priv->anchor_item = (list->next != NULL) ? list->next->data : ((list->prev != NULL) ? list->prev->data : NULL);
4524 
4525   /* use the next item (if any) as cursor, else use prev, otherwise reset cursor */
4526   if (G_UNLIKELY (item == icon_view->priv->cursor_item))
4527     icon_view->priv->cursor_item = (list->next != NULL) ? list->next->data : ((list->prev != NULL) ? list->prev->data : NULL);
4528 
4529   if (G_UNLIKELY (item == icon_view->priv->prelit_item))
4530     {
4531       /* reset the prelit item */
4532       icon_view->priv->prelit_item = NULL;
4533 
4534       /* cancel any pending single click timer */
4535       if (G_UNLIKELY (icon_view->priv->single_click_timeout_id != 0))
4536         g_source_remove (icon_view->priv->single_click_timeout_id);
4537 
4538       /* in single click mode, we also reset the cursor when realized */
4539       if (G_UNLIKELY (icon_view->priv->single_click && gtk_widget_get_realized (GTK_WIDGET(icon_view))))
4540         gdk_window_set_cursor (icon_view->priv->bin_window, NULL);
4541     }
4542 
4543   /* check if the selection changed */
4544   if (G_UNLIKELY (item->selected))
4545     changed = TRUE;
4546 
4547   /* release the item resources */
4548   g_free (item->box);
4549 
4550   /* drop the item from the list */
4551   icon_view->priv->items = g_list_delete_link (icon_view->priv->items, list);
4552 
4553   /* release the item */
4554   g_slice_free (ExoIconViewItem, item);
4555 
4556   /* update indices */
4557   for (; next; next = next->next)
4558     {
4559       item = next->data;
4560 
4561       item->index--;
4562     }
4563   verify_items (icon_view);
4564 
4565   /* recalculate the layout */
4566   exo_icon_view_queue_layout (icon_view);
4567 
4568   /* if we removed a previous selected item, we need
4569    * to tell others that we have a new selection.
4570    */
4571   if (G_UNLIKELY (changed))
4572     g_signal_emit (G_OBJECT (icon_view), icon_view_signals[SELECTION_CHANGED], 0);
4573 }
4574 
4575 
4576 
4577 static void
4578 exo_icon_view_rows_reordered (GtkTreeModel *model,
4579                               GtkTreePath  *parent,
4580                               GtkTreeIter  *iter,
4581                               gint         *new_order,
4582                               ExoIconView  *icon_view)
4583 {
4584   GList **list_array;
4585   GList  *list;
4586   gint   *order;
4587   gint     length;
4588   gint     i;
4589 
4590   /* cancel any editing attempt */
4591   exo_icon_view_stop_editing (icon_view, TRUE);
4592 
4593   /* determine the number of items to reorder */
4594   length = gtk_tree_model_iter_n_children (model, NULL);
4595   if (G_UNLIKELY (length == 0))
4596     return;
4597 
4598   list_array = g_newa (GList *, length);
4599   order = g_newa (gint, length);
4600 
4601   for (i = 0; i < length; i++)
4602     order[new_order[i]] = i;
4603 
4604   for (i = 0, list = icon_view->priv->items; list != NULL; list = list->next, i++)
4605     list_array[order[i]] = list;
4606 
4607   /* hook up the first item */
4608   icon_view->priv->items = list_array[0];
4609   list_array[0]->prev = NULL;
4610   ((ExoIconViewItem*)list_array[0]->data)->index = 0;
4611 
4612   /* hook up the remaining items */
4613   for (i = 1; i < length; ++i)
4614     {
4615       ((ExoIconViewItem*)list_array[i]->data)->index = i;
4616       list_array[i - 1]->next = list_array[i];
4617       list_array[i]->prev = list_array[i - 1];
4618     }
4619 
4620   /* hook up the last item */
4621   list_array[length - 1]->next = NULL;
4622 
4623   exo_icon_view_queue_layout (icon_view);
4624   verify_items (icon_view);
4625 }
4626 
4627 
4628 
4629 static void
4630 exo_icon_view_add_move_binding (GtkBindingSet  *binding_set,
4631                                 guint           keyval,
4632                                 guint           modmask,
4633                                 GtkMovementStep step,
4634                                 gint            count)
4635 {
4636 
4637   gtk_binding_entry_add_signal (binding_set, keyval, modmask, "move-cursor", 2, G_TYPE_ENUM, step, G_TYPE_INT, count);
4638 
4639   /* skip shift+n and shift+p because this blocks type-ahead search.
4640    * see http://bugzilla.xfce.org/show_bug.cgi?id=4633
4641    */
4642   if (G_LIKELY (keyval != GDK_KEY_p && keyval != GDK_KEY_n))
4643     gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK, "move-cursor", 2, G_TYPE_ENUM, step, G_TYPE_INT, count);
4644 
4645   if ((modmask & GDK_CONTROL_MASK) != GDK_CONTROL_MASK)
4646     {
4647       gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "move-cursor", 2, G_TYPE_ENUM, step, G_TYPE_INT, count);
4648       gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK, "move-cursor", 2, G_TYPE_ENUM, step, G_TYPE_INT, count);
4649     }
4650 }
4651 
4652 
4653 
4654 static gboolean
4655 exo_icon_view_real_move_cursor (ExoIconView     *icon_view,
4656                                 GtkMovementStep  step,
4657                                 gint             count)
4658 {
4659   GdkModifierType state;
4660 
4661   _exo_return_val_if_fail (EXO_ICON_VIEW (icon_view), FALSE);
4662   _exo_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS ||
4663                            step == GTK_MOVEMENT_VISUAL_POSITIONS ||
4664                            step == GTK_MOVEMENT_DISPLAY_LINES ||
4665                            step == GTK_MOVEMENT_PAGES ||
4666                            step == GTK_MOVEMENT_BUFFER_ENDS, FALSE);
4667 
4668   if (!gtk_widget_has_focus (GTK_WIDGET (icon_view)))
4669     return FALSE;
4670 
4671   exo_icon_view_stop_editing (icon_view, FALSE);
4672   EXO_ICON_VIEW_SET_FLAG (icon_view, EXO_ICON_VIEW_DRAW_KEYFOCUS);
4673   gtk_widget_grab_focus (GTK_WIDGET (icon_view));
4674 
4675   if (gtk_get_current_event_state (&state))
4676     {
4677       if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
4678         icon_view->priv->ctrl_pressed = TRUE;
4679       if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
4680         icon_view->priv->shift_pressed = TRUE;
4681     }
4682   /* else we assume not pressed */
4683 
4684   switch (step)
4685     {
4686     case GTK_MOVEMENT_LOGICAL_POSITIONS:
4687     case GTK_MOVEMENT_VISUAL_POSITIONS:
4688       exo_icon_view_move_cursor_left_right (icon_view, count);
4689       break;
4690     case GTK_MOVEMENT_DISPLAY_LINES:
4691       exo_icon_view_move_cursor_up_down (icon_view, count);
4692       break;
4693     case GTK_MOVEMENT_PAGES:
4694       exo_icon_view_move_cursor_page_up_down (icon_view, count);
4695       break;
4696     case GTK_MOVEMENT_BUFFER_ENDS:
4697       exo_icon_view_move_cursor_start_end (icon_view, count);
4698       break;
4699     default:
4700       g_assert_not_reached ();
4701     }
4702 
4703   icon_view->priv->ctrl_pressed = FALSE;
4704   icon_view->priv->shift_pressed = FALSE;
4705 
4706   return TRUE;
4707 }
4708 
4709 
4710 
4711 static gint
4712 find_cell (ExoIconView     *icon_view,
4713            ExoIconViewItem *item,
4714            gint             cell,
4715            GtkOrientation   orientation,
4716            gint             step,
4717            gint            *count)
4718 {
4719   gint n_focusable;
4720   gint *focusable;
4721   gint first_text;
4722   gint current;
4723   gint i, k;
4724   GList *l;
4725 
4726   if (icon_view->priv->orientation != orientation)
4727     return cell;
4728 
4729   exo_icon_view_set_cell_data (icon_view, item);
4730 
4731   focusable = g_new0 (gint, icon_view->priv->n_cells);
4732   n_focusable = 0;
4733 
4734   first_text = 0;
4735   current = 0;
4736   for (k = 0; k < 2; k++)
4737     for (l = icon_view->priv->cell_list, i = 0; l; l = l->next, i++)
4738       {
4739         ExoIconViewCellInfo *info = (ExoIconViewCellInfo *)l->data;
4740         GtkCellRendererMode mode;
4741 
4742         if (info->pack == (k ? GTK_PACK_START : GTK_PACK_END))
4743           continue;
4744 
4745         if (!gtk_cell_renderer_get_visible(info->cell))
4746           continue;
4747 
4748         if (GTK_IS_CELL_RENDERER_TEXT (info->cell))
4749           first_text = i;
4750 
4751         g_object_get(info->cell, "mode", &mode, NULL);
4752         if (mode != GTK_CELL_RENDERER_MODE_INERT)
4753           {
4754             if (cell == i)
4755               current = n_focusable;
4756 
4757             focusable[n_focusable] = i;
4758 
4759             n_focusable++;
4760           }
4761       }
4762 
4763   if (n_focusable == 0)
4764     focusable[n_focusable++] = first_text;
4765 
4766   if (cell < 0)
4767     {
4768       current = step > 0 ? 0 : n_focusable - 1;
4769       cell = focusable[current];
4770     }
4771 
4772   if (current + *count < 0)
4773     {
4774       cell = -1;
4775       *count = current + *count;
4776     }
4777   else if (current + *count > n_focusable - 1)
4778     {
4779       cell = -1;
4780       *count = current + *count - (n_focusable - 1);
4781     }
4782   else
4783     {
4784       cell = focusable[current + *count];
4785       *count = 0;
4786     }
4787 
4788   g_free (focusable);
4789 
4790   return cell;
4791 }
4792 
4793 
4794 
4795 static ExoIconViewItem *
4796 find_item_page_up_down (ExoIconView     *icon_view,
4797                         ExoIconViewItem *current,
4798                         gint             count)
4799 {
4800   GList *item = g_list_find (icon_view->priv->items, current);
4801   GList *next;
4802   gint   col = current->col;
4803   gint   y = current->area.y + count * gtk_adjustment_get_page_size(icon_view->priv->vadjustment);
4804 
4805   if (count > 0)
4806     {
4807       for (; item != NULL; item = item->next)
4808         {
4809           for (next = item->next; next; next = next->next)
4810             if (EXO_ICON_VIEW_ITEM (next->data)->col == col)
4811               break;
4812 
4813           if (next == NULL || EXO_ICON_VIEW_ITEM (next->data)->area.y > y)
4814             break;
4815         }
4816     }
4817   else
4818     {
4819       for (; item != NULL; item = item->prev)
4820         {
4821           for (next = item->prev; next; next = next->prev)
4822             if (EXO_ICON_VIEW_ITEM (next->data)->col == col)
4823               break;
4824 
4825           if (next == NULL || EXO_ICON_VIEW_ITEM (next->data)->area.y < y)
4826             break;
4827         }
4828     }
4829 
4830   return (item != NULL) ? item->data : NULL;
4831 }
4832 
4833 
4834 
4835 static gboolean
4836 exo_icon_view_select_all_between (ExoIconView     *icon_view,
4837                                   ExoIconViewItem *anchor,
4838                                   ExoIconViewItem *cursor)
4839 {
4840   GList *items;
4841   ExoIconViewItem *item, *last;
4842   gboolean dirty = FALSE;
4843 
4844   for (items = icon_view->priv->items; items; items = items->next)
4845     {
4846       item = items->data;
4847 
4848       if (item == anchor)
4849         {
4850           last = cursor;
4851           break;
4852         }
4853       else if (item == cursor)
4854         {
4855           last = anchor;
4856           break;
4857         }
4858     }
4859 
4860   for (; items; items = items->next)
4861     {
4862       item = items->data;
4863 
4864       if (!item->selected)
4865       {
4866         dirty = TRUE;
4867         item->selected = TRUE;
4868         exo_icon_view_item_selected_changed (icon_view, item);
4869       }
4870 
4871       exo_icon_view_queue_draw_item (icon_view, item);
4872 
4873       if (item == last)
4874         break;
4875     }
4876 
4877   return dirty;
4878 }
4879 
4880 
4881 
4882 static void
4883 exo_icon_view_move_cursor_up_down (ExoIconView *icon_view,
4884                                    gint         count)
4885 {
4886   ExoIconViewItem *item;
4887   gboolean         dirty = FALSE;
4888   GList           *list;
4889   gint             cell = -1;
4890   gint             step;
4891 
4892   if (!gtk_widget_has_focus (GTK_WIDGET (icon_view)))
4893     return;
4894 
4895   if (!icon_view->priv->cursor_item)
4896     {
4897       if (count > 0)
4898         list = icon_view->priv->items;
4899       else
4900         list = g_list_last (icon_view->priv->items);
4901 
4902       item = list ? list->data : NULL;
4903     }
4904   else
4905     {
4906       item = icon_view->priv->cursor_item;
4907       cell = icon_view->priv->cursor_cell;
4908       step = count > 0 ? 1 : -1;
4909       while (item)
4910         {
4911           cell = find_cell (icon_view, item, cell,
4912                             GTK_ORIENTATION_VERTICAL,
4913                             step, &count);
4914           if (count == 0)
4915             break;
4916 
4917           /* determine the list position for the item */
4918           list = g_list_find (icon_view->priv->items, item);
4919 
4920           if (G_LIKELY (icon_view->priv->layout_mode == EXO_ICON_VIEW_LAYOUT_ROWS))
4921             {
4922               /* determine the item in the next/prev row */
4923               if (step > 0)
4924                 {
4925                   for (list = list->next; list != NULL; list = list->next)
4926                     if (EXO_ICON_VIEW_ITEM (list->data)->row == item->row + step
4927                         && EXO_ICON_VIEW_ITEM (list->data)->col == item->col)
4928                       break;
4929                  }
4930               else
4931                 {
4932                   for (list = list->prev; list != NULL; list = list->prev)
4933                     if (EXO_ICON_VIEW_ITEM (list->data)->row == item->row + step
4934                         && EXO_ICON_VIEW_ITEM (list->data)->col == item->col)
4935                       break;
4936                 }
4937             }
4938           else
4939             {
4940               list = (step > 0) ? list->next : list->prev;
4941             }
4942 
4943           /* check if we found a matching item */
4944           item = (list != NULL) ? list->data : NULL;
4945 
4946           count = count - step;
4947         }
4948     }
4949 
4950   if (item == icon_view->priv->cursor_item)
4951     gtk_widget_error_bell (GTK_WIDGET (icon_view));
4952 
4953   if (!item)
4954     return;
4955 
4956   if (icon_view->priv->ctrl_pressed ||
4957       !icon_view->priv->shift_pressed ||
4958       !icon_view->priv->anchor_item ||
4959       icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
4960     icon_view->priv->anchor_item = item;
4961 
4962   exo_icon_view_set_cursor_item (icon_view, item, cell);
4963 
4964   if (!icon_view->priv->ctrl_pressed &&
4965       icon_view->priv->selection_mode != GTK_SELECTION_NONE)
4966     {
4967       dirty = exo_icon_view_unselect_all_internal (icon_view);
4968       dirty = exo_icon_view_select_all_between (icon_view,
4969                                                 icon_view->priv->anchor_item,
4970                                                 item) || dirty;
4971     }
4972 
4973   exo_icon_view_scroll_to_item (icon_view, item);
4974 
4975   if (dirty)
4976     g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
4977 }
4978 
4979 
4980 
4981 static void
4982 exo_icon_view_move_cursor_page_up_down (ExoIconView *icon_view,
4983                                         gint         count)
4984 {
4985   ExoIconViewItem *item;
4986   gboolean dirty = FALSE;
4987 
4988   if (!gtk_widget_has_focus (GTK_WIDGET (icon_view)))
4989     return;
4990 
4991   if (!icon_view->priv->cursor_item)
4992     {
4993       GList *list;
4994 
4995       if (count > 0)
4996         list = icon_view->priv->items;
4997       else
4998         list = g_list_last (icon_view->priv->items);
4999 
5000       item = list ? list->data : NULL;
5001     }
5002   else
5003     item = find_item_page_up_down (icon_view,
5004                                    icon_view->priv->cursor_item,
5005                                    count);
5006 
5007   if (item == icon_view->priv->cursor_item)
5008     gtk_widget_error_bell (GTK_WIDGET (icon_view));
5009 
5010   if (!item)
5011     return;
5012 
5013   if (icon_view->priv->ctrl_pressed ||
5014       !icon_view->priv->shift_pressed ||
5015       !icon_view->priv->anchor_item ||
5016       icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
5017     icon_view->priv->anchor_item = item;
5018 
5019   exo_icon_view_set_cursor_item (icon_view, item, -1);
5020 
5021   if (!icon_view->priv->ctrl_pressed &&
5022       icon_view->priv->selection_mode != GTK_SELECTION_NONE)
5023     {
5024       dirty = exo_icon_view_unselect_all_internal (icon_view);
5025       dirty = exo_icon_view_select_all_between (icon_view,
5026                                                 icon_view->priv->anchor_item,
5027                                                 item) || dirty;
5028     }
5029 
5030   exo_icon_view_scroll_to_item (icon_view, item);
5031 
5032   if (dirty)
5033     g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
5034 }
5035 
5036 
5037 
5038 static void
5039 exo_icon_view_move_cursor_left_right (ExoIconView *icon_view,
5040                                       gint         count)
5041 {
5042   ExoIconViewItem *item;
5043   gboolean         dirty = FALSE;
5044   GList           *list;
5045   gint             cell = -1;
5046   gint             step;
5047 
5048   if (!gtk_widget_has_focus (GTK_WIDGET (icon_view)))
5049     return;
5050 
5051   if (gtk_widget_get_direction (GTK_WIDGET (icon_view)) == GTK_TEXT_DIR_RTL)
5052     count *= -1;
5053 
5054   if (!icon_view->priv->cursor_item)
5055     {
5056       if (count > 0)
5057         list = icon_view->priv->items;
5058       else
5059         list = g_list_last (icon_view->priv->items);
5060 
5061       item = list ? list->data : NULL;
5062     }
5063   else
5064     {
5065       item = icon_view->priv->cursor_item;
5066       cell = icon_view->priv->cursor_cell;
5067       step = count > 0 ? 1 : -1;
5068       while (item)
5069         {
5070           cell = find_cell (icon_view, item, cell,
5071                             GTK_ORIENTATION_HORIZONTAL,
5072                             step, &count);
5073           if (count == 0)
5074             break;
5075 
5076           /* lookup the item in the list */
5077           list = g_list_find (icon_view->priv->items, item);
5078 
5079           if (G_LIKELY (icon_view->priv->layout_mode == EXO_ICON_VIEW_LAYOUT_ROWS))
5080             {
5081               /* determine the next/prev list item depending on step,
5082                * support wrapping around on the edges, as requested
5083                * in http://bugzilla.xfce.org/show_bug.cgi?id=1623.
5084                */
5085               list = (step > 0) ? list->next : list->prev;
5086             }
5087           else
5088             {
5089               /* determine the item in the next/prev row */
5090               if (step > 0)
5091                 {
5092                   for (list = list->next; list != NULL; list = list->next)
5093                     if (EXO_ICON_VIEW_ITEM (list->data)->col == item->col + step
5094                         && EXO_ICON_VIEW_ITEM (list->data)->row == item->row)
5095                       break;
5096                  }
5097               else
5098                 {
5099                   for (list = list->prev; list != NULL; list = list->prev)
5100                     if (EXO_ICON_VIEW_ITEM (list->data)->col == item->col + step
5101                         && EXO_ICON_VIEW_ITEM (list->data)->row == item->row)
5102                       break;
5103                 }
5104             }
5105 
5106           /* determine the item for the list position (if any) */
5107           item = (list != NULL) ? list->data : NULL;
5108 
5109           count = count - step;
5110         }
5111     }
5112 
5113   if (item == icon_view->priv->cursor_item)
5114     gtk_widget_error_bell (GTK_WIDGET (icon_view));
5115 
5116   if (!item)
5117     return;
5118 
5119   if (icon_view->priv->ctrl_pressed ||
5120       !icon_view->priv->shift_pressed ||
5121       !icon_view->priv->anchor_item ||
5122       icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
5123     icon_view->priv->anchor_item = item;
5124 
5125   exo_icon_view_set_cursor_item (icon_view, item, cell);
5126 
5127   if (!icon_view->priv->ctrl_pressed &&
5128       icon_view->priv->selection_mode != GTK_SELECTION_NONE)
5129     {
5130       dirty = exo_icon_view_unselect_all_internal (icon_view);
5131       dirty = exo_icon_view_select_all_between (icon_view,
5132                                                 icon_view->priv->anchor_item,
5133                                                 item) || dirty;
5134     }
5135 
5136   exo_icon_view_scroll_to_item (icon_view, item);
5137 
5138   if (dirty)
5139     g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
5140 }
5141 
5142 
5143 
5144 static void
5145 exo_icon_view_move_cursor_start_end (ExoIconView *icon_view,
5146                                      gint         count)
5147 {
5148   ExoIconViewItem *item;
5149   gboolean         dirty = FALSE;
5150   GList           *lp;
5151 
5152   if (!gtk_widget_has_focus (GTK_WIDGET (icon_view)))
5153     return;
5154 
5155   lp = (count < 0) ? icon_view->priv->items : g_list_last (icon_view->priv->items);
5156   item = lp ? EXO_ICON_VIEW_ITEM (lp->data) : NULL;
5157 
5158   if (item == icon_view->priv->cursor_item)
5159     gtk_widget_error_bell (GTK_WIDGET (icon_view));
5160 
5161   if (G_UNLIKELY (item == NULL))
5162     return;
5163 
5164   if (icon_view->priv->ctrl_pressed ||
5165       !icon_view->priv->shift_pressed ||
5166       !icon_view->priv->anchor_item ||
5167       icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
5168     icon_view->priv->anchor_item = item;
5169 
5170   exo_icon_view_set_cursor_item (icon_view, item, -1);
5171 
5172   if (!icon_view->priv->ctrl_pressed &&
5173       icon_view->priv->selection_mode != GTK_SELECTION_NONE)
5174     {
5175       dirty = exo_icon_view_unselect_all_internal (icon_view);
5176       dirty = exo_icon_view_select_all_between (icon_view,
5177                                                 icon_view->priv->anchor_item,
5178                                                 item) || dirty;
5179     }
5180 
5181   exo_icon_view_scroll_to_item (icon_view, item);
5182 
5183   if (G_UNLIKELY (dirty))
5184     g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
5185 }
5186 
5187 
5188 
5189 static void
5190 exo_icon_view_scroll_to_item (ExoIconView     *icon_view,
5191                               ExoIconViewItem *item)
5192 {
5193   gint x, y;
5194   gint focus_width;
5195   GtkAllocation allocation;
5196   GList *lp;
5197   GdkRectangle rect;
5198 
5199   gtk_widget_style_get (GTK_WIDGET (icon_view),
5200                         "focus-line-width", &focus_width,
5201                         NULL);
5202   gtk_widget_get_allocation (GTK_WIDGET (icon_view), &allocation);
5203 
5204   gdk_window_get_position (icon_view->priv->bin_window, &x, &y);
5205 
5206   rect.x = item->area.x;
5207   rect.y = item->area.y;
5208   rect.width = rect.height = 0;
5209   for (lp = icon_view->priv->cell_list; lp != NULL; lp = lp->next)
5210     {
5211       ExoIconViewCellInfo *info = EXO_ICON_VIEW_CELL_INFO (lp->data);
5212       if (G_UNLIKELY (!gtk_cell_renderer_get_visible(info->cell)))
5213         continue;
5214 
5215       if (icon_view->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
5216         {
5217           rect.width += item->box[info->position].width + (info->position > 0 ? icon_view->priv->spacing : 0);
5218           rect.height = MAX (rect.height, item->box[info->position].height);
5219         }
5220       else
5221         {
5222           rect.width = MAX (rect.width, item->box[info->position].width);
5223           rect.height += item->box[info->position].height + (info->position > 0 ? icon_view->priv->spacing : 0);
5224         }
5225     }
5226 
5227   if (y + rect.y - focus_width < 0)
5228     gtk_adjustment_set_value (icon_view->priv->vadjustment,
5229                               gtk_adjustment_get_value(icon_view->priv->vadjustment) + y + rect.y - focus_width);
5230   else if (y + rect.y + rect.height + focus_width > allocation.height)
5231     gtk_adjustment_set_value (icon_view->priv->vadjustment,
5232                               gtk_adjustment_get_value(icon_view->priv->vadjustment) + y + rect.y + rect.height
5233                               + focus_width - allocation.height);
5234 
5235   if (x + rect.x - focus_width < 0)
5236     {
5237       gtk_adjustment_set_value (icon_view->priv->hadjustment,
5238                                 gtk_adjustment_get_value(icon_view->priv->hadjustment) + x + rect.x - focus_width);
5239     }
5240   else if (x + rect.x + rect.width + focus_width > allocation.width
5241         && rect.width < allocation.width)
5242     {
5243       /* the second condition above is to make sure that we don't scroll horizontally if the item
5244        * width is larger than the allocation width. Fixes a weird scrolling bug in the compact view.
5245        * See http://bugzilla.xfce.org/show_bug.cgi?id=1683 for details.
5246        */
5247 
5248       gtk_adjustment_set_value (icon_view->priv->hadjustment,
5249                                 gtk_adjustment_get_value(icon_view->priv->hadjustment) + x + rect.x + rect.width
5250                                 + focus_width - allocation.width);
5251     }
5252 
5253   gtk_adjustment_changed (icon_view->priv->hadjustment);
5254   gtk_adjustment_changed (icon_view->priv->vadjustment);
5255 }
5256 
5257 
5258 
5259 static ExoIconViewCellInfo *
5260 exo_icon_view_get_cell_info (ExoIconView     *icon_view,
5261                              GtkCellRenderer *renderer)
5262 {
5263   GList *lp;
5264 
5265   for (lp = icon_view->priv->cell_list; lp != NULL; lp = lp->next)
5266     if (EXO_ICON_VIEW_CELL_INFO (lp->data)->cell == renderer)
5267       return lp->data;
5268 
5269   return NULL;
5270 }
5271 
5272 
5273 
5274 static void
5275 exo_icon_view_set_cell_data (const ExoIconView *icon_view,
5276                              ExoIconViewItem   *item)
5277 {
5278   ExoIconViewCellInfo *info;
5279   GtkTreePath         *path;
5280   GtkTreeIter          iter;
5281   GValue               value = {0, };
5282   GSList              *slp;
5283   GList               *lp;
5284 
5285   if (G_UNLIKELY (!EXO_ICON_VIEW_FLAG_SET (icon_view, EXO_ICON_VIEW_ITERS_PERSIST)))
5286     {
5287       path = gtk_tree_path_new_from_indices (item->index, -1);
5288       gtk_tree_model_get_iter (icon_view->priv->model, &iter, path);
5289       gtk_tree_path_free (path);
5290     }
5291   else
5292     {
5293       iter = item->iter;
5294     }
5295 
5296   for (lp = icon_view->priv->cell_list; lp != NULL; lp = lp->next)
5297     {
5298       info = EXO_ICON_VIEW_CELL_INFO (lp->data);
5299 
5300       for (slp = info->attributes; slp != NULL && slp->next != NULL; slp = slp->next->next)
5301         {
5302           gtk_tree_model_get_value (icon_view->priv->model, &iter, GPOINTER_TO_INT (slp->next->data), &value);
5303           g_object_set_property (G_OBJECT (info->cell), slp->data, &value);
5304           g_value_unset (&value);
5305         }
5306 
5307       if (G_UNLIKELY (info->func != NULL))
5308         (*info->func) (GTK_CELL_LAYOUT (icon_view), info->cell, icon_view->priv->model, &iter, info->func_data);
5309     }
5310 }
5311 
5312 
5313 
5314 static void
5315 free_cell_attributes (ExoIconViewCellInfo *info)
5316 {
5317   GSList *lp;
5318 
5319   for (lp = info->attributes; lp != NULL && lp->next != NULL; lp = lp->next->next)
5320     g_free (lp->data);
5321   g_slist_free (info->attributes);
5322   info->attributes = NULL;
5323 }
5324 
5325 
5326 
5327 static void
5328 free_cell_info (ExoIconViewCellInfo *info)
5329 {
5330   if (G_UNLIKELY (info->destroy != NULL))
5331     (*info->destroy) (info->func_data);
5332 
5333   free_cell_attributes (info);
5334   g_object_unref (G_OBJECT (info->cell));
5335   g_slice_free (ExoIconViewCellInfo, info);
5336 }
5337 
5338 
5339 
5340 static void
5341 exo_icon_view_cell_layout_pack_start (GtkCellLayout   *layout,
5342                                       GtkCellRenderer *renderer,
5343                                       gboolean         expand)
5344 {
5345   ExoIconViewCellInfo *info;
5346   ExoIconView         *icon_view = EXO_ICON_VIEW (layout);
5347 
5348   _exo_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
5349   _exo_return_if_fail (exo_icon_view_get_cell_info (icon_view, renderer) == NULL);
5350 
5351   g_object_ref_sink (renderer);
5352 
5353   info = g_slice_new0 (ExoIconViewCellInfo);
5354   info->cell = renderer;
5355   info->expand = expand ? TRUE : FALSE;
5356   info->pack = GTK_PACK_START;
5357   info->position = icon_view->priv->n_cells;
5358   info->is_text = GTK_IS_CELL_RENDERER_TEXT (renderer);
5359 
5360   icon_view->priv->cell_list = g_list_append (icon_view->priv->cell_list, info);
5361   icon_view->priv->n_cells++;
5362 
5363   exo_icon_view_invalidate_sizes (icon_view);
5364 }
5365 
5366 
5367 
5368 static void
5369 exo_icon_view_cell_layout_pack_end (GtkCellLayout   *layout,
5370                                     GtkCellRenderer *renderer,
5371                                     gboolean         expand)
5372 {
5373   ExoIconViewCellInfo *info;
5374   ExoIconView         *icon_view = EXO_ICON_VIEW (layout);
5375 
5376   _exo_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
5377   _exo_return_if_fail (exo_icon_view_get_cell_info (icon_view, renderer) == NULL);
5378 
5379   g_object_ref_sink (renderer);
5380 
5381   info = g_slice_new0 (ExoIconViewCellInfo);
5382   info->cell = renderer;
5383   info->expand = expand ? TRUE : FALSE;
5384   info->pack = GTK_PACK_END;
5385   info->position = icon_view->priv->n_cells;
5386   info->is_text = GTK_IS_CELL_RENDERER_TEXT (renderer);
5387 
5388   icon_view->priv->cell_list = g_list_append (icon_view->priv->cell_list, info);
5389   icon_view->priv->n_cells++;
5390 
5391   exo_icon_view_invalidate_sizes (icon_view);
5392 }
5393 
5394 
5395 
5396 static void
5397 exo_icon_view_cell_layout_add_attribute (GtkCellLayout   *layout,
5398                                          GtkCellRenderer *renderer,
5399                                          const gchar     *attribute,
5400                                          gint             column)
5401 {
5402   ExoIconViewCellInfo *info;
5403 
5404   info = exo_icon_view_get_cell_info (EXO_ICON_VIEW (layout), renderer);
5405   if (G_LIKELY (info != NULL))
5406     {
5407       info->attributes = g_slist_prepend (info->attributes, GINT_TO_POINTER (column));
5408       info->attributes = g_slist_prepend (info->attributes, g_strdup (attribute));
5409 
5410       exo_icon_view_invalidate_sizes (EXO_ICON_VIEW (layout));
5411     }
5412 }
5413 
5414 
5415 
5416 static void
5417 exo_icon_view_cell_layout_clear (GtkCellLayout *layout)
5418 {
5419   ExoIconView *icon_view = EXO_ICON_VIEW (layout);
5420 
5421   g_list_foreach (icon_view->priv->cell_list, (GFunc) free_cell_info, NULL);
5422   g_list_free (icon_view->priv->cell_list);
5423   icon_view->priv->cell_list = NULL;
5424   icon_view->priv->n_cells = 0;
5425 
5426   exo_icon_view_invalidate_sizes (icon_view);
5427 }
5428 
5429 
5430 
5431 static void
5432 exo_icon_view_cell_layout_set_cell_data_func (GtkCellLayout         *layout,
5433                                               GtkCellRenderer       *cell,
5434                                               GtkCellLayoutDataFunc  func,
5435                                               gpointer               func_data,
5436                                               GDestroyNotify         destroy)
5437 {
5438   ExoIconViewCellInfo *info;
5439   GDestroyNotify       notify;
5440 
5441   info = exo_icon_view_get_cell_info (EXO_ICON_VIEW (layout), cell);
5442   if (G_LIKELY (info != NULL))
5443     {
5444       if (G_UNLIKELY (info->destroy != NULL))
5445         {
5446           notify = info->destroy;
5447           info->destroy = NULL;
5448           (*notify) (info->func_data);
5449         }
5450 
5451       info->func = func;
5452       info->func_data = func_data;
5453       info->destroy = destroy;
5454 
5455       exo_icon_view_invalidate_sizes (EXO_ICON_VIEW (layout));
5456     }
5457 }
5458 
5459 
5460 
5461 static void
5462 exo_icon_view_cell_layout_clear_attributes (GtkCellLayout   *layout,
5463                                             GtkCellRenderer *renderer)
5464 {
5465   ExoIconViewCellInfo *info;
5466 
5467   info = exo_icon_view_get_cell_info (EXO_ICON_VIEW (layout), renderer);
5468   if (G_LIKELY (info != NULL))
5469     {
5470       free_cell_attributes (info);
5471 
5472       exo_icon_view_invalidate_sizes (EXO_ICON_VIEW (layout));
5473     }
5474 }
5475 
5476 
5477 
5478 static void
5479 exo_icon_view_cell_layout_reorder (GtkCellLayout   *layout,
5480                                    GtkCellRenderer *cell,
5481                                    gint             position)
5482 {
5483   ExoIconViewCellInfo *info;
5484   ExoIconView         *icon_view = EXO_ICON_VIEW (layout);
5485   GList               *lp;
5486   gint                 n;
5487 
5488   icon_view = EXO_ICON_VIEW (layout);
5489 
5490   info = exo_icon_view_get_cell_info (icon_view, cell);
5491   if (G_LIKELY (info != NULL))
5492     {
5493       lp = g_list_find (icon_view->priv->cell_list, info);
5494 
5495       icon_view->priv->cell_list = g_list_remove_link (icon_view->priv->cell_list, lp);
5496       icon_view->priv->cell_list = g_list_insert (icon_view->priv->cell_list, info, position);
5497 
5498       for (lp = icon_view->priv->cell_list, n = 0; lp != NULL; lp = lp->next, ++n)
5499         EXO_ICON_VIEW_CELL_INFO (lp->data)->position = n;
5500 
5501       exo_icon_view_invalidate_sizes (icon_view);
5502     }
5503 }
5504 
5505 
5506 
5507 /**
5508  * exo_icon_view_new:
5509  *
5510  * Creates a new #ExoIconView widget
5511  *
5512  * Return value: A newly created #ExoIconView widget
5513  **/
5514 GtkWidget*
5515 exo_icon_view_new (void)
5516 {
5517   return g_object_new (EXO_TYPE_ICON_VIEW, NULL);
5518 }
5519 
5520 
5521 
5522 /**
5523  * exo_icon_view_new_with_model:
5524  * @model: The model.
5525  *
5526  * Creates a new #ExoIconView widget with the model @model.
5527  *
5528  * Return value: A newly created #ExoIconView widget.
5529  **/
5530 GtkWidget*
5531 exo_icon_view_new_with_model (GtkTreeModel *model)
5532 {
5533   g_return_val_if_fail (model == NULL || GTK_IS_TREE_MODEL (model), NULL);
5534 
5535   return g_object_new (EXO_TYPE_ICON_VIEW,
5536                        "model", model,
5537                        NULL);
5538 }
5539 
5540 
5541 
5542 /**
5543  * exo_icon_view_widget_to_icon_coords:
5544  * @icon_view : a #ExoIconView.
5545  * @wx        : widget x coordinate.
5546  * @wy        : widget y coordinate.
5547  * @ix        : return location for icon x coordinate or %NULL.
5548  * @iy        : return location for icon y coordinate or %NULL.
5549  *
5550  * Converts widget coordinates to coordinates for the icon window
5551  * (the full scrollable area of the icon view).
5552  **/
5553 void
5554 exo_icon_view_widget_to_icon_coords (const ExoIconView *icon_view,
5555                                      gint               wx,
5556                                      gint               wy,
5557                                      gint              *ix,
5558                                      gint              *iy)
5559 {
5560   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
5561 
5562   if (G_LIKELY (ix != NULL))
5563     *ix = wx + gtk_adjustment_get_value(icon_view->priv->hadjustment);
5564   if (G_LIKELY (iy != NULL))
5565     *iy = wy + gtk_adjustment_get_value(icon_view->priv->vadjustment);
5566 }
5567 
5568 
5569 
5570 /**
5571  * exo_icon_view_icon_to_widget_coords:
5572  * @icon_view : a #ExoIconView.
5573  * @ix        : icon x coordinate.
5574  * @iy        : icon y coordinate.
5575  * @wx        : return location for widget x coordinate or %NULL.
5576  * @wy        : return location for widget y coordinate or %NULL.
5577  *
5578  * Converts icon view coordinates (coordinates in full scrollable
5579  * area of the icon view) to widget coordinates.
5580  **/
5581 void
5582 exo_icon_view_icon_to_widget_coords (const ExoIconView *icon_view,
5583                                      gint               ix,
5584                                      gint               iy,
5585                                      gint              *wx,
5586                                      gint              *wy)
5587 {
5588   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
5589 
5590   if (G_LIKELY (wx != NULL))
5591     *wx = ix - gtk_adjustment_get_value(icon_view->priv->hadjustment);
5592   if (G_LIKELY (wy != NULL))
5593     *wy = iy - gtk_adjustment_get_value(icon_view->priv->vadjustment);
5594 }
5595 
5596 
5597 
5598 /**
5599  * exo_icon_view_get_path_at_pos:
5600  * @icon_view : A #ExoIconView.
5601  * @x         : The x position to be identified
5602  * @y         : The y position to be identified
5603  *
5604  * Finds the path at the point (@x, @y), relative to widget coordinates.
5605  * See exo_icon_view_get_item_at_pos(), if you are also interested in
5606  * the cell at the specified position.
5607  *
5608  * Return value: The #GtkTreePath corresponding to the icon or %NULL
5609  *               if no icon exists at that position.
5610  **/
5611 GtkTreePath*
5612 exo_icon_view_get_path_at_pos (const ExoIconView *icon_view,
5613                                gint               x,
5614                                gint               y)
5615 {
5616   ExoIconViewItem *item;
5617 
5618   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), NULL);
5619 
5620   /* translate the widget coordinates to icon window coordinates */
5621   x += gtk_adjustment_get_value(icon_view->priv->hadjustment);
5622   y += gtk_adjustment_get_value(icon_view->priv->vadjustment);
5623 
5624   item = exo_icon_view_get_item_at_coords (icon_view, x, y, TRUE, NULL);
5625 
5626   return (item != NULL) ? gtk_tree_path_new_from_indices (item->index, -1) : NULL;
5627 }
5628 
5629 
5630 
5631 /**
5632  * exo_icon_view_get_item_at_pos:
5633  * @icon_view: A #ExoIconView.
5634  * @x: The x position to be identified
5635  * @y: The y position to be identified
5636  * @path: Return location for the path, or %NULL
5637  * @cell: Return location for the renderer responsible for the cell
5638  *   at (@x, @y), or %NULL
5639  *
5640  * Finds the path at the point (@x, @y), relative to widget coordinates.
5641  * In contrast to exo_icon_view_get_path_at_pos(), this function also
5642  * obtains the cell at the specified position. The returned path should
5643  * be freed with gtk_tree_path_free().
5644  *
5645  * Return value: %TRUE if an item exists at the specified position
5646  *
5647  * Since: 0.3.1
5648  **/
5649 gboolean
5650 exo_icon_view_get_item_at_pos (const ExoIconView *icon_view,
5651                                gint               x,
5652                                gint               y,
5653                                GtkTreePath      **path,
5654                                GtkCellRenderer  **cell)
5655 {
5656   ExoIconViewCellInfo *info = NULL;
5657   ExoIconViewItem     *item;
5658 
5659   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
5660 
5661   item = exo_icon_view_get_item_at_coords (icon_view, x, y, TRUE, &info);
5662 
5663   if (G_LIKELY (path != NULL))
5664     *path = (item != NULL) ? gtk_tree_path_new_from_indices (item->index, -1) : NULL;
5665 
5666   if (G_LIKELY (cell != NULL))
5667     *cell = (info != NULL) ? info->cell : NULL;
5668 
5669   return (item != NULL);
5670 }
5671 
5672 
5673 
5674 /**
5675  * exo_icon_view_get_visible_range:
5676  * @icon_view  : A #ExoIconView
5677  * @start_path : Return location for start of region, or %NULL
5678  * @end_path   : Return location for end of region, or %NULL
5679  *
5680  * Sets @start_path and @end_path to be the first and last visible path.
5681  * Note that there may be invisible paths in between.
5682  *
5683  * Both paths should be freed with gtk_tree_path_free() after use.
5684  *
5685  * Return value: %TRUE, if valid paths were placed in @start_path and @end_path
5686  *
5687  * Since: 0.3.1
5688  **/
5689 gboolean
5690 exo_icon_view_get_visible_range (const ExoIconView *icon_view,
5691                                  GtkTreePath      **start_path,
5692                                  GtkTreePath      **end_path)
5693 {
5694   const ExoIconViewPrivate *priv = icon_view->priv;
5695   const ExoIconViewItem    *item;
5696   const GList              *lp;
5697   gint                      start_index = -1;
5698   gint                      end_index = -1;
5699   gint                      i;
5700 
5701   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
5702 
5703   if (priv->hadjustment == NULL || priv->vadjustment == NULL)
5704     return FALSE;
5705 
5706   if (start_path == NULL && end_path == NULL)
5707     return FALSE;
5708 
5709   for (i = 0, lp = priv->items; lp != NULL; ++i, lp = lp->next)
5710     {
5711       item = (const ExoIconViewItem *) lp->data;
5712       if ((item->area.x + item->area.width >= (gint) gtk_adjustment_get_value(priv->hadjustment)) &&
5713           (item->area.y + item->area.height >= (gint) gtk_adjustment_get_value(priv->vadjustment)) &&
5714           (item->area.x <= (gint) (gtk_adjustment_get_value(priv->hadjustment) + gtk_adjustment_get_page_size(priv->hadjustment))) &&
5715           (item->area.y <= (gint) (gtk_adjustment_get_value(priv->vadjustment) + gtk_adjustment_get_page_size(priv->vadjustment))))
5716         {
5717           if (start_index == -1)
5718             start_index = i;
5719           end_index = i;
5720         }
5721     }
5722 
5723   if (start_path != NULL && start_index != -1)
5724     *start_path = gtk_tree_path_new_from_indices (start_index, -1);
5725   if (end_path != NULL && end_index != -1)
5726     *end_path = gtk_tree_path_new_from_indices (end_index, -1);
5727 
5728   return (start_index != -1);
5729 }
5730 
5731 
5732 
5733 /**
5734  * exo_icon_view_selected_foreach:
5735  * @icon_view : A #ExoIconView.
5736  * @func      : The funcion to call for each selected icon.
5737  * @data      : User data to pass to the function.
5738  *
5739  * Calls a function for each selected icon. Note that the model or
5740  * selection cannot be modified from within this function.
5741  **/
5742 void
5743 exo_icon_view_selected_foreach (ExoIconView           *icon_view,
5744                                 ExoIconViewForeachFunc func,
5745                                 gpointer               data)
5746 {
5747   GtkTreePath *path;
5748   GList       *lp;
5749 
5750   path = gtk_tree_path_new_first ();
5751   for (lp = icon_view->priv->items; lp != NULL; lp = lp->next)
5752     {
5753       if (EXO_ICON_VIEW_ITEM (lp->data)->selected)
5754         (*func) (icon_view, path, data);
5755       gtk_tree_path_next (path);
5756     }
5757   gtk_tree_path_free (path);
5758 }
5759 
5760 
5761 
5762 /**
5763  * exo_icon_view_get_selection_mode:
5764  * @icon_view : A #ExoIconView.
5765  *
5766  * Gets the selection mode of the @icon_view.
5767  *
5768  * Return value: the current selection mode
5769  **/
5770 GtkSelectionMode
5771 exo_icon_view_get_selection_mode (const ExoIconView *icon_view)
5772 {
5773   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), GTK_SELECTION_SINGLE);
5774   return icon_view->priv->selection_mode;
5775 }
5776 
5777 
5778 
5779 /**
5780  * exo_icon_view_set_selection_mode:
5781  * @icon_view : A #ExoIconView.
5782  * @mode      : The selection mode
5783  *
5784  * Sets the selection mode of the @icon_view.
5785  **/
5786 void
5787 exo_icon_view_set_selection_mode (ExoIconView      *icon_view,
5788                                   GtkSelectionMode  mode)
5789 {
5790   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
5791 
5792   if (G_LIKELY (mode != icon_view->priv->selection_mode))
5793     {
5794       if (mode == GTK_SELECTION_NONE || icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE)
5795         exo_icon_view_unselect_all (icon_view);
5796 
5797       icon_view->priv->selection_mode = mode;
5798 
5799       g_object_notify (G_OBJECT (icon_view), "selection-mode");
5800     }
5801 }
5802 
5803 
5804 
5805 /**
5806  * exo_icon_view_get_layout_mode:
5807  * @icon_view : A #ExoIconView.
5808  *
5809  * Returns the #ExoIconViewLayoutMode used to layout the
5810  * items in the @icon_view.
5811  *
5812  * Return value: the layout mode of @icon_view.
5813  *
5814  * Since: 0.3.1.5
5815  **/
5816 ExoIconViewLayoutMode
5817 exo_icon_view_get_layout_mode (const ExoIconView *icon_view)
5818 {
5819   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), EXO_ICON_VIEW_LAYOUT_ROWS);
5820   return icon_view->priv->layout_mode;
5821 }
5822 
5823 
5824 
5825 /**
5826  * exo_icon_view_set_layout_mode:
5827  * @icon_view   : a #ExoIconView.
5828  * @layout_mode : the new #ExoIconViewLayoutMode for @icon_view.
5829  *
5830  * Sets the layout mode of @icon_view to @layout_mode.
5831  *
5832  * Since: 0.3.1.5
5833  **/
5834 void
5835 exo_icon_view_set_layout_mode (ExoIconView          *icon_view,
5836                                ExoIconViewLayoutMode layout_mode)
5837 {
5838   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
5839 
5840   /* check if we have a new setting */
5841   if (G_LIKELY (icon_view->priv->layout_mode != layout_mode))
5842     {
5843       /* apply the new setting */
5844       icon_view->priv->layout_mode = layout_mode;
5845 
5846       /* cancel any active cell editor */
5847       exo_icon_view_stop_editing (icon_view, TRUE);
5848 
5849       /* invalidate the current item sizes */
5850       exo_icon_view_invalidate_sizes (icon_view);
5851       exo_icon_view_queue_layout (icon_view);
5852 
5853       /* notify listeners */
5854       g_object_notify (G_OBJECT (icon_view), "layout-mode");
5855     }
5856 }
5857 
5858 
5859 
5860 /**
5861  * exo_icon_view_get_model:
5862  * @icon_view : a #ExoIconView
5863  *
5864  * Returns the model the #ExoIconView is based on. Returns %NULL if the
5865  * model is unset.
5866  *
5867  * Return value: A #GtkTreeModel, or %NULL if none is currently being used.
5868  **/
5869 GtkTreeModel*
5870 exo_icon_view_get_model (const ExoIconView *icon_view)
5871 {
5872   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), NULL);
5873   return icon_view->priv->model;
5874 }
5875 
5876 
5877 
5878 /**
5879  * exo_icon_view_set_model:
5880  * @icon_view : A #ExoIconView.
5881  * @model     : The model.
5882  *
5883  * Sets the model for a #ExoIconView.
5884  * If the @icon_view already has a model set, it will remove
5885  * it before setting the new model.  If @model is %NULL, then
5886  * it will unset the old model.
5887  **/
5888 void
5889 exo_icon_view_set_model (ExoIconView  *icon_view,
5890                          GtkTreeModel *model)
5891 {
5892   ExoIconViewItem *item;
5893   GtkTreeIter      iter;
5894   GList           *items = NULL;
5895   GList           *lp;
5896   gint             n;
5897 
5898   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
5899   g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
5900 
5901   /* verify that we don't already use that model */
5902   if (G_UNLIKELY (icon_view->priv->model == model))
5903     return;
5904 
5905   /* verify the new model */
5906   if (G_LIKELY (model != NULL))
5907     {
5908       g_return_if_fail (gtk_tree_model_get_flags (model) & GTK_TREE_MODEL_LIST_ONLY);
5909 
5910       if (G_UNLIKELY (icon_view->priv->pixbuf_column != -1))
5911         g_return_if_fail (gtk_tree_model_get_column_type (model, icon_view->priv->pixbuf_column) == GDK_TYPE_PIXBUF);
5912 
5913       if (G_UNLIKELY (icon_view->priv->text_column != -1))
5914         g_return_if_fail (gtk_tree_model_get_column_type (model, icon_view->priv->text_column) == G_TYPE_STRING);
5915 
5916       if (G_UNLIKELY (icon_view->priv->markup_column != -1))
5917         g_return_if_fail (gtk_tree_model_get_column_type (model, icon_view->priv->markup_column) == G_TYPE_STRING);
5918     }
5919 
5920   /* be sure to cancel any pending editor */
5921   exo_icon_view_stop_editing (icon_view, TRUE);
5922 
5923   /* disconnect from the previous model */
5924   if (G_LIKELY (icon_view->priv->model != NULL))
5925     {
5926       /* disconnect signals handlers from the previous model */
5927       g_signal_handlers_disconnect_by_func (G_OBJECT (icon_view->priv->model), exo_icon_view_row_changed, icon_view);
5928       g_signal_handlers_disconnect_by_func (G_OBJECT (icon_view->priv->model), exo_icon_view_row_inserted, icon_view);
5929       g_signal_handlers_disconnect_by_func (G_OBJECT (icon_view->priv->model), exo_icon_view_row_deleted, icon_view);
5930       g_signal_handlers_disconnect_by_func (G_OBJECT (icon_view->priv->model), exo_icon_view_rows_reordered, icon_view);
5931 
5932       /* release our reference on the model */
5933       g_object_unref (G_OBJECT (icon_view->priv->model));
5934 
5935       /* drop all items belonging to the previous model */
5936       for (lp = icon_view->priv->items; lp != NULL; lp = lp->next)
5937         {
5938           g_free (EXO_ICON_VIEW_ITEM (lp->data)->box);
5939           g_slice_free (ExoIconViewItem, lp->data);
5940         }
5941       g_list_free (icon_view->priv->items);
5942       icon_view->priv->items = NULL;
5943 
5944       /* reset statistics */
5945       icon_view->priv->search_column = -1;
5946       icon_view->priv->anchor_item = NULL;
5947       icon_view->priv->cursor_item = NULL;
5948       icon_view->priv->prelit_item = NULL;
5949       icon_view->priv->last_single_clicked = NULL;
5950       icon_view->priv->width = 0;
5951       icon_view->priv->height = 0;
5952 
5953       /* cancel any pending single click timer */
5954       if (G_UNLIKELY (icon_view->priv->single_click_timeout_id != 0))
5955         g_source_remove (icon_view->priv->single_click_timeout_id);
5956 
5957       /* reset cursor when in single click mode and realized */
5958       if (G_UNLIKELY (icon_view->priv->single_click && gtk_widget_get_realized (GTK_WIDGET(icon_view))))        gdk_window_set_cursor (icon_view->priv->bin_window, NULL);
5959     }
5960 
5961   /* be sure to drop any previous scroll_to_path reference,
5962    * as it points to the old (no longer valid) model.
5963    */
5964   if (G_UNLIKELY (icon_view->priv->scroll_to_path != NULL))
5965     {
5966       gtk_tree_row_reference_free (icon_view->priv->scroll_to_path);
5967       icon_view->priv->scroll_to_path = NULL;
5968     }
5969 
5970   /* activate the new model */
5971   icon_view->priv->model = model;
5972 
5973   /* connect to the new model */
5974   if (G_LIKELY (model != NULL))
5975     {
5976       /* take a reference on the model */
5977       g_object_ref (G_OBJECT (model));
5978 
5979       /* connect signals */
5980       g_signal_connect (G_OBJECT (model), "row-changed", G_CALLBACK (exo_icon_view_row_changed), icon_view);
5981       g_signal_connect (G_OBJECT (model), "row-inserted", G_CALLBACK (exo_icon_view_row_inserted), icon_view);
5982       g_signal_connect (G_OBJECT (model), "row-deleted", G_CALLBACK (exo_icon_view_row_deleted), icon_view);
5983       g_signal_connect (G_OBJECT (model), "rows-reordered", G_CALLBACK (exo_icon_view_rows_reordered), icon_view);
5984 
5985       /* check if the new model supports persistent iterators */
5986       if (gtk_tree_model_get_flags (model) & GTK_TREE_MODEL_ITERS_PERSIST)
5987         EXO_ICON_VIEW_SET_FLAG (icon_view, EXO_ICON_VIEW_ITERS_PERSIST);
5988       else
5989         EXO_ICON_VIEW_UNSET_FLAG (icon_view, EXO_ICON_VIEW_ITERS_PERSIST);
5990 
5991       /* determine an appropriate search column */
5992       if (icon_view->priv->search_column <= 0)
5993         {
5994           /* we simply use the first string column */
5995           for (n = 0; n < gtk_tree_model_get_n_columns (model); ++n)
5996             if (g_value_type_transformable (gtk_tree_model_get_column_type (model, n), G_TYPE_STRING))
5997               {
5998                 icon_view->priv->search_column = n;
5999                 break;
6000               }
6001         }
6002 
6003       /* build up the initial items list */
6004       if (gtk_tree_model_get_iter_first (model, &iter))
6005         {
6006           n = 0;
6007           do
6008             {
6009               item = g_slice_new0 (ExoIconViewItem);
6010               item->iter = iter;
6011               item->area.width = -1;
6012               item->index = n++;
6013               items = g_list_prepend (items, item);
6014             }
6015           while (gtk_tree_model_iter_next (model, &iter));
6016         }
6017       icon_view->priv->items = g_list_reverse (items);
6018 
6019       /* layout the new items */
6020       exo_icon_view_queue_layout (icon_view);
6021     }
6022 
6023   /* hide the interactive search dialog (if any) */
6024   if (G_LIKELY (icon_view->priv->search_window != NULL))
6025     exo_icon_view_search_dialog_hide (icon_view->priv->search_window, icon_view);
6026 
6027   /* notify listeners */
6028   g_object_notify (G_OBJECT (icon_view), "model");
6029 
6030   if (gtk_widget_get_realized (GTK_WIDGET (icon_view)))
6031     gtk_widget_queue_resize (GTK_WIDGET (icon_view));
6032 }
6033 
6034 
6035 
6036 static void
6037 update_text_cell (ExoIconView *icon_view)
6038 {
6039   ExoIconViewCellInfo *info;
6040   GList *l;
6041   gint i;
6042 
6043   if (icon_view->priv->text_column == -1 &&
6044       icon_view->priv->markup_column == -1)
6045     {
6046       if (icon_view->priv->text_cell != -1)
6047         {
6048           info = g_list_nth_data (icon_view->priv->cell_list,
6049                                   icon_view->priv->text_cell);
6050 
6051           icon_view->priv->cell_list = g_list_remove (icon_view->priv->cell_list, info);
6052 
6053           free_cell_info (info);
6054 
6055           icon_view->priv->n_cells--;
6056           icon_view->priv->text_cell = -1;
6057         }
6058     }
6059   else
6060     {
6061       if (icon_view->priv->text_cell == -1)
6062         {
6063           GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
6064           gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (icon_view), cell, FALSE);
6065           for (l = icon_view->priv->cell_list, i = 0; l; l = l->next, i++)
6066             {
6067               info = l->data;
6068               if (info->cell == cell)
6069                 {
6070                   icon_view->priv->text_cell = i;
6071                   break;
6072                 }
6073             }
6074         }
6075 
6076       info = g_list_nth_data (icon_view->priv->cell_list,
6077                               icon_view->priv->text_cell);
6078 
6079       if (icon_view->priv->markup_column != -1)
6080         gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (icon_view),
6081                                         info->cell,
6082                                         "markup", icon_view->priv->markup_column,
6083                                         NULL);
6084       else
6085         gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (icon_view),
6086                                         info->cell,
6087                                         "text", icon_view->priv->text_column,
6088                                         NULL);
6089     }
6090 }
6091 
6092 static void
6093 update_pixbuf_cell (ExoIconView *icon_view)
6094 {
6095   ExoIconViewCellInfo *info;
6096   GList *l;
6097   gint i;
6098 
6099   if (icon_view->priv->pixbuf_column == -1)
6100     {
6101       if (icon_view->priv->pixbuf_cell != -1)
6102         {
6103           info = g_list_nth_data (icon_view->priv->cell_list,
6104                                   icon_view->priv->pixbuf_cell);
6105 
6106           icon_view->priv->cell_list = g_list_remove (icon_view->priv->cell_list, info);
6107 
6108           free_cell_info (info);
6109 
6110           icon_view->priv->n_cells--;
6111           icon_view->priv->pixbuf_cell = -1;
6112         }
6113     }
6114   else
6115     {
6116       if (icon_view->priv->pixbuf_cell == -1)
6117         {
6118           GtkCellRenderer *cell = gtk_cell_renderer_pixbuf_new ();
6119 
6120           gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (icon_view), cell, FALSE);
6121           for (l = icon_view->priv->cell_list, i = 0; l; l = l->next, i++)
6122             {
6123               info = l->data;
6124               if (info->cell == cell)
6125                 {
6126                   icon_view->priv->pixbuf_cell = i;
6127                   break;
6128                 }
6129             }
6130         }
6131 
6132         info = g_list_nth_data (icon_view->priv->cell_list,
6133                                 icon_view->priv->pixbuf_cell);
6134 
6135         gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (icon_view),
6136                                         info->cell,
6137                                         "pixbuf", icon_view->priv->pixbuf_column,
6138                                         NULL);
6139     }
6140 }
6141 
6142 
6143 
6144 /**
6145  * exo_icon_view_select_path:
6146  * @icon_view : A #ExoIconView.
6147  * @path      : The #GtkTreePath to be selected.
6148  *
6149  * Selects the row at @path.
6150  **/
6151 void
6152 exo_icon_view_select_path (ExoIconView *icon_view,
6153                            GtkTreePath *path)
6154 {
6155   ExoIconViewItem *item;
6156 
6157   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6158   g_return_if_fail (icon_view->priv->model != NULL);
6159   g_return_if_fail (gtk_tree_path_get_depth (path) > 0);
6160 
6161   item = g_list_nth_data (icon_view->priv->items, gtk_tree_path_get_indices(path)[0]);
6162   if (G_LIKELY (item != NULL))
6163     exo_icon_view_select_item (icon_view, item);
6164 }
6165 
6166 
6167 
6168 /**
6169  * exo_icon_view_unselect_path:
6170  * @icon_view : A #ExoIconView.
6171  * @path      : The #GtkTreePath to be unselected.
6172  *
6173  * Unselects the row at @path.
6174  **/
6175 void
6176 exo_icon_view_unselect_path (ExoIconView *icon_view,
6177                              GtkTreePath *path)
6178 {
6179   ExoIconViewItem *item;
6180 
6181   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6182   g_return_if_fail (icon_view->priv->model != NULL);
6183   g_return_if_fail (gtk_tree_path_get_depth (path) > 0);
6184 
6185   item = g_list_nth_data (icon_view->priv->items, gtk_tree_path_get_indices(path)[0]);
6186   if (G_LIKELY (item != NULL))
6187     exo_icon_view_unselect_item (icon_view, item);
6188 }
6189 
6190 
6191 
6192 /**
6193  * exo_icon_view_get_selected_items:
6194  * @icon_view: A #ExoIconView.
6195  *
6196  * Creates a list of paths of all selected items. Additionally, if you are
6197  * planning on modifying the model after calling this function, you may
6198  * want to convert the returned list into a list of #GtkTreeRowReference<!-- -->s.
6199  * To do this, you can use gtk_tree_row_reference_new().
6200  *
6201  * To free the return value, use:
6202  * <informalexample><programlisting>
6203  * g_list_foreach (list, gtk_tree_path_free, NULL);
6204  * g_list_free (list);
6205  * </programlisting></informalexample>
6206  *
6207  * Return value: A #GList containing a #GtkTreePath for each selected row.
6208  **/
6209 GList*
6210 exo_icon_view_get_selected_items (const ExoIconView *icon_view)
6211 {
6212   GList *selected = NULL;
6213   GList *lp;
6214   gint   i;
6215 
6216   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), NULL);
6217 
6218   for (i = 0, lp = icon_view->priv->items; lp != NULL; ++i, lp = lp->next)
6219     {
6220       if (EXO_ICON_VIEW_ITEM (lp->data)->selected)
6221         selected = g_list_append (selected, gtk_tree_path_new_from_indices (i, -1));
6222     }
6223 
6224   return selected;
6225 }
6226 
6227 
6228 
6229 gint exo_icon_view_count_selected_items (const ExoIconView *icon_view)
6230 {
6231   GList *lp;
6232   gint   i = 0;
6233 
6234   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), 0);
6235 
6236   for (lp = icon_view->priv->items; lp != NULL; lp = lp->next)
6237     {
6238       if (EXO_ICON_VIEW_ITEM (lp->data)->selected)
6239         i++;
6240     }
6241 
6242   return i;
6243 }
6244 
6245 
6246 
6247 /**
6248  * exo_icon_view_select_all:
6249  * @icon_view : A #ExoIconView.
6250  *
6251  * Selects all the icons. @icon_view must has its selection mode set
6252  * to #GTK_SELECTION_MULTIPLE.
6253  **/
6254 void
6255 exo_icon_view_select_all (ExoIconView *icon_view)
6256 {
6257   GList *items;
6258   gboolean dirty = FALSE;
6259 
6260   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6261 
6262   if (icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
6263     return;
6264 
6265   for (items = icon_view->priv->items; items; items = items->next)
6266     {
6267       ExoIconViewItem *item = items->data;
6268 
6269       if (!item->selected)
6270         {
6271           dirty = TRUE;
6272           item->selected = TRUE;
6273           exo_icon_view_queue_draw_item (icon_view, item);
6274         }
6275     }
6276 
6277   if (dirty)
6278     g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
6279 }
6280 
6281 
6282 
6283 /**
6284  * exo_icon_view_unselect_all:
6285  * @icon_view : A #ExoIconView.
6286  *
6287  * Unselects all the icons.
6288  **/
6289 void
6290 exo_icon_view_unselect_all (ExoIconView *icon_view)
6291 {
6292   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6293 
6294   if (G_UNLIKELY (icon_view->priv->selection_mode == GTK_SELECTION_BROWSE))
6295     return;
6296 
6297   if (exo_icon_view_unselect_all_internal (icon_view))
6298     g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
6299 }
6300 
6301 
6302 
6303 /**
6304  * exo_icon_view_path_is_selected:
6305  * @icon_view: A #ExoIconView.
6306  * @path: A #GtkTreePath to check selection on.
6307  *
6308  * Returns %TRUE if the icon pointed to by @path is currently
6309  * selected. If @icon does not point to a valid location, %FALSE is returned.
6310  *
6311  * Return value: %TRUE if @path is selected.
6312  **/
6313 gboolean
6314 exo_icon_view_path_is_selected (const ExoIconView *icon_view,
6315                                 GtkTreePath       *path)
6316 {
6317   ExoIconViewItem *item;
6318 
6319   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
6320   g_return_val_if_fail (icon_view->priv->model != NULL, FALSE);
6321   g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);
6322 
6323   item = g_list_nth_data (icon_view->priv->items, gtk_tree_path_get_indices(path)[0]);
6324 
6325   return (item != NULL && item->selected);
6326 }
6327 
6328 
6329 
6330 /**
6331  * exo_icon_view_item_activated:
6332  * @icon_view : a #ExoIconView
6333  * @path      : the #GtkTreePath to be activated
6334  *
6335  * Activates the item determined by @path.
6336  **/
6337 void
6338 exo_icon_view_item_activated (ExoIconView *icon_view,
6339                               GtkTreePath *path)
6340 {
6341   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6342   g_return_if_fail (gtk_tree_path_get_depth (path) > 0);
6343 
6344   g_signal_emit (icon_view, icon_view_signals[ITEM_ACTIVATED], 0, path);
6345 }
6346 
6347 
6348 
6349 /**
6350  * exo_icon_view_get_cursor:
6351  * @icon_view : A #ExoIconView
6352  * @path      : Return location for the current cursor path, or %NULL
6353  * @cell      : Return location the current focus cell, or %NULL
6354  *
6355  * Fills in @path and @cell with the current cursor path and cell.
6356  * If the cursor isn't currently set, then *@path will be %NULL.
6357  * If no cell currently has focus, then *@cell will be %NULL.
6358  *
6359  * The returned #GtkTreePath must be freed with gtk_tree_path_free().
6360  *
6361  * Return value: %TRUE if the cursor is set.
6362  *
6363  * Since: 0.3.1
6364  **/
6365 gboolean
6366 exo_icon_view_get_cursor (const ExoIconView *icon_view,
6367                           GtkTreePath      **path,
6368                           GtkCellRenderer  **cell)
6369 {
6370   ExoIconViewCellInfo *info;
6371   ExoIconViewItem     *item;
6372 
6373   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
6374 
6375   item = icon_view->priv->cursor_item;
6376   info = (icon_view->priv->cursor_cell < 0) ? NULL : g_list_nth_data (icon_view->priv->cell_list, icon_view->priv->cursor_cell);
6377 
6378   if (G_LIKELY (path != NULL))
6379     *path = (item != NULL) ? gtk_tree_path_new_from_indices (item->index, -1) : NULL;
6380 
6381   if (G_LIKELY (cell != NULL))
6382     *cell = (info != NULL) ? info->cell : NULL;
6383 
6384   return (item != NULL);
6385 }
6386 
6387 
6388 
6389 /**
6390  * exo_icon_view_set_cursor:
6391  * @icon_view     : a #ExoIconView
6392  * @path          : a #GtkTreePath
6393  * @cell          : a #GtkCellRenderer or %NULL
6394  * @start_editing : %TRUE if the specified cell should start being edited.
6395  *
6396  * Sets the current keyboard focus to be at @path, and selects it.  This is
6397  * useful when you want to focus the user's attention on a particular item.
6398  * If @cell is not %NULL, then focus is given to the cell specified by
6399  * it. Additionally, if @start_editing is %TRUE, then editing should be
6400  * started in the specified cell.
6401  *
6402  * This function is often followed by <literal>gtk_widget_grab_focus
6403  * (icon_view)</literal> in order to give keyboard focus to the widget.
6404  * Please note that editing can only happen when the widget is realized.
6405  *
6406  * Since: 0.3.1
6407  **/
6408 void
6409 exo_icon_view_set_cursor (ExoIconView     *icon_view,
6410                           GtkTreePath     *path,
6411                           GtkCellRenderer *cell,
6412                           gboolean         start_editing)
6413 {
6414   ExoIconViewItem *item;
6415   ExoIconViewCellInfo *info =  NULL;
6416   GList *l;
6417   gint i, cell_pos;
6418 
6419   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6420   g_return_if_fail (path != NULL);
6421   g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell));
6422 
6423   exo_icon_view_stop_editing (icon_view, TRUE);
6424 
6425   item = g_list_nth_data (icon_view->priv->items, gtk_tree_path_get_indices(path)[0]);
6426   if (G_UNLIKELY (item == NULL))
6427     return;
6428 
6429   cell_pos = -1;
6430   for (l = icon_view->priv->cell_list, i = 0; l; l = l->next, i++)
6431     {
6432       info = l->data;
6433 
6434       if (info->cell == cell)
6435         {
6436           cell_pos = i;
6437           break;
6438         }
6439 
6440       info = NULL;
6441     }
6442 
6443   /* place the cursor on the item */
6444   exo_icon_view_set_cursor_item (icon_view, item, cell_pos);
6445   icon_view->priv->anchor_item = item;
6446 
6447   /* scroll to the item (maybe delayed) */
6448   exo_icon_view_scroll_to_path (icon_view, path, FALSE, 0.0f, 0.0f);
6449 
6450   if (start_editing)
6451     exo_icon_view_start_editing (icon_view, item, info, NULL);
6452 }
6453 
6454 
6455 
6456 /**
6457  * exo_icon_view_scroll_to_path:
6458  * @icon_view: A #ExoIconView.
6459  * @path: The path of the item to move to.
6460  * @use_align: whether to use alignment arguments, or %FALSE.
6461  * @row_align: The vertical alignment of the item specified by @path.
6462  * @col_align: The horizontal alignment of the item specified by @column.
6463  *
6464  * Moves the alignments of @icon_view to the position specified by @path.
6465  * @row_align determines where the row is placed, and @col_align determines where
6466  * @column is placed.  Both are expected to be between 0.0 and 1.0.
6467  * 0.0 means left/top alignment, 1.0 means right/bottom alignment, 0.5 means center.
6468  *
6469  * If @use_align is %FALSE, then the alignment arguments are ignored, and the
6470  * tree does the minimum amount of work to scroll the item onto the screen.
6471  * This means that the item will be scrolled to the edge closest to its current
6472  * position.  If the item is currently visible on the screen, nothing is done.
6473  *
6474  * This function only works if the model is set, and @path is a valid row on the
6475  * model.  If the model changes before the @tree_view is realized, the centered
6476  * path will be modified to reflect this change.
6477  *
6478  * Since: 0.3.1
6479  **/
6480 void
6481 exo_icon_view_scroll_to_path (ExoIconView *icon_view,
6482                               GtkTreePath *path,
6483                               gboolean     use_align,
6484                               gfloat       row_align,
6485                               gfloat       col_align)
6486 {
6487   ExoIconViewItem *item;
6488 
6489   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6490   g_return_if_fail (gtk_tree_path_get_depth (path) > 0);
6491   g_return_if_fail (row_align >= 0.0 && row_align <= 1.0);
6492   g_return_if_fail (col_align >= 0.0 && col_align <= 1.0);
6493 
6494   /* Delay scrolling if either not realized or pending layout() */
6495   if (!gtk_widget_get_realized (GTK_WIDGET (icon_view)) || icon_view->priv->layout_idle_id != 0)
6496     {
6497       /* release the previous scroll_to_path reference */
6498       if (G_UNLIKELY (icon_view->priv->scroll_to_path != NULL))
6499         gtk_tree_row_reference_free (icon_view->priv->scroll_to_path);
6500 
6501       /* remember a reference for the new path and settings */
6502       icon_view->priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (icon_view), icon_view->priv->model, path);
6503       icon_view->priv->scroll_to_use_align = use_align;
6504       icon_view->priv->scroll_to_row_align = row_align;
6505       icon_view->priv->scroll_to_col_align = col_align;
6506     }
6507   else
6508     {
6509       item = g_list_nth_data (icon_view->priv->items, gtk_tree_path_get_indices(path)[0]);
6510       if (G_UNLIKELY (item == NULL))
6511         return;
6512 
6513       if (use_align)
6514         {
6515           gint x, y;
6516           gint focus_width;
6517           gfloat offset, value;
6518           GtkAllocation allocation;
6519 
6520           gtk_widget_style_get (GTK_WIDGET (icon_view),
6521                                 "focus-line-width", &focus_width,
6522                                 NULL);
6523           gtk_widget_get_allocation (GTK_WIDGET (icon_view), &allocation);
6524 
6525           gdk_window_get_position (icon_view->priv->bin_window, &x, &y);
6526 
6527           offset =  y + item->area.y - focus_width -
6528             row_align * (allocation.height - item->area.height);
6529           value = CLAMP (gtk_adjustment_get_value(icon_view->priv->vadjustment) + offset,
6530                          gtk_adjustment_get_lower(icon_view->priv->vadjustment),
6531                          gtk_adjustment_get_upper(icon_view->priv->vadjustment) - gtk_adjustment_get_page_size(icon_view->priv->vadjustment));
6532           gtk_adjustment_set_value (icon_view->priv->vadjustment, value);
6533 
6534           offset = x + item->area.x - focus_width -
6535             col_align * (allocation.width - item->area.width);
6536           value = CLAMP (gtk_adjustment_get_value(icon_view->priv->hadjustment) + offset,
6537                          gtk_adjustment_get_lower(icon_view->priv->hadjustment),
6538                          gtk_adjustment_get_upper(icon_view->priv->hadjustment) - gtk_adjustment_get_page_size(icon_view->priv->hadjustment));
6539           gtk_adjustment_set_value (icon_view->priv->hadjustment, value);
6540 
6541           gtk_adjustment_changed (icon_view->priv->hadjustment);
6542           gtk_adjustment_changed (icon_view->priv->vadjustment);
6543         }
6544       else
6545         {
6546           exo_icon_view_scroll_to_item (icon_view, item);
6547         }
6548     }
6549 }
6550 
6551 
6552 
6553 /**
6554  * exo_icon_view_get_orientation:
6555  * @icon_view : a #ExoIconView
6556  *
6557  * Returns the value of the ::orientation property which determines
6558  * whether the labels are drawn beside the icons instead of below.
6559  *
6560  * Return value: the relative position of texts and icons
6561  *
6562  * Since: 0.3.1
6563  **/
6564 GtkOrientation
6565 exo_icon_view_get_orientation (const ExoIconView *icon_view)
6566 {
6567   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), GTK_ORIENTATION_VERTICAL);
6568   return icon_view->priv->orientation;
6569 }
6570 
6571 
6572 
6573 /**
6574  * exo_icon_view_set_orientation:
6575  * @icon_view   : a #ExoIconView
6576  * @orientation : the relative position of texts and icons
6577  *
6578  * Sets the ::orientation property which determines whether the labels
6579  * are drawn beside the icons instead of below.
6580  *
6581  * Since: 0.3.1
6582  **/
6583 void
6584 exo_icon_view_set_orientation (ExoIconView   *icon_view,
6585                                GtkOrientation orientation)
6586 {
6587   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6588 
6589   if (G_LIKELY (icon_view->priv->orientation != orientation))
6590     {
6591       icon_view->priv->orientation = orientation;
6592 
6593       exo_icon_view_stop_editing (icon_view, TRUE);
6594       exo_icon_view_invalidate_sizes (icon_view);
6595 
6596       update_text_cell (icon_view);
6597       update_pixbuf_cell (icon_view);
6598 
6599       g_object_notify (G_OBJECT (icon_view), "orientation");
6600     }
6601 }
6602 
6603 
6604 
6605 /**
6606  * exo_icon_view_get_columns:
6607  * @icon_view: a #ExoIconView
6608  *
6609  * Returns the value of the ::columns property.
6610  *
6611  * Return value: the number of columns, or -1
6612  */
6613 gint
6614 exo_icon_view_get_columns (const ExoIconView *icon_view)
6615 {
6616   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), -1);
6617   return icon_view->priv->columns;
6618 }
6619 
6620 
6621 
6622 /**
6623  * exo_icon_view_set_columns:
6624  * @icon_view : a #ExoIconView
6625  * @columns   : the number of columns
6626  *
6627  * Sets the ::columns property which determines in how
6628  * many columns the icons are arranged. If @columns is
6629  * -1, the number of columns will be chosen automatically
6630  * to fill the available area.
6631  *
6632  * Since: 0.3.1
6633  */
6634 void
6635 exo_icon_view_set_columns (ExoIconView *icon_view,
6636                            gint         columns)
6637 {
6638   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6639 
6640   if (G_LIKELY (icon_view->priv->columns != columns))
6641     {
6642       icon_view->priv->columns = columns;
6643 
6644       exo_icon_view_stop_editing (icon_view, TRUE);
6645       exo_icon_view_queue_layout (icon_view);
6646 
6647       g_object_notify (G_OBJECT (icon_view), "columns");
6648     }
6649 }
6650 
6651 
6652 
6653 /**
6654  * exo_icon_view_get_item_width:
6655  * @icon_view: a #ExoIconView
6656  *
6657  * Returns the value of the ::item-width property.
6658  *
6659  * Return value: the width of a single item, or -1
6660  *
6661  * Since: 0.3.1
6662  */
6663 gint
6664 exo_icon_view_get_item_width (const ExoIconView *icon_view)
6665 {
6666   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), -1);
6667   return icon_view->priv->item_width;
6668 }
6669 
6670 
6671 
6672 /**
6673  * exo_icon_view_set_item_width:
6674  * @icon_view  : a #ExoIconView
6675  * @item_width : the width for each item
6676  *
6677  * Sets the ::item-width property which specifies the width
6678  * to use for each item. If it is set to -1, the icon view will
6679  * automatically determine a suitable item size.
6680  *
6681  * Since: 0.3.1
6682  */
6683 void
6684 exo_icon_view_set_item_width (ExoIconView *icon_view,
6685                               gint         item_width)
6686 {
6687   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6688 
6689   if (icon_view->priv->item_width != item_width)
6690     {
6691       icon_view->priv->item_width = item_width;
6692 
6693       exo_icon_view_stop_editing (icon_view, TRUE);
6694       exo_icon_view_invalidate_sizes (icon_view);
6695 
6696       update_text_cell (icon_view);
6697 
6698       g_object_notify (G_OBJECT (icon_view), "item-width");
6699     }
6700 }
6701 
6702 
6703 
6704 /**
6705  * exo_icon_view_get_spacing:
6706  * @icon_view: a #ExoIconView
6707  *
6708  * Returns the value of the ::spacing property.
6709  *
6710  * Return value: the space between cells
6711  *
6712  * Since: 0.3.1
6713  */
6714 gint
6715 exo_icon_view_get_spacing (const ExoIconView *icon_view)
6716 {
6717   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), -1);
6718   return icon_view->priv->spacing;
6719 }
6720 
6721 
6722 
6723 /**
6724  * exo_icon_view_set_spacing:
6725  * @icon_view : a #ExoIconView
6726  * @spacing   : the spacing
6727  *
6728  * Sets the ::spacing property which specifies the space
6729  * which is inserted between the cells (i.e. the icon and
6730  * the text) of an item.
6731  *
6732  * Since: 0.3.1
6733  */
6734 void
6735 exo_icon_view_set_spacing (ExoIconView *icon_view,
6736                            gint         spacing)
6737 {
6738   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6739 
6740   if (G_LIKELY (icon_view->priv->spacing != spacing))
6741     {
6742       icon_view->priv->spacing = spacing;
6743 
6744       exo_icon_view_stop_editing (icon_view, TRUE);
6745       exo_icon_view_invalidate_sizes (icon_view);
6746 
6747       g_object_notify (G_OBJECT (icon_view), "spacing");
6748     }
6749 }
6750 
6751 
6752 
6753 /**
6754  * exo_icon_view_get_row_spacing:
6755  * @icon_view: a #ExoIconView
6756  *
6757  * Returns the value of the ::row-spacing property.
6758  *
6759  * Return value: the space between rows
6760  *
6761  * Since: 0.3.1
6762  */
6763 gint
6764 exo_icon_view_get_row_spacing (const ExoIconView *icon_view)
6765 {
6766   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), -1);
6767   return icon_view->priv->row_spacing;
6768 }
6769 
6770 
6771 
6772 /**
6773  * exo_icon_view_set_row_spacing:
6774  * @icon_view   : a #ExoIconView
6775  * @row_spacing : the row spacing
6776  *
6777  * Sets the ::row-spacing property which specifies the space
6778  * which is inserted between the rows of the icon view.
6779  *
6780  * Since: 0.3.1
6781  */
6782 void
6783 exo_icon_view_set_row_spacing (ExoIconView *icon_view,
6784                                gint         row_spacing)
6785 {
6786   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6787 
6788   if (G_LIKELY (icon_view->priv->row_spacing != row_spacing))
6789     {
6790       icon_view->priv->row_spacing = row_spacing;
6791 
6792       exo_icon_view_stop_editing (icon_view, TRUE);
6793       exo_icon_view_invalidate_sizes (icon_view);
6794 
6795       g_object_notify (G_OBJECT (icon_view), "row-spacing");
6796     }
6797 }
6798 
6799 
6800 
6801 /**
6802  * exo_icon_view_get_column_spacing:
6803  * @icon_view: a #ExoIconView
6804  *
6805  * Returns the value of the ::column-spacing property.
6806  *
6807  * Return value: the space between columns
6808  *
6809  * Since: 0.3.1
6810  **/
6811 gint
6812 exo_icon_view_get_column_spacing (const ExoIconView *icon_view)
6813 {
6814   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), -1);
6815   return icon_view->priv->column_spacing;
6816 }
6817 
6818 
6819 
6820 /**
6821  * exo_icon_view_set_column_spacing:
6822  * @icon_view      : a #ExoIconView
6823  * @column_spacing : the column spacing
6824  *
6825  * Sets the ::column-spacing property which specifies the space
6826  * which is inserted between the columns of the icon view.
6827  *
6828  * Since: 0.3.1
6829  **/
6830 void
6831 exo_icon_view_set_column_spacing (ExoIconView *icon_view,
6832                                   gint         column_spacing)
6833 {
6834   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6835 
6836   if (G_LIKELY (icon_view->priv->column_spacing != column_spacing))
6837     {
6838       icon_view->priv->column_spacing = column_spacing;
6839 
6840       exo_icon_view_stop_editing (icon_view, TRUE);
6841       exo_icon_view_invalidate_sizes (icon_view);
6842 
6843       g_object_notify (G_OBJECT (icon_view), "column-spacing");
6844     }
6845 }
6846 
6847 
6848 
6849 /**
6850  * exo_icon_view_get_margin:
6851  * @icon_view : a #ExoIconView
6852  *
6853  * Returns the value of the ::margin property.
6854  *
6855  * Return value: the space at the borders
6856  *
6857  * Since: 0.3.1
6858  **/
6859 gint
6860 exo_icon_view_get_margin (const ExoIconView *icon_view)
6861 {
6862   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), -1);
6863   return icon_view->priv->margin;
6864 }
6865 
6866 
6867 
6868 /**
6869  * exo_icon_view_set_margin:
6870  * @icon_view : a #ExoIconView
6871  * @margin    : the margin
6872  *
6873  * Sets the ::margin property which specifies the space
6874  * which is inserted at the top, bottom, left and right
6875  * of the icon view.
6876  *
6877  * Since: 0.3.1
6878  **/
6879 void
6880 exo_icon_view_set_margin (ExoIconView *icon_view,
6881                           gint         margin)
6882 {
6883   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6884 
6885   if (G_LIKELY (icon_view->priv->margin != margin))
6886     {
6887       icon_view->priv->margin = margin;
6888 
6889       exo_icon_view_stop_editing (icon_view, TRUE);
6890       exo_icon_view_invalidate_sizes (icon_view);
6891 
6892       g_object_notify (G_OBJECT (icon_view), "margin");
6893     }
6894 }
6895 
6896 
6897 
6898 /* Get/set whether drag_motion requested the drag data and
6899  * drag_data_received should thus not actually insert the data,
6900  * since the data doesn't result from a drop.
6901  */
6902 static void
6903 set_status_pending (GdkDragContext *context,
6904                     GdkDragAction   suggested_action)
6905 {
6906   g_object_set_data (G_OBJECT (context),
6907                      I_("exo-icon-view-status-pending"),
6908                      GINT_TO_POINTER (suggested_action));
6909 }
6910 
6911 static GdkDragAction
6912 get_status_pending (GdkDragContext *context)
6913 {
6914   return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context), I_("exo-icon-view-status-pending")));
6915 }
6916 
6917 static void
6918 unset_reorderable (ExoIconView *icon_view)
6919 {
6920   if (icon_view->priv->reorderable)
6921     {
6922       icon_view->priv->reorderable = FALSE;
6923       g_object_notify (G_OBJECT (icon_view), "reorderable");
6924     }
6925 }
6926 
6927 static void
6928 clear_source_info (ExoIconView *icon_view)
6929 {
6930   if (icon_view->priv->source_targets)
6931     gtk_target_list_unref (icon_view->priv->source_targets);
6932   icon_view->priv->source_targets = NULL;
6933 
6934   icon_view->priv->source_set = FALSE;
6935 }
6936 
6937 static void
6938 clear_dest_info (ExoIconView *icon_view)
6939 {
6940   if (icon_view->priv->dest_targets)
6941     gtk_target_list_unref (icon_view->priv->dest_targets);
6942   icon_view->priv->dest_targets = NULL;
6943 
6944   icon_view->priv->dest_set = FALSE;
6945 }
6946 
6947 static void
6948 set_source_row (GdkDragContext *context,
6949                 GtkTreeModel   *model,
6950                 GtkTreePath    *source_row)
6951 {
6952   if (source_row)
6953     g_object_set_data_full (G_OBJECT (context),
6954                             I_("exo-icon-view-source-row"),
6955                             gtk_tree_row_reference_new (model, source_row),
6956                             (GDestroyNotify) gtk_tree_row_reference_free);
6957   else
6958     g_object_set_data_full (G_OBJECT (context),
6959                             I_("exo-icon-view-source-row"),
6960                             NULL, NULL);
6961 }
6962 
6963 static GtkTreePath*
6964 get_source_row (GdkDragContext *context)
6965 {
6966   GtkTreeRowReference *ref;
6967 
6968   ref = g_object_get_data (G_OBJECT (context), I_("exo-icon-view-source-row"));
6969 
6970   if (ref)
6971     return gtk_tree_row_reference_get_path (ref);
6972   else
6973     return NULL;
6974 }
6975 
6976 typedef struct
6977 {
6978   GtkTreeRowReference *dest_row;
6979   gboolean             empty_view_drop;
6980   gboolean             drop_append_mode;
6981 } DestRow;
6982 
6983 static void
6984 dest_row_free (gpointer data)
6985 {
6986   DestRow *dr = (DestRow *)data;
6987 
6988   gtk_tree_row_reference_free (dr->dest_row);
6989   g_slice_free (DestRow, dr);
6990 }
6991 
6992 static void
6993 set_dest_row (GdkDragContext *context,
6994               GtkTreeModel   *model,
6995               GtkTreePath    *dest_row,
6996               gboolean        empty_view_drop,
6997               gboolean        drop_append_mode)
6998 {
6999   DestRow *dr;
7000 
7001   if (!dest_row)
7002     {
7003       g_object_set_data_full (G_OBJECT (context),
7004                               I_("exo-icon-view-dest-row"),
7005                               NULL, NULL);
7006       return;
7007     }
7008 
7009   dr = g_slice_new0 (DestRow);
7010 
7011   dr->dest_row = gtk_tree_row_reference_new (model, dest_row);
7012   dr->empty_view_drop = empty_view_drop;
7013   dr->drop_append_mode = drop_append_mode;
7014   g_object_set_data_full (G_OBJECT (context),
7015                           I_("exo-icon-view-dest-row"),
7016                           dr, (GDestroyNotify) dest_row_free);
7017 }
7018 
7019 
7020 
7021 static GtkTreePath*
7022 get_dest_row (GdkDragContext *context)
7023 {
7024   DestRow *dr;
7025 
7026   dr = g_object_get_data (G_OBJECT (context), I_("exo-icon-view-dest-row"));
7027 
7028   if (dr)
7029     {
7030       GtkTreePath *path = NULL;
7031 
7032       if (dr->dest_row)
7033         path = gtk_tree_row_reference_get_path (dr->dest_row);
7034       else if (dr->empty_view_drop)
7035         path = gtk_tree_path_new_from_indices (0, -1);
7036       else
7037         path = NULL;
7038 
7039       if (path && dr->drop_append_mode)
7040         gtk_tree_path_next (path);
7041 
7042       return path;
7043     }
7044   else
7045     return NULL;
7046 }
7047 
7048 
7049 
7050 static gboolean
7051 check_model_dnd (GtkTreeModel *model,
7052                  GType         required_iface,
7053                  const gchar  *_signal)
7054 {
7055   if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface))
7056     {
7057       g_warning ("You must override the default '%s' handler "
7058                  "on ExoIconView when using models that don't support "
7059                  "the %s interface and enabling drag-and-drop. The simplest way to do this "
7060                  "is to connect to '%s' and call "
7061                  "g_signal_stop_emission_by_name() in your signal handler to prevent "
7062                  "the default handler from running. Look at the source code "
7063                  "for the default handler in gtkiconview.c to get an idea what "
7064                  "your handler should do. (gtkiconview.c is in the GTK+ source "
7065                  "code.) If you're using GTK+ from a language other than C, "
7066                  "there may be a more natural way to override default handlers, e.g. via derivation.",
7067                  _signal, g_type_name (required_iface), _signal);
7068       return FALSE;
7069     }
7070   else
7071     return TRUE;
7072 }
7073 
7074 
7075 
7076 static void
7077 remove_scroll_timeout (ExoIconView *icon_view)
7078 {
7079   if (icon_view->priv->scroll_timeout_id != 0)
7080     {
7081       g_source_remove (icon_view->priv->scroll_timeout_id);
7082 
7083       icon_view->priv->scroll_timeout_id = 0;
7084     }
7085 }
7086 
7087 
7088 
7089 static void
7090 exo_icon_view_autoscroll (ExoIconView *icon_view)
7091 {
7092   gint px, py, x, y, width, height;
7093   gint hoffset, voffset;
7094   gfloat value;
7095   GdkWindow *window = gtk_widget_get_window (GTK_WIDGET (icon_view));
7096 
7097   gdk_window_get_device_position (window,
7098                                   gdk_device_manager_get_client_pointer(
7099                                         gdk_display_get_device_manager(
7100                                             gdk_window_get_display(window))),
7101                                   &px, &py, NULL);
7102 #if GTK_CHECK_VERSION(3, 0, 0)
7103   gdk_window_get_geometry (window, &x, &y, &width, &height);
7104 #else
7105   gdk_window_get_geometry (window, &x, &y, &width, &height, NULL);
7106 #endif
7107 
7108   /* see if we are near the edge. */
7109   voffset = py - (y + 2 * SCROLL_EDGE_SIZE);
7110   if (voffset > 0)
7111     voffset = MAX (py - (y + height - 2 * SCROLL_EDGE_SIZE), 0);
7112 
7113   hoffset = px - (x + 2 * SCROLL_EDGE_SIZE);
7114   if (hoffset > 0)
7115     hoffset = MAX (px - (x + width - 2 * SCROLL_EDGE_SIZE), 0);
7116 
7117   if (voffset != 0)
7118     {
7119       value = CLAMP (gtk_adjustment_get_value(icon_view->priv->vadjustment) + voffset,
7120                      gtk_adjustment_get_lower(icon_view->priv->vadjustment),
7121                      gtk_adjustment_get_upper(icon_view->priv->vadjustment) - gtk_adjustment_get_page_size(icon_view->priv->vadjustment));
7122       gtk_adjustment_set_value (icon_view->priv->vadjustment, value);
7123     }
7124   if (hoffset != 0)
7125     {
7126       value = CLAMP (gtk_adjustment_get_value(icon_view->priv->hadjustment) + hoffset,
7127                      gtk_adjustment_get_lower(icon_view->priv->hadjustment),
7128                      gtk_adjustment_get_upper(icon_view->priv->hadjustment) - gtk_adjustment_get_page_size(icon_view->priv->hadjustment));
7129       gtk_adjustment_set_value (icon_view->priv->hadjustment, value);
7130     }
7131 }
7132 
7133 
7134 static gboolean
7135 drag_scroll_timeout (gpointer data)
7136 {
7137   ExoIconView *icon_view = EXO_ICON_VIEW (data);
7138 
7139   if(!g_source_is_destroyed(g_main_current_source()))
7140     exo_icon_view_autoscroll (icon_view);
7141 
7142   return TRUE;
7143 }
7144 
7145 
7146 static gboolean
7147 set_destination (ExoIconView    *icon_view,
7148                  GdkDragContext *context,
7149                  gint            x,
7150                  gint            y,
7151                  GdkDragAction  *suggested_action,
7152                  GdkAtom        *target)
7153 {
7154   GtkWidget *widget;
7155   GtkTreePath *path = NULL;
7156   ExoIconViewDropPosition pos;
7157   ExoIconViewDropPosition old_pos;
7158   GtkTreePath *old_dest_path = NULL;
7159   gboolean can_drop = FALSE;
7160 
7161   widget = GTK_WIDGET (icon_view);
7162 
7163   *suggested_action = 0;
7164   *target = GDK_NONE;
7165 
7166   if (!icon_view->priv->dest_set)
7167     {
7168       /* someone unset us as a drag dest, note that if
7169        * we return FALSE drag_leave isn't called
7170        */
7171 
7172       exo_icon_view_set_drag_dest_item (icon_view,
7173                                         NULL,
7174                                         EXO_ICON_VIEW_DROP_LEFT);
7175 
7176       remove_scroll_timeout (EXO_ICON_VIEW (widget));
7177 
7178       return FALSE; /* no longer a drop site */
7179     }
7180 
7181   *target = gtk_drag_dest_find_target (widget, context, icon_view->priv->dest_targets);
7182   if (*target == GDK_NONE)
7183     return FALSE;
7184 
7185   if (!exo_icon_view_get_dest_item_at_pos (icon_view, x, y, &path, &pos))
7186     {
7187       gint n_children;
7188       GtkTreeModel *model;
7189 
7190       /* the row got dropped on empty space, let's setup a special case
7191        */
7192 
7193       if (path)
7194         gtk_tree_path_free (path);
7195 
7196       model = exo_icon_view_get_model (icon_view);
7197 
7198       n_children = gtk_tree_model_iter_n_children (model, NULL);
7199       if (n_children)
7200         {
7201           pos = EXO_ICON_VIEW_DROP_BELOW;
7202           path = gtk_tree_path_new_from_indices (n_children - 1, -1);
7203         }
7204       else
7205         {
7206           pos = EXO_ICON_VIEW_DROP_ABOVE;
7207           path = gtk_tree_path_new_from_indices (0, -1);
7208         }
7209 
7210       can_drop = TRUE;
7211 
7212       goto out;
7213     }
7214 
7215   g_assert (path);
7216 
7217   exo_icon_view_get_drag_dest_item (icon_view,
7218                                     &old_dest_path,
7219                                     &old_pos);
7220 
7221   if (old_dest_path)
7222     gtk_tree_path_free (old_dest_path);
7223 
7224   if (TRUE /* FIXME if the location droppable predicate */)
7225     {
7226       can_drop = TRUE;
7227     }
7228 
7229 out:
7230   if (can_drop)
7231     {
7232       GtkWidget *source_widget;
7233 
7234       *suggested_action = gdk_drag_context_get_suggested_action (context);
7235       source_widget = gtk_drag_get_source_widget (context);
7236 
7237       if (source_widget == widget)
7238         {
7239           /* Default to MOVE, unless the user has
7240            * pressed ctrl or shift to affect available actions
7241            */
7242           if ((gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE) != 0)
7243             *suggested_action = GDK_ACTION_MOVE;
7244         }
7245 
7246       exo_icon_view_set_drag_dest_item (EXO_ICON_VIEW (widget),
7247                                         path, pos);
7248     }
7249   else
7250     {
7251       /* can't drop here */
7252       exo_icon_view_set_drag_dest_item (EXO_ICON_VIEW (widget),
7253                                         NULL,
7254                                         EXO_ICON_VIEW_DROP_LEFT);
7255     }
7256 
7257   if (path)
7258     gtk_tree_path_free (path);
7259 
7260   return TRUE;
7261 }
7262 
7263 static GtkTreePath*
7264 get_logical_destination (ExoIconView *icon_view,
7265                          gboolean    *drop_append_mode)
7266 {
7267   /* adjust path to point to the row the drop goes in front of */
7268   GtkTreePath *path = NULL;
7269   ExoIconViewDropPosition pos;
7270 
7271   *drop_append_mode = FALSE;
7272 
7273   exo_icon_view_get_drag_dest_item (icon_view, &path, &pos);
7274 
7275   if (path == NULL)
7276     return NULL;
7277 
7278   if (pos == EXO_ICON_VIEW_DROP_RIGHT ||
7279       pos == EXO_ICON_VIEW_DROP_BELOW)
7280     {
7281       GtkTreeIter iter;
7282       GtkTreeModel *model = icon_view->priv->model;
7283 
7284       if (!gtk_tree_model_get_iter (model, &iter, path) ||
7285           !gtk_tree_model_iter_next (model, &iter))
7286         *drop_append_mode = TRUE;
7287       else
7288         {
7289           *drop_append_mode = FALSE;
7290           gtk_tree_path_next (path);
7291         }
7292     }
7293 
7294   return path;
7295 }
7296 
7297 static gboolean
7298 exo_icon_view_maybe_begin_drag (ExoIconView    *icon_view,
7299                                 GdkEventMotion *event)
7300 {
7301   GdkDragContext *context;
7302   GtkTreePath *path = NULL;
7303   gint button;
7304   GtkTreeModel *model;
7305   gboolean retval = FALSE;
7306 
7307   if (!icon_view->priv->source_set)
7308     goto out;
7309 
7310   if (icon_view->priv->pressed_button < 0)
7311     goto out;
7312 
7313   if (!gtk_drag_check_threshold (GTK_WIDGET (icon_view),
7314                                  icon_view->priv->press_start_x,
7315                                  icon_view->priv->press_start_y,
7316                                  event->x, event->y))
7317     goto out;
7318 
7319   model = exo_icon_view_get_model (icon_view);
7320 
7321   if (model == NULL)
7322     goto out;
7323 
7324   button = icon_view->priv->pressed_button;
7325   icon_view->priv->pressed_button = -1;
7326 
7327   path = exo_icon_view_get_path_at_pos (icon_view,
7328                                         icon_view->priv->press_start_x,
7329                                         icon_view->priv->press_start_y);
7330 
7331   if (path == NULL)
7332     goto out;
7333 
7334   if (!GTK_IS_TREE_DRAG_SOURCE (model) ||
7335       !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model),
7336                                            path))
7337     goto out;
7338 
7339   /* FIXME Check whether we're a start button, if not return FALSE and
7340    * free path
7341    */
7342 
7343   /* Now we can begin the drag */
7344 
7345   retval = TRUE;
7346 
7347   context = gtk_drag_begin (GTK_WIDGET (icon_view),
7348                             icon_view->priv->source_targets,
7349                             icon_view->priv->source_actions,
7350                             button,
7351                             (GdkEvent*)event);
7352 
7353   set_source_row (context, model, path);
7354 
7355  out:
7356   if (path)
7357     gtk_tree_path_free (path);
7358 
7359   return retval;
7360 }
7361 
7362 /* Source side drag signals */
7363 static void
7364 exo_icon_view_drag_begin (GtkWidget      *widget,
7365                           GdkDragContext *context)
7366 {
7367   ExoIconView *icon_view;
7368   ExoIconViewItem *item;
7369   GdkPixbuf *icon;
7370   gint x, y;
7371   GtkTreePath *path;
7372 
7373   icon_view = EXO_ICON_VIEW (widget);
7374 
7375   /* if the user uses a custom DnD impl, we don't set the icon here */
7376   if (!icon_view->priv->dest_set && !icon_view->priv->source_set)
7377     return;
7378 
7379   item = exo_icon_view_get_item_at_coords (icon_view,
7380                                            icon_view->priv->press_start_x,
7381                                            icon_view->priv->press_start_y,
7382                                            TRUE,
7383                                            NULL);
7384 
7385   _exo_return_if_fail (item != NULL);
7386 
7387   x = icon_view->priv->press_start_x - item->area.x + 1;
7388   y = icon_view->priv->press_start_y - item->area.y + 1;
7389 
7390   path = gtk_tree_path_new_from_indices (item->index, -1);
7391   icon = exo_icon_view_create_drag_icon (icon_view, path);
7392   gtk_tree_path_free (path);
7393 
7394   gtk_drag_set_icon_pixbuf (context,
7395                             icon,
7396                             x, y);
7397 
7398   g_object_unref (icon);
7399 }
7400 
7401 static void
7402 exo_icon_view_drag_end (GtkWidget      *widget,
7403                         GdkDragContext *context)
7404 {
7405   /* do nothing */
7406 }
7407 
7408 static void
7409 exo_icon_view_drag_data_get (GtkWidget        *widget,
7410                              GdkDragContext   *context,
7411                              GtkSelectionData *selection_data,
7412                              guint             info,
7413                              guint             drag_time)
7414 {
7415   ExoIconView *icon_view;
7416   GtkTreeModel *model;
7417   GtkTreePath *source_row;
7418 
7419   icon_view = EXO_ICON_VIEW (widget);
7420   model = exo_icon_view_get_model (icon_view);
7421 
7422   if (model == NULL)
7423     return;
7424 
7425   if (!icon_view->priv->dest_set)
7426     return;
7427 
7428   source_row = get_source_row (context);
7429 
7430   if (source_row == NULL)
7431     return;
7432 
7433   /* We can implement the GTK_TREE_MODEL_ROW target generically for
7434    * any model; for DragSource models there are some other targets
7435    * we also support.
7436    */
7437 
7438   if (GTK_IS_TREE_DRAG_SOURCE (model) &&
7439       gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model),
7440                                           source_row,
7441                                           selection_data))
7442     goto done;
7443 
7444   /* If drag_data_get does nothing, try providing row data. */
7445   if (gtk_selection_data_get_target(selection_data) == gdk_atom_intern ("GTK_TREE_MODEL_ROW", FALSE))
7446     gtk_tree_set_row_drag_data (selection_data,
7447                                 model,
7448                                 source_row);
7449 
7450  done:
7451   gtk_tree_path_free (source_row);
7452 }
7453 
7454 static void
7455 exo_icon_view_drag_data_delete (GtkWidget      *widget,
7456                                 GdkDragContext *context)
7457 {
7458   GtkTreeModel *model;
7459   ExoIconView *icon_view;
7460   GtkTreePath *source_row;
7461 
7462   icon_view = EXO_ICON_VIEW (widget);
7463   model = exo_icon_view_get_model (icon_view);
7464 
7465   if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete"))
7466     return;
7467 
7468   if (!icon_view->priv->dest_set)
7469     return;
7470 
7471   source_row = get_source_row (context);
7472 
7473   if (source_row == NULL)
7474     return;
7475 
7476   gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model),
7477                                          source_row);
7478 
7479   gtk_tree_path_free (source_row);
7480 
7481   set_source_row (context, NULL, NULL);
7482 }
7483 
7484 /* Target side drag signals */
7485 static void
7486 exo_icon_view_drag_leave (GtkWidget      *widget,
7487                           GdkDragContext *context,
7488                           guint           drag_time)
7489 {
7490   ExoIconView *icon_view;
7491 
7492   icon_view = EXO_ICON_VIEW (widget);
7493 
7494   /* unset any highlight row */
7495   exo_icon_view_set_drag_dest_item (icon_view,
7496                                     NULL,
7497                                     EXO_ICON_VIEW_DROP_LEFT);
7498 
7499   remove_scroll_timeout (icon_view);
7500 }
7501 
7502 static gboolean
7503 exo_icon_view_drag_motion (GtkWidget      *widget,
7504                            GdkDragContext *context,
7505                            gint            x,
7506                            gint            y,
7507                            guint           drag_time)
7508 {
7509   ExoIconViewDropPosition pos;
7510   GdkDragAction           suggested_action = 0;
7511   GtkTreePath            *path = NULL;
7512   ExoIconView            *icon_view = EXO_ICON_VIEW (widget);
7513   gboolean                empty;
7514   GdkAtom                 target;
7515 
7516   if (!set_destination (icon_view, context, x, y, &suggested_action, &target))
7517     return FALSE;
7518 
7519   exo_icon_view_get_drag_dest_item (icon_view, &path, &pos);
7520 
7521   /* we only know this *after* set_desination_row */
7522   empty = icon_view->priv->empty_view_drop;
7523 
7524   if (path == NULL && !empty)
7525     {
7526       /* Can't drop here. */
7527       gdk_drag_status (context, 0, drag_time);
7528     }
7529   else
7530     {
7531       if (icon_view->priv->scroll_timeout_id == 0)
7532         icon_view->priv->scroll_timeout_id = gdk_threads_add_timeout (50, drag_scroll_timeout, icon_view);
7533 
7534       if (target == gdk_atom_intern ("GTK_TREE_MODEL_ROW", FALSE))
7535         {
7536           /* Request data so we can use the source row when
7537            * determining whether to accept the drop
7538            */
7539           set_status_pending (context, suggested_action);
7540           gtk_drag_get_data (widget, context, target, drag_time);
7541         }
7542       else
7543         {
7544           set_status_pending (context, 0);
7545           gdk_drag_status (context, suggested_action, drag_time);
7546         }
7547     }
7548 
7549   if (path != NULL)
7550     gtk_tree_path_free (path);
7551 
7552   return TRUE;
7553 }
7554 
7555 static gboolean
7556 exo_icon_view_drag_drop (GtkWidget      *widget,
7557                          GdkDragContext *context,
7558                          gint            x,
7559                          gint            y,
7560                          guint           drag_time)
7561 {
7562   ExoIconView *icon_view;
7563   GtkTreePath *path;
7564   GdkDragAction suggested_action = 0;
7565   GdkAtom target = GDK_NONE;
7566   GtkTreeModel *model;
7567   gboolean drop_append_mode;
7568 
7569   icon_view = EXO_ICON_VIEW (widget);
7570   model = exo_icon_view_get_model (icon_view);
7571 
7572   remove_scroll_timeout (EXO_ICON_VIEW (widget));
7573 
7574   if (!icon_view->priv->dest_set)
7575     return FALSE;
7576 
7577   if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_drop"))
7578     return FALSE;
7579 
7580   if (!set_destination (icon_view, context, x, y, &suggested_action, &target))
7581     return FALSE;
7582 
7583   path = get_logical_destination (icon_view, &drop_append_mode);
7584 
7585   if (target != GDK_NONE && path != NULL)
7586     {
7587       /* in case a motion had requested drag data, change things so we
7588        * treat drag data receives as a drop.
7589        */
7590       set_status_pending (context, 0);
7591       set_dest_row (context, model, path,
7592                     icon_view->priv->empty_view_drop, drop_append_mode);
7593     }
7594 
7595   if (path)
7596     gtk_tree_path_free (path);
7597 
7598   /* Unset this thing */
7599   exo_icon_view_set_drag_dest_item (icon_view, NULL, EXO_ICON_VIEW_DROP_LEFT);
7600 
7601   if (target != GDK_NONE)
7602     {
7603       gtk_drag_get_data (widget, context, target, drag_time);
7604       return TRUE;
7605     }
7606   else
7607     return FALSE;
7608 }
7609 
7610 static void
7611 exo_icon_view_drag_data_received (GtkWidget        *widget,
7612                                   GdkDragContext   *context,
7613                                   gint              x,
7614                                   gint              y,
7615                                   GtkSelectionData *selection_data,
7616                                   guint             info,
7617                                   guint             drag_time)
7618 {
7619   GtkTreePath *path;
7620   gboolean accepted = FALSE;
7621   GtkTreeModel *model;
7622   ExoIconView *icon_view;
7623   GtkTreePath *dest_row;
7624   GdkDragAction suggested_action;
7625   gboolean drop_append_mode;
7626 
7627   icon_view = EXO_ICON_VIEW (widget);
7628   model = exo_icon_view_get_model (icon_view);
7629 
7630   if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_data_received"))
7631     return;
7632 
7633   if (!icon_view->priv->dest_set)
7634     return;
7635 
7636   suggested_action = get_status_pending (context);
7637 
7638   if (suggested_action)
7639     {
7640       /* We are getting this data due to a request in drag_motion,
7641        * rather than due to a request in drag_drop, so we are just
7642        * supposed to call drag_status, not actually paste in the
7643        * data.
7644        */
7645       path = get_logical_destination (icon_view, &drop_append_mode);
7646 
7647       if (path == NULL)
7648         suggested_action = 0;
7649 
7650       if (suggested_action)
7651         {
7652           if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
7653                                                      path,
7654                                                      selection_data))
7655             suggested_action = 0;
7656         }
7657 
7658       gdk_drag_status (context, suggested_action, drag_time);
7659 
7660       if (path)
7661         gtk_tree_path_free (path);
7662 
7663       /* If you can't drop, remove user drop indicator until the next motion */
7664       if (suggested_action == 0)
7665         exo_icon_view_set_drag_dest_item (icon_view,
7666                                           NULL,
7667                                           EXO_ICON_VIEW_DROP_LEFT);
7668       return;
7669     }
7670 
7671 
7672   dest_row = get_dest_row (context);
7673 
7674   if (dest_row == NULL)
7675     return;
7676 
7677   if (gtk_selection_data_get_length(selection_data) >= 0)
7678     {
7679       if (gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model),
7680                                                  dest_row,
7681                                                  selection_data))
7682         accepted = TRUE;
7683     }
7684 
7685   gtk_drag_finish (context,
7686                    accepted,
7687                    (gdk_drag_context_get_selected_action (context) == GDK_ACTION_MOVE),
7688                    drag_time);
7689 
7690   gtk_tree_path_free (dest_row);
7691 
7692   /* drop dest_row */
7693   set_dest_row (context, NULL, NULL, FALSE, FALSE);
7694 }
7695 
7696 
7697 
7698 /**
7699  * exo_icon_view_enable_model_drag_source:
7700  * @icon_view         : a #GtkIconTreeView
7701  * @start_button_mask : Mask of allowed buttons to start drag
7702  * @targets           : the table of targets that the drag will support
7703  * @n_targets         : the number of items in @targets
7704  * @actions           : the bitmask of possible actions for a drag from this widget
7705  *
7706  * Turns @icon_view into a drag source for automatic DND.
7707  *
7708  * Since: 0.3.1
7709  **/
7710 void
7711 exo_icon_view_enable_model_drag_source (ExoIconView              *icon_view,
7712                                         GdkModifierType           start_button_mask,
7713                                         const GtkTargetEntry     *targets,
7714                                         gint                      n_targets,
7715                                         GdkDragAction             actions)
7716 {
7717   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
7718 
7719   gtk_drag_source_set (GTK_WIDGET (icon_view), 0, NULL, 0, actions);
7720 
7721   clear_source_info (icon_view);
7722   icon_view->priv->start_button_mask = start_button_mask;
7723   icon_view->priv->source_targets = gtk_target_list_new (targets, n_targets);
7724   icon_view->priv->source_actions = actions;
7725 
7726   icon_view->priv->source_set = TRUE;
7727 
7728   unset_reorderable (icon_view);
7729 }
7730 
7731 
7732 
7733 /**
7734  * exo_icon_view_enable_model_drag_dest:
7735  * @icon_view : a #ExoIconView
7736  * @targets   : the table of targets that the drag will support
7737  * @n_targets : the number of items in @targets
7738  * @actions   : the bitmask of possible actions for a drag from this widget
7739  *
7740  * Turns @icon_view into a drop destination for automatic DND.
7741  *
7742  * Since: 0.3.1
7743  **/
7744 void
7745 exo_icon_view_enable_model_drag_dest (ExoIconView          *icon_view,
7746                                       const GtkTargetEntry *targets,
7747                                       gint                  n_targets,
7748                                       GdkDragAction         actions)
7749 {
7750   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
7751 
7752   gtk_drag_dest_set (GTK_WIDGET (icon_view), 0, NULL, 0, actions);
7753 
7754   clear_dest_info (icon_view);
7755 
7756   icon_view->priv->dest_targets = gtk_target_list_new (targets, n_targets);
7757   icon_view->priv->dest_actions = actions;
7758 
7759   icon_view->priv->dest_set = TRUE;
7760 
7761   unset_reorderable (icon_view);
7762 }
7763 
7764 
7765 
7766 /**
7767  * exo_icon_view_unset_model_drag_source:
7768  * @icon_view : a #ExoIconView
7769  *
7770  * Undoes the effect of #exo_icon_view_enable_model_drag_source().
7771  *
7772  * Since: 0.3.1
7773  **/
7774 void
7775 exo_icon_view_unset_model_drag_source (ExoIconView *icon_view)
7776 {
7777   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
7778 
7779   if (icon_view->priv->source_set)
7780     {
7781       gtk_drag_source_unset (GTK_WIDGET (icon_view));
7782       clear_source_info (icon_view);
7783     }
7784 
7785   unset_reorderable (icon_view);
7786 }
7787 
7788 
7789 
7790 /**
7791  * exo_icon_view_unset_model_drag_dest:
7792  * @icon_view : a #ExoIconView
7793  *
7794  * Undoes the effect of #exo_icon_view_enable_model_drag_dest().
7795  *
7796  * Since: 0.3.1
7797  **/
7798 void
7799 exo_icon_view_unset_model_drag_dest (ExoIconView *icon_view)
7800 {
7801   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
7802 
7803   if (icon_view->priv->dest_set)
7804     {
7805       gtk_drag_dest_unset (GTK_WIDGET (icon_view));
7806       clear_dest_info (icon_view);
7807     }
7808 
7809   unset_reorderable (icon_view);
7810 }
7811 
7812 
7813 
7814 /**
7815  * exo_icon_view_set_drag_dest_item:
7816  * @icon_view : a #ExoIconView
7817  * @path      : The path of the item to highlight, or %NULL.
7818  * @pos       : Specifies whether to drop, relative to the item
7819  *
7820  * Sets the item that is highlighted for feedback.
7821  *
7822  * Since: 0.3.1
7823  */
7824 void
7825 exo_icon_view_set_drag_dest_item (ExoIconView            *icon_view,
7826                                   GtkTreePath            *path,
7827                                   ExoIconViewDropPosition pos)
7828 {
7829   ExoIconViewItem *item;
7830   GtkTreePath     *previous_path;
7831 
7832   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
7833 
7834   /* Note; this function is exported to allow a custom DND
7835    * implementation, so it can't touch TreeViewDragInfo
7836    */
7837 
7838   if (icon_view->priv->dest_item != NULL)
7839     {
7840       /* determine and reset the previous path */
7841       previous_path = gtk_tree_row_reference_get_path (icon_view->priv->dest_item);
7842       gtk_tree_row_reference_free (icon_view->priv->dest_item);
7843       icon_view->priv->dest_item = NULL;
7844 
7845       /* check if the path is still valid */
7846       if (G_LIKELY (previous_path != NULL))
7847         {
7848           /* schedule a redraw for the previous path */
7849           item = g_list_nth_data (icon_view->priv->items, gtk_tree_path_get_indices (previous_path)[0]);
7850           if (G_LIKELY (item != NULL))
7851             exo_icon_view_queue_draw_item (icon_view, item);
7852           gtk_tree_path_free (previous_path);
7853         }
7854     }
7855 
7856   /* special case a drop on an empty model */
7857   icon_view->priv->empty_view_drop = FALSE;
7858   if (pos == EXO_ICON_VIEW_NO_DROP && path
7859       && gtk_tree_path_get_depth (path) == 1
7860       && gtk_tree_path_get_indices (path)[0] == 0)
7861     {
7862       gint n_children;
7863 
7864       n_children = gtk_tree_model_iter_n_children (icon_view->priv->model,
7865                                                    NULL);
7866 
7867       if (n_children == 0)
7868         icon_view->priv->empty_view_drop = TRUE;
7869     }
7870 
7871   icon_view->priv->dest_pos = pos;
7872 
7873   if (G_LIKELY (path != NULL))
7874     {
7875       /* take a row reference for the new item path */
7876       icon_view->priv->dest_item = gtk_tree_row_reference_new_proxy (G_OBJECT (icon_view), icon_view->priv->model, path);
7877 
7878       /* schedule a redraw on the new path */
7879       item = g_list_nth_data (icon_view->priv->items, gtk_tree_path_get_indices (path)[0]);
7880       if (G_LIKELY (item != NULL))
7881         exo_icon_view_queue_draw_item (icon_view, item);
7882     }
7883 }
7884 
7885 
7886 
7887 /**
7888  * exo_icon_view_get_drag_dest_item:
7889  * @icon_view : a #ExoIconView
7890  * @path      : Return location for the path of the highlighted item, or %NULL.
7891  * @pos       : Return location for the drop position, or %NULL
7892  *
7893  * Gets information about the item that is highlighted for feedback.
7894  *
7895  * Since: 0.3.1
7896  **/
7897 void
7898 exo_icon_view_get_drag_dest_item (ExoIconView              *icon_view,
7899                                   GtkTreePath             **path,
7900                                   ExoIconViewDropPosition  *pos)
7901 {
7902   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
7903 
7904   if (path)
7905     {
7906       if (icon_view->priv->dest_item)
7907         *path = gtk_tree_row_reference_get_path (icon_view->priv->dest_item);
7908       else
7909         *path = NULL;
7910     }
7911 
7912   if (pos)
7913     *pos = icon_view->priv->dest_pos;
7914 }
7915 
7916 
7917 
7918 /**
7919  * exo_icon_view_get_dest_item_at_pos:
7920  * @icon_view : a #ExoIconView
7921  * @drag_x    : the position to determine the destination item for
7922  * @drag_y    : the position to determine the destination item for
7923  * @path      : Return location for the path of the highlighted item, or %NULL.
7924  * @pos       : Return location for the drop position, or %NULL
7925  *
7926  * Determines the destination item for a given position.
7927  *
7928  * Both @drag_x and @drag_y are given in icon window coordinates. Use
7929  * #exo_icon_view_widget_to_icon_coords() if you need to translate
7930  * widget coordinates first.
7931  *
7932  * Return value: whether there is an item at the given position.
7933  *
7934  * Since: 0.3.1
7935  **/
7936 gboolean
7937 exo_icon_view_get_dest_item_at_pos (ExoIconView              *icon_view,
7938                                     gint                      drag_x,
7939                                     gint                      drag_y,
7940                                     GtkTreePath             **path,
7941                                     ExoIconViewDropPosition  *pos)
7942 {
7943   ExoIconViewItem *item;
7944 
7945   /* Note; this function is exported to allow a custom DND
7946    * implementation, so it can't touch TreeViewDragInfo
7947    */
7948 
7949   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
7950   g_return_val_if_fail (drag_x >= 0, FALSE);
7951   g_return_val_if_fail (drag_y >= 0, FALSE);
7952   g_return_val_if_fail (icon_view->priv->bin_window != NULL, FALSE);
7953 
7954   if (G_LIKELY (path != NULL))
7955     *path = NULL;
7956 
7957   item = exo_icon_view_get_item_at_coords (icon_view, drag_x, drag_y, FALSE, NULL);
7958 
7959   if (G_UNLIKELY (item == NULL))
7960     return FALSE;
7961 
7962   if (G_LIKELY (path != NULL))
7963     *path = gtk_tree_path_new_from_indices (item->index, -1);
7964 
7965   if (G_LIKELY (pos != NULL))
7966     {
7967       if (drag_x < item->area.x + item->area.width / 4)
7968         *pos = EXO_ICON_VIEW_DROP_LEFT;
7969       else if (drag_x > item->area.x + item->area.width * 3 / 4)
7970         *pos = EXO_ICON_VIEW_DROP_RIGHT;
7971       else if (drag_y < item->area.y + item->area.height / 4)
7972         *pos = EXO_ICON_VIEW_DROP_ABOVE;
7973       else if (drag_y > item->area.y + item->area.height * 3 / 4)
7974         *pos = EXO_ICON_VIEW_DROP_BELOW;
7975       else
7976         *pos = EXO_ICON_VIEW_DROP_INTO;
7977     }
7978 
7979   return TRUE;
7980 }
7981 
7982 
7983 
7984 /**
7985  * exo_icon_view_create_drag_icon:
7986  * @icon_view : a #ExoIconView
7987  * @path      : a #GtkTreePath in @icon_view
7988  *
7989  * Creates a #GdkPixbuf representation of the item at @path.
7990  * This image is used for a drag icon.
7991  *
7992  * Return value: a newly-allocated pixmap of the drag icon.
7993  *
7994  * Since: 0.3.1
7995  **/
7996 GdkPixbuf*
7997 exo_icon_view_create_drag_icon (ExoIconView *icon_view,
7998                                 GtkTreePath *path)
7999 {
8000   GdkRectangle area;
8001   GtkWidget   *widget = GTK_WIDGET (icon_view);
8002 #if GTK_CHECK_VERSION(3, 0, 0)
8003   cairo_surface_t *s;
8004 #else
8005   GdkPixmap   *drawable;
8006 #endif
8007   GdkPixbuf   *pixbuf;
8008   cairo_t     *cr;
8009   GList       *lp;
8010   GtkStyle    *style;
8011   gint         idx;
8012 
8013   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), NULL);
8014   g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, NULL);
8015 
8016   /* verify that the widget is realized */
8017   if (G_UNLIKELY (!gtk_widget_get_realized (GTK_WIDGET (icon_view))))
8018     return NULL;
8019 
8020   idx = gtk_tree_path_get_indices (path)[0];
8021   style = gtk_widget_get_style (widget);
8022 
8023   for (lp = icon_view->priv->items; lp != NULL; lp = lp->next)
8024     {
8025       ExoIconViewItem *item = lp->data;
8026       if (G_UNLIKELY (idx == item->index))
8027         {
8028 #if GTK_CHECK_VERSION(3, 0, 0)
8029           s = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
8030                                           item->area.width + 2,
8031                                           item->area.height + 2);
8032 
8033           cr = cairo_create (s);
8034 #else
8035           drawable = gdk_pixmap_new (icon_view->priv->bin_window,
8036                                      item->area.width + 2,
8037                                      item->area.height + 2,
8038                                      -1);
8039 
8040           cr = gdk_cairo_create (drawable);
8041 #endif
8042           gdk_cairo_set_source_color (cr, &style->base[gtk_widget_get_state (widget)]);
8043           cairo_rectangle (cr, 0, 0, item->area.width + 2, item->area.height + 2);
8044           cairo_fill (cr);
8045 
8046           area.x = 0;
8047           area.y = 0;
8048           area.width = item->area.width;
8049           area.height = item->area.height;
8050 
8051 #if GTK_CHECK_VERSION(3, 0, 0)
8052           exo_icon_view_paint_item (icon_view, item, &area, cr, 1, 1, FALSE);
8053 #else
8054           /* NOTE: this is inefficient but Gtk+2 uses GtkWindow for render */
8055           exo_icon_view_paint_item (icon_view, item, &area, drawable, 1, 1, FALSE);
8056 #endif
8057 
8058           gdk_cairo_set_source_color (cr, &style->black);
8059           cairo_rectangle (cr, 1, 1, item->area.width + 1, item->area.height + 1);
8060           cairo_stroke (cr);
8061 
8062           cairo_destroy (cr);
8063 
8064 #if GTK_CHECK_VERSION(3, 0, 0)
8065           pixbuf = gdk_pixbuf_get_from_surface (s, 0, 0,
8066                                                 item->area.width + 2,
8067                                                 item->area.height + 2);
8068           cairo_surface_destroy (s);
8069 
8070           return pixbuf;
8071 #else
8072           pixbuf = gdk_pixbuf_get_from_drawable (NULL, drawable,
8073                                                  gdk_drawable_get_colormap (drawable),
8074                                                  0, 0, 0, 0,
8075                                                  item->area.width + 2,
8076                                                  item->area.height + 2);
8077           g_object_unref (drawable);
8078           return pixbuf;
8079 #endif
8080         }
8081     }
8082 
8083   return NULL;
8084 }
8085 
8086 
8087 
8088 /**
8089  * exo_icon_view_get_reorderable:
8090  * @icon_view : a #ExoIconView
8091  *
8092  * Retrieves whether the user can reorder the list via drag-and-drop.
8093  * See exo_icon_view_set_reorderable().
8094  *
8095  * Return value: %TRUE if the list can be reordered.
8096  *
8097  * Since: 0.3.1
8098  **/
8099 gboolean
8100 exo_icon_view_get_reorderable (ExoIconView *icon_view)
8101 {
8102   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
8103 
8104   return icon_view->priv->reorderable;
8105 }
8106 
8107 
8108 
8109 /**
8110  * exo_icon_view_set_reorderable:
8111  * @icon_view   : A #ExoIconView.
8112  * @reorderable : %TRUE, if the list of items can be reordered.
8113  *
8114  * This function is a convenience function to allow you to reorder models that
8115  * support the #GtkTreeDragSourceIface and the #GtkTreeDragDestIface.  Both
8116  * #GtkTreeStore and #GtkListStore support these.  If @reorderable is %TRUE, then
8117  * the user can reorder the model by dragging and dropping rows.  The
8118  * developer can listen to these changes by connecting to the model's
8119  * ::row-inserted and ::row-deleted signals.
8120  *
8121  * This function does not give you any degree of control over the order -- any
8122  * reordering is allowed.  If more control is needed, you should probably
8123  * handle drag and drop manually.
8124  *
8125  * Since: 0.3.1
8126  **/
8127 void
8128 exo_icon_view_set_reorderable (ExoIconView *icon_view,
8129                                gboolean     reorderable)
8130 {
8131   static const GtkTargetEntry item_targets[] =
8132   {
8133     { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0, },
8134   };
8135 
8136   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
8137 
8138   reorderable = (reorderable != FALSE);
8139 
8140   if (G_UNLIKELY (icon_view->priv->reorderable == reorderable))
8141     return;
8142 
8143   if (G_LIKELY (reorderable))
8144     {
8145       exo_icon_view_enable_model_drag_source (icon_view, GDK_BUTTON1_MASK, item_targets, G_N_ELEMENTS (item_targets), GDK_ACTION_MOVE);
8146       exo_icon_view_enable_model_drag_dest (icon_view, item_targets, G_N_ELEMENTS (item_targets), GDK_ACTION_MOVE);
8147     }
8148   else
8149     {
8150       exo_icon_view_unset_model_drag_source (icon_view);
8151       exo_icon_view_unset_model_drag_dest (icon_view);
8152     }
8153 
8154   icon_view->priv->reorderable = reorderable;
8155 
8156   g_object_notify (G_OBJECT (icon_view), "reorderable");
8157 }
8158 
8159 
8160 
8161 /*----------------------*
8162  * Single-click support *
8163  *----------------------*/
8164 
8165 /**
8166  * exo_icon_view_get_single_click:
8167  * @icon_view : a #ExoIconView.
8168  *
8169  * Returns %TRUE if @icon_view is currently in single click mode,
8170  * else %FALSE will be returned.
8171  *
8172  * Return value: whether @icon_view is currently in single click mode.
8173  *
8174  * Since: 0.3.1.3
8175  **/
8176 gboolean
8177 exo_icon_view_get_single_click (const ExoIconView *icon_view)
8178 {
8179   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
8180   return icon_view->priv->single_click;
8181 }
8182 
8183 
8184 
8185 /**
8186  * exo_icon_view_set_single_click:
8187  * @icon_view    : a #ExoIconView.
8188  * @single_click : %TRUE for single click, %FALSE for double click mode.
8189  *
8190  * If @single_click is %TRUE, @icon_view will be in single click mode
8191  * afterwards, else @icon_view will be in double click mode.
8192  *
8193  * Since: 0.3.1.3
8194  **/
8195 void
8196 exo_icon_view_set_single_click (ExoIconView *icon_view,
8197                                 gboolean     single_click)
8198 {
8199   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
8200 
8201   /* normalize the value */
8202   single_click = !!single_click;
8203 
8204   /* check if we have a new setting here */
8205   if (icon_view->priv->single_click != single_click)
8206     {
8207       icon_view->priv->single_click = single_click;
8208       g_object_notify (G_OBJECT (icon_view), "single-click");
8209     }
8210 }
8211 
8212 
8213 
8214 /**
8215  * exo_icon_view_get_single_click_timeout:
8216  * @icon_view : a #ExoIconView.
8217  *
8218  * Returns the amount of time in milliseconds after which the
8219  * item under the mouse cursor will be selected automatically
8220  * in single click mode. A value of %0 means that the behavior
8221  * is disabled and the user must alter the selection manually.
8222  *
8223  * Return value: the single click autoselect timeout or %0 if
8224  *               the behavior is disabled.
8225  *
8226  * Since: 0.3.1.5
8227  **/
8228 guint
8229 exo_icon_view_get_single_click_timeout (const ExoIconView *icon_view)
8230 {
8231   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), 0u);
8232   return icon_view->priv->single_click_timeout;
8233 }
8234 
8235 
8236 
8237 /**
8238  * exo_icon_view_set_single_click_timeout:
8239  * @icon_view            : a #ExoIconView.
8240  * @single_click_timeout : the new timeout or %0 to disable.
8241  *
8242  * If @single_click_timeout is a value greater than zero, it specifies
8243  * the amount of time in milliseconds after which the item under the
8244  * mouse cursor will be selected automatically in single click mode.
8245  * A value of %0 for @single_click_timeout disables the autoselection
8246  * for @icon_view.
8247  *
8248  * This setting does not have any effect unless the @icon_view is in
8249  * single-click mode, see exo_icon_view_set_single_click().
8250  *
8251  * Since: 0.3.1.5
8252  **/
8253 void
8254 exo_icon_view_set_single_click_timeout (ExoIconView *icon_view,
8255                                         guint        single_click_timeout)
8256 {
8257   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
8258 
8259   /* check if we have a new setting */
8260   if (icon_view->priv->single_click_timeout != single_click_timeout)
8261     {
8262       /* apply the new setting */
8263       icon_view->priv->single_click_timeout = single_click_timeout;
8264 
8265       /* be sure to cancel any pending single click timeout */
8266       if (G_UNLIKELY (icon_view->priv->single_click_timeout_id != 0))
8267         g_source_remove (icon_view->priv->single_click_timeout_id);
8268 
8269       /* notify listeners */
8270       g_object_notify (G_OBJECT (icon_view), "single-click-timeout");
8271     }
8272 }
8273 
8274 
8275 
8276 static gboolean
8277 exo_icon_view_single_click_timeout (gpointer user_data)
8278 {
8279   ExoIconViewItem *item;
8280   gboolean         dirty = FALSE;
8281   ExoIconView     *icon_view = EXO_ICON_VIEW (user_data);
8282 
8283   /* ensure that source isn't removed yet */
8284   if(g_source_is_destroyed(g_main_current_source()))
8285       return FALSE;
8286 
8287   /* verify that we are in single-click mode, have focus and a prelit item */
8288   if (gtk_widget_has_focus (GTK_WIDGET (icon_view)) && icon_view->priv->single_click && icon_view->priv->prelit_item != NULL)
8289     {
8290       /* work on the prelit item */
8291       item = icon_view->priv->prelit_item;
8292 
8293       /* be sure the item is fully visible */
8294       exo_icon_view_scroll_to_item (icon_view, item);
8295 
8296       /* change the selection appropriately */
8297       if (G_UNLIKELY (icon_view->priv->selection_mode == GTK_SELECTION_NONE))
8298         {
8299           exo_icon_view_set_cursor_item (icon_view, item, -1);
8300         }
8301       else if ((icon_view->priv->single_click_timeout_state & GDK_SHIFT_MASK) != 0
8302             && icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE)
8303         {
8304           if (!(icon_view->priv->single_click_timeout_state & GDK_CONTROL_MASK))
8305             /* unselect all previously selected items */
8306             exo_icon_view_unselect_all_internal (icon_view);
8307 
8308           /* select all items between the anchor and the prelit item */
8309           exo_icon_view_set_cursor_item (icon_view, item, -1);
8310           if (icon_view->priv->anchor_item == NULL)
8311             icon_view->priv->anchor_item = item;
8312           else
8313             exo_icon_view_select_all_between (icon_view, icon_view->priv->anchor_item, item);
8314 
8315           /* selection was changed */
8316           dirty = TRUE;
8317         }
8318       else
8319         {
8320           if ((icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE ||
8321               ((icon_view->priv->selection_mode == GTK_SELECTION_SINGLE) && item->selected)) &&
8322               (icon_view->priv->single_click_timeout_state & GDK_CONTROL_MASK) != 0)
8323             {
8324               item->selected = !item->selected;
8325               exo_icon_view_queue_draw_item (icon_view, item);
8326               dirty = TRUE;
8327             }
8328           else if (!item->selected)
8329             {
8330               exo_icon_view_unselect_all_internal (icon_view);
8331               exo_icon_view_queue_draw_item (icon_view, item);
8332               item->selected = TRUE;
8333               dirty = TRUE;
8334             }
8335           exo_icon_view_set_cursor_item (icon_view, item, -1);
8336           icon_view->priv->anchor_item = item;
8337         }
8338     }
8339 
8340   /* emit "selection-changed" and stop drawing keyboard
8341    * focus indicator if the selection was altered
8342    */
8343   if (G_LIKELY (dirty))
8344     {
8345       /* reset "draw keyfocus" flag */
8346       EXO_ICON_VIEW_UNSET_FLAG (icon_view, EXO_ICON_VIEW_DRAW_KEYFOCUS);
8347 
8348       /* emit "selection-changed" */
8349       g_signal_emit (G_OBJECT (icon_view), icon_view_signals[SELECTION_CHANGED], 0);
8350     }
8351 
8352   return FALSE;
8353 }
8354 
8355 
8356 
8357 static void
8358 exo_icon_view_single_click_timeout_destroy (gpointer user_data)
8359 {
8360   EXO_ICON_VIEW (user_data)->priv->single_click_timeout_id = 0;
8361 }
8362 
8363 
8364 
8365 /*----------------------------*
8366  * Interactive search support *
8367  *----------------------------*/
8368 
8369 /**
8370  * exo_icon_view_get_enable_search:
8371  * @icon_view : an #ExoIconView.
8372  *
8373  * Returns whether or not the @icon_view allows to start
8374  * interactive searching by typing in text.
8375  *
8376  * Return value: whether or not to let the user search
8377  *               interactively.
8378  *
8379  * Since: 0.3.1.3
8380  **/
8381 gboolean
8382 exo_icon_view_get_enable_search (const ExoIconView *icon_view)
8383 {
8384   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
8385   return icon_view->priv->enable_search;
8386 }
8387 
8388 
8389 
8390 /**
8391  * exo_icon_view_set_enable_search:
8392  * @icon_view     : an #ExoIconView.
8393  * @enable_search : %TRUE if the user can search interactively.
8394  *
8395  * If @enable_search is set, then the user can type in text to search through
8396  * the @icon_view interactively (this is sometimes called "typeahead find").
8397  *
8398  * Note that even if this is %FALSE, the user can still initiate a search
8399  * using the "start-interactive-search" key binding.
8400  *
8401  * Since: 0.3.1.3
8402  **/
8403 void
8404 exo_icon_view_set_enable_search (ExoIconView *icon_view,
8405                                  gboolean     enable_search)
8406 {
8407   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
8408 
8409   enable_search = !!enable_search;
8410 
8411   if (G_LIKELY (icon_view->priv->enable_search != enable_search))
8412     {
8413       icon_view->priv->enable_search = enable_search;
8414       g_object_notify (G_OBJECT (icon_view), "enable-search");
8415     }
8416 }
8417 
8418 
8419 
8420 /**
8421  * exo_icon_view_get_search_column:
8422  * @icon_view : an #ExoIconView.
8423  *
8424  * Returns the column searched on by the interactive search code.
8425  *
8426  * Return value: the column the interactive search code searches in.
8427  *
8428  * Since: 0.3.1.3
8429  **/
8430 gint
8431 exo_icon_view_get_search_column (const ExoIconView *icon_view)
8432 {
8433   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), -1);
8434   return icon_view->priv->search_column;
8435 }
8436 
8437 
8438 
8439 /**
8440  * exo_icon_view_set_search_column:
8441  * @icon_view     : an #ExoIconView.
8442  * @search_column : the column of the model to search in, or -1 to disable searching.
8443  *
8444  * Sets @search_column as the column where the interactive search code should search in.
8445  *
8446  * If the search column is set, user can use the "start-interactive-search" key
8447  * binding to bring up search popup. The "enable-search" property controls
8448  * whether simply typing text will also start an interactive search.
8449  *
8450  * Note that @search_column refers to a column of the model.
8451  *
8452  * Since: 0.3.1.3
8453  **/
8454 void
8455 exo_icon_view_set_search_column (ExoIconView *icon_view,
8456                                  gint         search_column)
8457 {
8458   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
8459   g_return_if_fail (search_column >= -1);
8460 
8461   if (G_LIKELY (icon_view->priv->search_column != search_column))
8462     {
8463       icon_view->priv->search_column = search_column;
8464       g_object_notify (G_OBJECT (icon_view), "search-column");
8465     }
8466 }
8467 
8468 
8469 
8470 /**
8471  * exo_icon_view_get_search_equal_func:
8472  * @icon_view : an #ExoIconView.
8473  *
8474  * Returns the compare function currently in use.
8475  *
8476  * Return value: the currently used compare function for the search code.
8477  *
8478  * Since: 0.3.1.3
8479  **/
8480 ExoIconViewSearchEqualFunc
8481 exo_icon_view_get_search_equal_func (const ExoIconView *icon_view)
8482 {
8483   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), NULL);
8484   return icon_view->priv->search_equal_func;
8485 }
8486 
8487 
8488 
8489 /**
8490  * exo_icon_view_set_search_equal_func:
8491  * @icon_view            : an #ExoIconView.
8492  * @search_equal_func    : the compare function to use during the search, or %NULL.
8493  * @search_equal_data    : user data to pass to @search_equal_func, or %NULL.
8494  * @search_equal_destroy : destroy notifier for @search_equal_data, or %NULL.
8495  *
8496  * Sets the compare function for the interactive search capabilities;
8497  * note that some like strcmp() returning 0 for equality
8498  * #ExoIconViewSearchEqualFunc returns %FALSE on matches.
8499  *
8500  * Specifying %NULL for @search_equal_func will reset @icon_view to use the default
8501  * search equal function.
8502  *
8503  * Since: 0.3.1.3
8504  **/
8505 void
8506 exo_icon_view_set_search_equal_func (ExoIconView               *icon_view,
8507                                      ExoIconViewSearchEqualFunc search_equal_func,
8508                                      gpointer                   search_equal_data,
8509                                      GDestroyNotify             search_equal_destroy)
8510 {
8511   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
8512   g_return_if_fail (search_equal_func != NULL || (search_equal_data == NULL && search_equal_destroy == NULL));
8513 
8514   /* destroy the previous data (if any) */
8515   if (G_UNLIKELY (icon_view->priv->search_equal_destroy != NULL))
8516     (*icon_view->priv->search_equal_destroy) (icon_view->priv->search_equal_data);
8517 
8518   icon_view->priv->search_equal_func = (search_equal_func != NULL) ? search_equal_func : exo_icon_view_search_equal_func;
8519   icon_view->priv->search_equal_data = search_equal_data;
8520   icon_view->priv->search_equal_destroy = search_equal_destroy;
8521 }
8522 
8523 
8524 
8525 /**
8526  * exo_icon_view_get_search_position_func:
8527  * @icon_view : an #ExoIconView.
8528  *
8529  * Returns the search dialog positioning function currently in use.
8530  *
8531  * Return value: the currently used function for positioning the search dialog.
8532  *
8533  * Since: 0.3.1.3
8534  **/
8535 ExoIconViewSearchPositionFunc
8536 exo_icon_view_get_search_position_func (const ExoIconView *icon_view)
8537 {
8538   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), NULL);
8539   return icon_view->priv->search_position_func;
8540 }
8541 
8542 
8543 
8544 /**
8545  * exo_icon_view_set_search_position_func:
8546  * @icon_view               : an #ExoIconView.
8547  * @search_position_func    : the function to use to position the search dialog, or %NULL.
8548  * @search_position_data    : user data to pass to @search_position_func, or %NULL.
8549  * @search_position_destroy : destroy notifier for @search_position_data, or %NULL.
8550  *
8551  * Sets the function to use when positioning the seach dialog.
8552  *
8553  * Specifying %NULL for @search_position_func will reset @icon_view to use the default
8554  * search position function.
8555  *
8556  * Since: 0.3.1.3
8557  **/
8558 void
8559 exo_icon_view_set_search_position_func (ExoIconView                  *icon_view,
8560                                         ExoIconViewSearchPositionFunc search_position_func,
8561                                         gpointer                      search_position_data,
8562                                         GDestroyNotify                search_position_destroy)
8563 {
8564   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
8565   g_return_if_fail (search_position_func != NULL || (search_position_data == NULL && search_position_destroy == NULL));
8566 
8567   /* destroy the previous data (if any) */
8568   if (icon_view->priv->search_position_destroy != NULL)
8569     (*icon_view->priv->search_position_destroy) (icon_view->priv->search_position_data);
8570 
8571   icon_view->priv->search_position_func = (search_position_func != NULL) ? search_position_func : exo_icon_view_search_position_func;
8572   icon_view->priv->search_position_data = search_position_data;
8573   icon_view->priv->search_position_destroy = search_position_destroy;
8574 }
8575 
8576 
8577 
8578 static void
8579 exo_icon_view_search_activate (GtkEntry    *entry,
8580                                ExoIconView *icon_view)
8581 {
8582   GtkTreePath *path;
8583 
8584   /* hide the interactive search dialog */
8585   exo_icon_view_search_dialog_hide (icon_view->priv->search_window, icon_view);
8586 
8587   /* check if we have a cursor item, and if so, activate it */
8588   if (exo_icon_view_get_cursor (icon_view, &path, NULL))
8589     {
8590       /* only activate the cursor item if it's selected */
8591       if (exo_icon_view_path_is_selected (icon_view, path))
8592         exo_icon_view_item_activated (icon_view, path);
8593       gtk_tree_path_free (path);
8594     }
8595 }
8596 
8597 
8598 
8599 static void
8600 exo_icon_view_search_dialog_hide (GtkWidget   *search_dialog,
8601                                   ExoIconView *icon_view)
8602 {
8603   _exo_return_if_fail (GTK_IS_WIDGET (search_dialog));
8604   _exo_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
8605 
8606   if (icon_view->priv->search_disable_popdown)
8607     return;
8608 
8609   /* disconnect the "changed" signal handler */
8610   if (icon_view->priv->search_entry_changed_id != 0)
8611     {
8612       g_signal_handler_disconnect (G_OBJECT (icon_view->priv->search_entry), icon_view->priv->search_entry_changed_id);
8613       icon_view->priv->search_entry_changed_id = 0;
8614     }
8615 
8616   /* disable the flush timeout */
8617   if (icon_view->priv->search_timeout_id != 0)
8618     g_source_remove (icon_view->priv->search_timeout_id);
8619 
8620   /* send focus-out event */
8621   _exo_gtk_widget_send_focus_change (icon_view->priv->search_entry, FALSE);
8622   gtk_widget_hide (search_dialog);
8623   gtk_entry_set_text (GTK_ENTRY (icon_view->priv->search_entry), "");
8624 }
8625 
8626 
8627 
8628 static void
8629 exo_icon_view_search_ensure_directory (ExoIconView *icon_view)
8630 {
8631   GtkWidget *toplevel;
8632   GtkWidget *frame;
8633   GtkWidget *vbox;
8634   GtkWindowGroup *group;
8635 
8636   /* determine the toplevel window */
8637   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (icon_view));
8638 
8639   /* check if we already have a search window */
8640   if (G_LIKELY (icon_view->priv->search_window != NULL))
8641     {
8642       if ((group = gtk_window_get_group (GTK_WINDOW (toplevel))) != NULL)
8643         gtk_window_group_add_window (group, GTK_WINDOW (icon_view->priv->search_window));
8644       else if ((group = gtk_window_get_group (GTK_WINDOW (icon_view->priv->search_window))) != NULL)
8645         gtk_window_group_remove_window (group, GTK_WINDOW (icon_view->priv->search_window));
8646       return;
8647     }
8648 
8649   /* allocate a new search window */
8650   icon_view->priv->search_window = gtk_window_new (GTK_WINDOW_POPUP);
8651   gtk_window_set_type_hint (GTK_WINDOW (icon_view->priv->search_window),
8652                             GDK_WINDOW_TYPE_HINT_UTILITY);
8653   if ((group = gtk_window_get_group (GTK_WINDOW (toplevel))) != NULL)
8654     gtk_window_group_add_window (group, GTK_WINDOW (icon_view->priv->search_window));
8655   gtk_window_set_modal (GTK_WINDOW (icon_view->priv->search_window), TRUE);
8656   gtk_window_set_screen (GTK_WINDOW (icon_view->priv->search_window), gtk_widget_get_screen (GTK_WIDGET (icon_view)));
8657   gtk_window_set_transient_for (GTK_WINDOW (icon_view->priv->search_window), GTK_WINDOW (toplevel));
8658 
8659   /* connect signal handlers */
8660   g_signal_connect (G_OBJECT (icon_view->priv->search_window), "delete-event", G_CALLBACK (exo_icon_view_search_delete_event), icon_view);
8661   g_signal_connect (G_OBJECT (icon_view->priv->search_window), "scroll-event", G_CALLBACK (exo_icon_view_search_scroll_event), icon_view);
8662   g_signal_connect (G_OBJECT (icon_view->priv->search_window), "key-press-event", G_CALLBACK (exo_icon_view_search_key_press_event), icon_view);
8663   g_signal_connect (G_OBJECT (icon_view->priv->search_window), "button-press-event", G_CALLBACK (exo_icon_view_search_button_press_event), icon_view);
8664 
8665   /* allocate the frame widget */
8666   frame = g_object_new (GTK_TYPE_FRAME, "shadow-type", GTK_SHADOW_ETCHED_IN, NULL);
8667   gtk_container_add (GTK_CONTAINER (icon_view->priv->search_window), frame);
8668   gtk_widget_show (frame);
8669 
8670   /* allocate the vertical box */
8671   vbox = g_object_new (GTK_TYPE_VBOX, "border-width", 3, NULL);
8672   gtk_container_add (GTK_CONTAINER (frame), vbox);
8673   gtk_widget_show (vbox);
8674 
8675   /* allocate the search entry widget */
8676   icon_view->priv->search_entry = gtk_entry_new ();
8677   g_signal_connect (G_OBJECT (icon_view->priv->search_entry), "activate", G_CALLBACK (exo_icon_view_search_activate), icon_view);
8678 #if GTK_CHECK_VERSION(2, 20, 0)
8679   g_signal_connect (G_OBJECT (icon_view->priv->search_entry), "preedit-changed",
8680                     G_CALLBACK (exo_icon_view_search_preedit_changed), icon_view);
8681 #else
8682   g_signal_connect (G_OBJECT (GTK_ENTRY (icon_view->priv->search_entry)->im_context), "preedit-changed",
8683                     G_CALLBACK (exo_icon_view_search_preedit_changed), icon_view);
8684 #endif
8685   gtk_box_pack_start (GTK_BOX (vbox), icon_view->priv->search_entry, TRUE, TRUE, 0);
8686   gtk_widget_realize (icon_view->priv->search_entry);
8687   gtk_widget_show (icon_view->priv->search_entry);
8688 }
8689 
8690 
8691 
8692 static void
8693 exo_icon_view_search_init (GtkWidget   *search_entry,
8694                            ExoIconView *icon_view)
8695 {
8696   GtkTreeModel *model;
8697   GtkTreeIter   iter;
8698   const gchar  *text;
8699   gint          length;
8700   gint          count = 0;
8701 
8702   _exo_return_if_fail (GTK_IS_ENTRY (search_entry));
8703   _exo_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
8704 
8705   /* determine the current text for the search entry */
8706   text = gtk_entry_get_text (GTK_ENTRY (search_entry));
8707   if (G_UNLIKELY (text == NULL))
8708     return;
8709 
8710   /* unselect all items */
8711   exo_icon_view_unselect_all (icon_view);
8712 
8713   /* renew the flush timeout */
8714   if ((icon_view->priv->search_timeout_id != 0))
8715     {
8716       /* drop the previous timeout */
8717       g_source_remove (icon_view->priv->search_timeout_id);
8718 
8719       /* schedule a new timeout */
8720       icon_view->priv->search_timeout_id = gdk_threads_add_timeout_full (G_PRIORITY_LOW, EXO_ICON_VIEW_SEARCH_DIALOG_TIMEOUT,
8721                                                                exo_icon_view_search_timeout, icon_view,
8722                                                                exo_icon_view_search_timeout_destroy);
8723     }
8724 
8725   /* verify that we have a search text */
8726   length = strlen (text);
8727   if (length < 1)
8728     return;
8729 
8730   /* verify that we have a valid model */
8731   model = exo_icon_view_get_model (icon_view);
8732   if (G_UNLIKELY (model == NULL))
8733     return;
8734 
8735   /* start the interactive search */
8736   if (gtk_tree_model_get_iter_first (model, &iter))
8737     {
8738       /* let's see if we have a match */
8739       if (exo_icon_view_search_iter (icon_view, model, &iter, text, &count, 1))
8740         icon_view->priv->search_selected_iter = 1;
8741     }
8742 }
8743 
8744 
8745 
8746 static gboolean
8747 exo_icon_view_search_iter (ExoIconView  *icon_view,
8748                            GtkTreeModel *model,
8749                            GtkTreeIter  *iter,
8750                            const gchar  *text,
8751                            gint         *count,
8752                            gint          n)
8753 {
8754   GtkTreePath *path;
8755 
8756   _exo_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
8757   _exo_return_val_if_fail (GTK_IS_TREE_MODEL (model), FALSE);
8758   _exo_return_val_if_fail (count != NULL, FALSE);
8759 
8760   /* search for a matching item */
8761   do
8762     {
8763       if (!(*icon_view->priv->search_equal_func) (model, icon_view->priv->search_column, text, iter, icon_view->priv->search_equal_data))
8764         {
8765           (*count) += 1;
8766           if (*count == n)
8767             {
8768               /* place cursor on the item and select it */
8769               path = gtk_tree_model_get_path (model, iter);
8770               exo_icon_view_select_path (icon_view, path);
8771               exo_icon_view_set_cursor (icon_view, path, NULL, FALSE);
8772               gtk_tree_path_free (path);
8773               return TRUE;
8774             }
8775         }
8776     }
8777   while (gtk_tree_model_iter_next (model, iter));
8778 
8779   /* no match */
8780   return FALSE;
8781 }
8782 
8783 
8784 
8785 static void
8786 exo_icon_view_search_move (GtkWidget   *widget,
8787                            ExoIconView *icon_view,
8788                            gboolean     move_up)
8789 {
8790   GtkTreeModel *model;
8791   const gchar  *text;
8792   GtkTreeIter   iter;
8793   gboolean      retval;
8794   gint          length;
8795   gint          count = 0;
8796 
8797   /* determine the current text for the search entry */
8798   text = gtk_entry_get_text (GTK_ENTRY (icon_view->priv->search_entry));
8799   if (G_UNLIKELY (text == NULL))
8800     return;
8801 
8802   /* if we already selected the first item, we cannot go up */
8803   if (move_up && icon_view->priv->search_selected_iter == 1)
8804     return;
8805 
8806   /* determine the length of the search text */
8807   length = strlen (text);
8808   if (G_UNLIKELY (length < 1))
8809     return;
8810 
8811   /* unselect all items */
8812   exo_icon_view_unselect_all (icon_view);
8813 
8814   /* verify that we have a valid model */
8815   model = exo_icon_view_get_model (icon_view);
8816   if (G_UNLIKELY (model == NULL))
8817     return;
8818 
8819   /* determine the iterator to the first item */
8820   if (!gtk_tree_model_get_iter_first (model, &iter))
8821     return;
8822 
8823   /* first attempt to search */
8824   retval = exo_icon_view_search_iter (icon_view, model, &iter, text, &count, move_up
8825                                       ? (icon_view->priv->search_selected_iter - 1)
8826                                       : (icon_view->priv->search_selected_iter + 1));
8827 
8828   /* check if we found something */
8829   if (G_LIKELY (retval))
8830     {
8831       /* match found */
8832       icon_view->priv->search_selected_iter += move_up ? -1 : 1;
8833     }
8834   else
8835     {
8836       /* return to old iter */
8837       if (gtk_tree_model_get_iter_first (model, &iter))
8838         {
8839           count = 0;
8840           exo_icon_view_search_iter (icon_view, model, &iter, text, &count,
8841                                      icon_view->priv->search_selected_iter);
8842         }
8843     }
8844 }
8845 
8846 
8847 
8848 static void
8849 #if GTK_CHECK_VERSION(2, 20, 0)
8850 exo_icon_view_search_preedit_changed (GtkEntry     *entry,
8851                                       gchar        *preedit,
8852                                       ExoIconView  *icon_view)
8853 #else
8854 exo_icon_view_search_preedit_changed (GtkIMContext *im_context,
8855                                       ExoIconView  *icon_view)
8856 #endif
8857 {
8858   icon_view->priv->search_imcontext_changed = TRUE;
8859 
8860   /* re-register the search timeout */
8861   if (G_LIKELY (icon_view->priv->search_timeout_id != 0))
8862     {
8863       g_source_remove (icon_view->priv->search_timeout_id);
8864       icon_view->priv->search_timeout_id = gdk_threads_add_timeout_full (G_PRIORITY_LOW, EXO_ICON_VIEW_SEARCH_DIALOG_TIMEOUT,
8865                                                                exo_icon_view_search_timeout, icon_view,
8866                                                                exo_icon_view_search_timeout_destroy);
8867     }
8868 }
8869 
8870 
8871 
8872 static gboolean
8873 exo_icon_view_search_start (ExoIconView *icon_view,
8874                             gboolean     keybinding)
8875 {
8876   GTypeClass *klass;
8877 
8878   /* check if typeahead is enabled */
8879   if (G_UNLIKELY (!icon_view->priv->enable_search && !keybinding))
8880     return FALSE;
8881 
8882   /* check if we already display the search window */
8883   if (icon_view->priv->search_window != NULL && gtk_widget_get_visible (icon_view->priv->search_window))
8884     return TRUE;
8885 
8886   /* we only start interactive search if we have focus,
8887    * we don't want to start interactive search if one of
8888    * our children has the focus.
8889    */
8890   if (!gtk_widget_has_focus (GTK_WIDGET (icon_view)))
8891     return FALSE;
8892 
8893   /* verify that we have a search column */
8894   if (G_UNLIKELY (icon_view->priv->search_column < 0))
8895     return FALSE;
8896 
8897   exo_icon_view_search_ensure_directory (icon_view);
8898 
8899   /* clear search entry if we were started by a keybinding */
8900   if (G_UNLIKELY (keybinding))
8901     gtk_entry_set_text (GTK_ENTRY (icon_view->priv->search_entry), "");
8902 
8903   /* determine the position for the search dialog */
8904   (*icon_view->priv->search_position_func) (icon_view, icon_view->priv->search_window, icon_view->priv->search_position_data);
8905 
8906   /* display the search dialog */
8907   gtk_widget_show (icon_view->priv->search_window);
8908 
8909   /* connect "changed" signal for the entry */
8910   if (G_UNLIKELY (icon_view->priv->search_entry_changed_id == 0))
8911     {
8912       icon_view->priv->search_entry_changed_id = g_signal_connect (G_OBJECT (icon_view->priv->search_entry), "changed",
8913                                                                    G_CALLBACK (exo_icon_view_search_init), icon_view);
8914     }
8915 
8916   /* start the search timeout */
8917   icon_view->priv->search_timeout_id = gdk_threads_add_timeout_full (G_PRIORITY_LOW, EXO_ICON_VIEW_SEARCH_DIALOG_TIMEOUT,
8918                                                            exo_icon_view_search_timeout, icon_view,
8919                                                            exo_icon_view_search_timeout_destroy);
8920 
8921   /* grab focus will select all the text, we don't want that to happen, so we
8922    * call the parent instance and bypass the selection change. This is probably
8923    * really hackish, but GtkTreeView does it as well *hrhr*
8924    */
8925   klass = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (icon_view->priv->search_entry));
8926   (*GTK_WIDGET_CLASS (klass)->grab_focus) (icon_view->priv->search_entry);
8927 
8928   /* send focus-in event */
8929   _exo_gtk_widget_send_focus_change (icon_view->priv->search_entry, TRUE);
8930 
8931   /* search first matching iter */
8932   exo_icon_view_search_init (icon_view->priv->search_entry, icon_view);
8933 
8934   return TRUE;
8935 }
8936 
8937 
8938 
8939 static gboolean
8940 exo_icon_view_search_equal_func (GtkTreeModel *model,
8941                                  gint          column,
8942                                  const gchar  *key,
8943                                  GtkTreeIter  *iter,
8944                                  gpointer      user_data)
8945 {
8946   const gchar *str;
8947   gboolean     retval = TRUE;
8948   GValue       transformed = { 0, };
8949   GValue       value = { 0, };
8950   gchar       *case_normalized_string = NULL;
8951   gchar       *case_normalized_key = NULL;
8952   gchar       *normalized_string;
8953   gchar       *normalized_key;
8954 
8955   /* determine the value for the column/iter */
8956   gtk_tree_model_get_value (model, iter, column, &value);
8957 
8958   /* try to transform the value to a string */
8959   g_value_init (&transformed, G_TYPE_STRING);
8960   if (!g_value_transform (&value, &transformed))
8961     {
8962       g_value_unset (&value);
8963       return TRUE;
8964     }
8965   g_value_unset (&value);
8966 
8967   /* check if we have a string value */
8968   str = g_value_get_string (&transformed);
8969   if (G_UNLIKELY (str == NULL))
8970     {
8971       g_value_unset (&transformed);
8972       return TRUE;
8973     }
8974 
8975   /* normalize the string and the key */
8976   normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
8977   normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
8978 
8979   /* check if we have normalized both string */
8980   if (G_LIKELY (normalized_string != NULL && normalized_key != NULL))
8981     {
8982       case_normalized_string = g_utf8_casefold (normalized_string, -1);
8983       case_normalized_key = g_utf8_casefold (normalized_key, -1);
8984 
8985       /* compare the casefolded strings */
8986       if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0)
8987         retval = FALSE;
8988     }
8989 
8990   /* cleanup */
8991   g_free (case_normalized_string);
8992   g_free (case_normalized_key);
8993   g_value_unset (&transformed);
8994   g_free (normalized_string);
8995   g_free (normalized_key);
8996 
8997   return retval;
8998 }
8999 
9000 
9001 
9002 static void
9003 exo_icon_view_search_position_func (ExoIconView *icon_view,
9004                                     GtkWidget   *search_dialog,
9005                                     gpointer     user_data)
9006 {
9007   GtkRequisition requisition;
9008   GdkRectangle   monitor;
9009   GdkWindow     *view_window = gtk_widget_get_window (GTK_WIDGET (icon_view));
9010   GdkScreen     *screen = gdk_window_get_screen (view_window);
9011   gint           view_width, view_height;
9012   gint           view_x, view_y;
9013   gint           monitor_num;
9014   gint           x, y;
9015 
9016   /* determine the monitor geometry */
9017   monitor_num = gdk_screen_get_monitor_at_window (screen, view_window);
9018   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
9019 
9020   /* make sure the search dialog is realized */
9021   gtk_widget_realize (search_dialog);
9022 
9023   gdk_window_get_origin (view_window, &view_x, &view_y);
9024 #if GTK_CHECK_VERSION(2, 24, 0)
9025   view_width = gdk_window_get_width (view_window);
9026   view_height = gdk_window_get_height (view_window);
9027 #else
9028   gdk_drawable_get_size (view_window, &view_width, &view_height);
9029 #endif
9030 #if GTK_CHECK_VERSION(3, 0, 0)
9031   gtk_widget_get_preferred_size (search_dialog, NULL, &requisition);
9032 #else
9033   gtk_widget_size_request (search_dialog, &requisition);
9034 #endif
9035 
9036   if (view_x + view_width > gdk_screen_get_width (screen))
9037     x = gdk_screen_get_width (screen) - requisition.width;
9038   else if (view_x + view_width - requisition.width < 0)
9039     x = 0;
9040   else
9041     x = view_x + view_width - requisition.width;
9042 
9043   if (view_y + view_height + requisition.height > gdk_screen_get_height (screen))
9044     y = gdk_screen_get_height (screen) - requisition.height;
9045   else if (view_y + view_height < 0) /* isn't really possible ... */
9046     y = 0;
9047   else
9048     y = view_y + view_height;
9049 
9050   gtk_window_move (GTK_WINDOW (search_dialog), x, y);
9051 }
9052 
9053 
9054 
9055 static gboolean
9056 exo_icon_view_search_button_press_event (GtkWidget      *widget,
9057                                          GdkEventButton *event,
9058                                          ExoIconView    *icon_view)
9059 {
9060   _exo_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
9061   _exo_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
9062 
9063   /* hide the search dialog */
9064   exo_icon_view_search_dialog_hide (widget, icon_view);
9065 
9066   if (event->window == icon_view->priv->bin_window)
9067     exo_icon_view_button_press_event (GTK_WIDGET (icon_view), event);
9068 
9069   return TRUE;
9070 }
9071 
9072 
9073 
9074 static gboolean
9075 exo_icon_view_search_delete_event (GtkWidget   *widget,
9076                                    GdkEventAny *event,
9077                                    ExoIconView *icon_view)
9078 {
9079   _exo_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
9080   _exo_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
9081 
9082   /* hide the search dialog */
9083   exo_icon_view_search_dialog_hide (widget, icon_view);
9084 
9085   return TRUE;
9086 }
9087 
9088 
9089 
9090 static gboolean
9091 exo_icon_view_search_key_press_event (GtkWidget   *widget,
9092                                       GdkEventKey *event,
9093                                       ExoIconView *icon_view)
9094 {
9095   gboolean retval = FALSE;
9096 
9097   _exo_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
9098   _exo_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
9099 
9100 
9101   /* close window and cancel the search */
9102   if (event->keyval == GDK_KEY_Escape || event->keyval == GDK_KEY_Tab)
9103     {
9104       exo_icon_view_search_dialog_hide (widget, icon_view);
9105       return TRUE;
9106     }
9107 
9108   /* select previous matching iter */
9109   if (event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_KP_Up)
9110     {
9111       exo_icon_view_search_move (widget, icon_view, TRUE);
9112       retval = TRUE;
9113     }
9114 
9115   if (((event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == (GDK_CONTROL_MASK | GDK_SHIFT_MASK))
9116       && (event->keyval == GDK_KEY_g || event->keyval == GDK_KEY_G))
9117     {
9118       exo_icon_view_search_move (widget, icon_view, TRUE);
9119       retval = TRUE;
9120     }
9121 
9122   /* select next matching iter */
9123   if (event->keyval == GDK_KEY_Down || event->keyval == GDK_KEY_KP_Down)
9124     {
9125       exo_icon_view_search_move (widget, icon_view, FALSE);
9126       retval = TRUE;
9127     }
9128 
9129   if (((event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == GDK_CONTROL_MASK)
9130       && (event->keyval == GDK_KEY_g || event->keyval == GDK_KEY_G))
9131     {
9132       exo_icon_view_search_move (widget, icon_view, FALSE);
9133       retval = TRUE;
9134     }
9135 
9136   /* renew the flush timeout */
9137   if (retval && (icon_view->priv->search_timeout_id != 0))
9138     {
9139       /* drop the previous timeout */
9140       g_source_remove (icon_view->priv->search_timeout_id);
9141 
9142       /* schedule a new timeout */
9143       icon_view->priv->search_timeout_id = gdk_threads_add_timeout_full (G_PRIORITY_LOW, EXO_ICON_VIEW_SEARCH_DIALOG_TIMEOUT,
9144                                                                exo_icon_view_search_timeout, icon_view,
9145                                                                exo_icon_view_search_timeout_destroy);
9146     }
9147 
9148   return retval;
9149 }
9150 
9151 
9152 
9153 static gboolean
9154 exo_icon_view_search_scroll_event (GtkWidget      *widget,
9155                                    GdkEventScroll *event,
9156                                    ExoIconView    *icon_view)
9157 {
9158   gboolean retval = TRUE;
9159 
9160   _exo_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
9161   _exo_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
9162 
9163   if (event->direction == GDK_SCROLL_UP)
9164     exo_icon_view_search_move (widget, icon_view, TRUE);
9165   else if (event->direction == GDK_SCROLL_DOWN)
9166     exo_icon_view_search_move (widget, icon_view, FALSE);
9167   else
9168     retval = FALSE;
9169 
9170   return retval;
9171 }
9172 
9173 
9174 
9175 static gboolean
9176 exo_icon_view_search_timeout (gpointer user_data)
9177 {
9178   ExoIconView *icon_view = EXO_ICON_VIEW (user_data);
9179 
9180   if(!g_source_is_destroyed(g_main_current_source()))
9181     exo_icon_view_search_dialog_hide (icon_view->priv->search_window, icon_view);
9182 
9183   return FALSE;
9184 }
9185 
9186 
9187 
9188 static void
9189 exo_icon_view_search_timeout_destroy (gpointer user_data)
9190 {
9191   EXO_ICON_VIEW (user_data)->priv->search_timeout_id = 0;
9192 }
9193 
9194 
9195 /* Accessibility Support */
9196 
9197 static gpointer accessible_parent_class;
9198 static gpointer accessible_item_parent_class;
9199 static GQuark accessible_private_data_quark = 0;
9200 
9201 #define EXO_TYPE_ICON_VIEW_ITEM_ACCESSIBLE      (exo_icon_view_item_accessible_get_type ())
9202 #define EXO_ICON_VIEW_ITEM_ACCESSIBLE(obj)      (G_TYPE_CHECK_INSTANCE_CAST ((obj), EXO_TYPE_ICON_VIEW_ITEM_ACCESSIBLE, ExoIconViewItemAccessible))
9203 #define EXO_IS_ICON_VIEW_ITEM_ACCESSIBLE(obj)   (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EXO_TYPE_ICON_VIEW_ITEM_ACCESSIBLE))
9204 
9205 static GType exo_icon_view_item_accessible_get_type (void);
9206 
9207 enum {
9208     ACTION_ACTIVATE,
9209     LAST_ACTION
9210 };
9211 
9212 typedef struct
9213 {
9214   AtkObject parent;
9215   ExoIconViewItem *item;
9216   GtkWidget *widget;
9217   AtkStateSet *state_set;
9218   gchar *text;
9219   GtkTextBuffer *text_buffer;
9220   gchar *action_descriptions[LAST_ACTION];
9221   gchar *image_description;
9222   guint action_idle_handler;
9223 } ExoIconViewItemAccessible;
9224 
9225 static const gchar *const exo_icon_view_item_accessible_action_names[] =
9226 {
9227   "activate",
9228   NULL
9229 };
9230 
9231 static const gchar *const exo_icon_view_item_accessible_action_descriptions[] =
9232 {
9233   "Activate item",
9234   NULL
9235 };
9236 typedef struct _ExoIconViewItemAccessibleClass
9237 {
9238   AtkObjectClass parent_class;
9239 
9240 } ExoIconViewItemAccessibleClass;
9241 
9242 static gboolean exo_icon_view_item_accessible_is_showing (ExoIconViewItemAccessible *item);
9243 
9244 static gboolean
9245 exo_icon_view_item_accessible_idle_do_action (gpointer data)
9246 {
9247   ExoIconViewItemAccessible *item;
9248   ExoIconView *icon_view;
9249   GtkTreePath *path;
9250 
9251   if(g_source_is_destroyed(g_main_current_source()))
9252     return FALSE;
9253 
9254   item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (data);
9255   item->action_idle_handler = 0;
9256 
9257   if (item->widget != NULL)
9258     {
9259       icon_view = EXO_ICON_VIEW (item->widget);
9260       path = gtk_tree_path_new_from_indices (item->item->index, -1);
9261       exo_icon_view_item_activated (icon_view, path);
9262       gtk_tree_path_free (path);
9263     }
9264 
9265   return FALSE;
9266 }
9267 
9268 static gboolean
9269 exo_icon_view_item_accessible_action_do_action (AtkAction *action,
9270                                                 gint       i)
9271 {
9272   ExoIconViewItemAccessible *item;
9273 
9274   if (i < 0 || i >= LAST_ACTION)
9275     return FALSE;
9276 
9277   item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (action);
9278 
9279   if (!EXO_IS_ICON_VIEW (item->widget))
9280     return FALSE;
9281 
9282   if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT))
9283     return FALSE;
9284 
9285   switch (i)
9286     {
9287     case ACTION_ACTIVATE:
9288       if (!item->action_idle_handler)
9289         item->action_idle_handler = gdk_threads_add_idle (exo_icon_view_item_accessible_idle_do_action, item);
9290       break;
9291     default:
9292       g_assert_not_reached ();
9293       return FALSE;
9294 
9295     }
9296   return TRUE;
9297 }
9298 
9299 static gint
9300 exo_icon_view_item_accessible_action_get_n_actions (AtkAction *action)
9301 {
9302         return LAST_ACTION;
9303 }
9304 
9305 static const gchar *
9306 exo_icon_view_item_accessible_action_get_description (AtkAction *action,
9307                                                       gint       i)
9308 {
9309   ExoIconViewItemAccessible *item;
9310 
9311   if (i < 0 || i >= LAST_ACTION)
9312     return NULL;
9313 
9314   item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (action);
9315 
9316   if (item->action_descriptions[i])
9317     return item->action_descriptions[i];
9318   else
9319     return exo_icon_view_item_accessible_action_descriptions[i];
9320 }
9321 
9322 static const gchar *
9323 exo_icon_view_item_accessible_action_get_name (AtkAction *action,
9324                                                gint       i)
9325 {
9326   if (i < 0 || i >= LAST_ACTION)
9327     return NULL;
9328 
9329   return exo_icon_view_item_accessible_action_names[i];
9330 }
9331 
9332 static gboolean
9333 exo_icon_view_item_accessible_action_set_description (AtkAction   *action,
9334                                                       gint         i,
9335                                                       const gchar *description)
9336 {
9337   ExoIconViewItemAccessible *item;
9338 
9339   if (i < 0 || i >= LAST_ACTION)
9340     return FALSE;
9341 
9342   item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (action);
9343 
9344   g_free (item->action_descriptions[i]);
9345 
9346   item->action_descriptions[i] = g_strdup (description);
9347 
9348   return TRUE;
9349 }
9350 
9351 static void
9352 atk_action_item_interface_init (AtkActionIface *iface)
9353 {
9354   iface->do_action = exo_icon_view_item_accessible_action_do_action;
9355   iface->get_n_actions = exo_icon_view_item_accessible_action_get_n_actions;
9356   iface->get_description = exo_icon_view_item_accessible_action_get_description;
9357   iface->get_name = exo_icon_view_item_accessible_action_get_name;
9358   iface->set_description = exo_icon_view_item_accessible_action_set_description;
9359 }
9360 
9361 static const gchar *
9362 exo_icon_view_item_accessible_image_get_image_description (AtkImage *image)
9363 {
9364   ExoIconViewItemAccessible *item;
9365 
9366   item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (image);
9367 
9368   return item->image_description;
9369 }
9370 
9371 static gboolean
9372 exo_icon_view_item_accessible_image_set_image_description (AtkImage    *image,
9373                                                            const gchar *description)
9374 {
9375   ExoIconViewItemAccessible *item;
9376 
9377   item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (image);
9378 
9379   g_free (item->image_description);
9380   item->image_description = g_strdup (description);
9381 
9382   return TRUE;
9383 }
9384 
9385 static gboolean
9386 get_pixbuf_box (ExoIconView     *icon_view,
9387 		ExoIconViewItem *item,
9388 		GdkRectangle    *box)
9389 {
9390   GList *l;
9391 
9392   for (l = icon_view->priv->cell_list; l; l = l->next)
9393     {
9394       ExoIconViewCellInfo *info = l->data;
9395 
9396       if (GTK_IS_CELL_RENDERER_PIXBUF (info->cell))
9397 	{
9398 	  if (info->position < item->n_cells)
9399 	    *box = item->box[info->position];
9400 
9401 	  return TRUE;
9402 	}
9403     }
9404 
9405   return FALSE;
9406 }
9407 
9408 static gchar *
9409 get_text (ExoIconView     *icon_view,
9410 	  ExoIconViewItem *item)
9411 {
9412   GList *l;
9413   gchar *text;
9414 
9415   for (l = icon_view->priv->cell_list; l; l = l->next)
9416     {
9417       ExoIconViewCellInfo *info = l->data;
9418 
9419       if (GTK_IS_CELL_RENDERER_TEXT (info->cell))
9420 	{
9421 	  g_object_get (info->cell, "text", &text, NULL);
9422 
9423 	  return text;
9424 	}
9425     }
9426 
9427   return NULL;
9428 }
9429 
9430 static void
9431 exo_icon_view_item_accessible_image_get_image_size (AtkImage *image,
9432                                                     gint     *width,
9433                                                     gint     *height)
9434 {
9435   ExoIconViewItemAccessible *item;
9436   GdkRectangle box;
9437 
9438   item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (image);
9439 
9440   if (!EXO_IS_ICON_VIEW (item->widget))
9441     return;
9442 
9443   if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT))
9444     return;
9445 
9446   if (get_pixbuf_box (EXO_ICON_VIEW (item->widget), item->item, &box))
9447     {
9448       *width = box.width;
9449       *height = box.height;
9450     }
9451 }
9452 
9453 static void
9454 exo_icon_view_item_accessible_image_get_image_position (AtkImage    *image,
9455                                                         gint        *x,
9456                                                         gint        *y,
9457                                                         AtkCoordType coord_type)
9458 {
9459   ExoIconViewItemAccessible *item;
9460   AtkObject *parent_obj;
9461   GdkRectangle box;
9462 
9463   item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (image);
9464 
9465   if (!EXO_IS_ICON_VIEW (item->widget))
9466     return;
9467 
9468   if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT))
9469     return;
9470 
9471   parent_obj = gtk_widget_get_accessible (item->widget);
9472   atk_component_get_extents (ATK_COMPONENT (parent_obj), x, y, NULL, NULL, coord_type);
9473 
9474   if (get_pixbuf_box (EXO_ICON_VIEW (item->widget), item->item, &box))
9475     {
9476       *x+= box.x - item->item->area.x;
9477       *y+= box.y - item->item->area.y;
9478     }
9479 
9480 }
9481 
9482 static void
9483 atk_image_item_interface_init (AtkImageIface *iface)
9484 {
9485   iface->get_image_description = exo_icon_view_item_accessible_image_get_image_description;
9486   iface->set_image_description = exo_icon_view_item_accessible_image_set_image_description;
9487   iface->get_image_size = exo_icon_view_item_accessible_image_get_image_size;
9488   iface->get_image_position = exo_icon_view_item_accessible_image_get_image_position;
9489 }
9490 
9491 static gchar *
9492 exo_icon_view_item_accessible_text_get_text (AtkText *text,
9493                                              gint     start_pos,
9494                                              gint     end_pos)
9495 {
9496   ExoIconViewItemAccessible *item;
9497   GtkTextIter start, end;
9498   GtkTextBuffer *buffer;
9499 
9500   item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (text);
9501 
9502   if (!EXO_IS_ICON_VIEW (item->widget))
9503     return NULL;
9504 
9505   if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT))
9506     return NULL;
9507 
9508   buffer = item->text_buffer;
9509   gtk_text_buffer_get_iter_at_offset (buffer, &start, start_pos);
9510   if (end_pos < 0)
9511     gtk_text_buffer_get_end_iter (buffer, &end);
9512   else
9513     gtk_text_buffer_get_iter_at_offset (buffer, &end, end_pos);
9514 
9515   return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
9516 }
9517 
9518 static gunichar
9519 exo_icon_view_item_accessible_text_get_character_at_offset (AtkText *text,
9520                                                             gint     offset)
9521 {
9522   ExoIconViewItemAccessible *item;
9523   GtkTextIter start, end;
9524   GtkTextBuffer *buffer;
9525   gchar *string;
9526   gunichar unichar;
9527 
9528   item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (text);
9529 
9530   if (!EXO_IS_ICON_VIEW (item->widget))
9531     return '\0';
9532 
9533   if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT))
9534     return '\0';
9535 
9536   buffer = item->text_buffer;
9537   if (offset >= gtk_text_buffer_get_char_count (buffer))
9538     return '\0';
9539 
9540   gtk_text_buffer_get_iter_at_offset (buffer, &start, offset);
9541   end = start;
9542   gtk_text_iter_forward_char (&end);
9543   string = gtk_text_buffer_get_slice (buffer, &start, &end, FALSE);
9544   unichar = g_utf8_get_char (string);
9545   g_free(string);
9546 
9547   return unichar;
9548 }
9549 
9550 static gchar*
9551 exo_icon_view_item_accessible_text_get_text_before_offset (AtkText         *text,
9552                                                            gint            offset,
9553                                                            AtkTextBoundary boundary_type,
9554                                                            gint            *start_offset,
9555                                                            gint            *end_offset)
9556 {
9557   ExoIconViewItemAccessible *item;
9558   GtkTextIter start, end;
9559   GtkTextBuffer *buffer;
9560 
9561   item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (text);
9562 
9563   if (!EXO_IS_ICON_VIEW (item->widget))
9564     return NULL;
9565 
9566   if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT))
9567     return NULL;
9568 
9569   buffer = item->text_buffer;
9570 
9571   if (!gtk_text_buffer_get_char_count (buffer))
9572     {
9573       *start_offset = 0;
9574       *end_offset = 0;
9575       return g_strdup ("");
9576     }
9577   gtk_text_buffer_get_iter_at_offset (buffer, &start, offset);
9578 
9579   end = start;
9580 
9581   switch (boundary_type)
9582     {
9583     case ATK_TEXT_BOUNDARY_CHAR:
9584       gtk_text_iter_backward_char(&start);
9585       break;
9586     case ATK_TEXT_BOUNDARY_WORD_START:
9587       if (!gtk_text_iter_starts_word (&start))
9588         gtk_text_iter_backward_word_start (&start);
9589       end = start;
9590       gtk_text_iter_backward_word_start(&start);
9591       break;
9592     case ATK_TEXT_BOUNDARY_WORD_END:
9593       if (gtk_text_iter_inside_word (&start) &&
9594           !gtk_text_iter_starts_word (&start))
9595         gtk_text_iter_backward_word_start (&start);
9596       while (!gtk_text_iter_ends_word (&start))
9597         {
9598           if (!gtk_text_iter_backward_char (&start))
9599             break;
9600         }
9601       end = start;
9602       gtk_text_iter_backward_word_start(&start);
9603       while (!gtk_text_iter_ends_word (&start))
9604         {
9605           if (!gtk_text_iter_backward_char (&start))
9606             break;
9607         }
9608       break;
9609     case ATK_TEXT_BOUNDARY_SENTENCE_START:
9610       if (!gtk_text_iter_starts_sentence (&start))
9611         gtk_text_iter_backward_sentence_start (&start);
9612       end = start;
9613       gtk_text_iter_backward_sentence_start (&start);
9614       break;
9615     case ATK_TEXT_BOUNDARY_SENTENCE_END:
9616       if (gtk_text_iter_inside_sentence (&start) &&
9617           !gtk_text_iter_starts_sentence (&start))
9618         gtk_text_iter_backward_sentence_start (&start);
9619       while (!gtk_text_iter_ends_sentence (&start))
9620         {
9621           if (!gtk_text_iter_backward_char (&start))
9622             break;
9623         }
9624       end = start;
9625       gtk_text_iter_backward_sentence_start (&start);
9626       while (!gtk_text_iter_ends_sentence (&start))
9627         {
9628           if (!gtk_text_iter_backward_char (&start))
9629             break;
9630         }
9631       break;
9632    case ATK_TEXT_BOUNDARY_LINE_START:
9633    case ATK_TEXT_BOUNDARY_LINE_END:
9634       break;
9635     }
9636 
9637   *start_offset = gtk_text_iter_get_offset (&start);
9638   *end_offset = gtk_text_iter_get_offset (&end);
9639 
9640   return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
9641 }
9642 
9643 static gchar*
9644 exo_icon_view_item_accessible_text_get_text_at_offset (AtkText         *text,
9645                                                        gint            offset,
9646                                                        AtkTextBoundary boundary_type,
9647                                                        gint            *start_offset,
9648                                                        gint            *end_offset)
9649 {
9650   ExoIconViewItemAccessible *item;
9651   GtkTextIter start, end;
9652   GtkTextBuffer *buffer;
9653 
9654   item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (text);
9655 
9656   if (!EXO_IS_ICON_VIEW (item->widget))
9657     return NULL;
9658 
9659   if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT))
9660     return NULL;
9661 
9662   buffer = item->text_buffer;
9663 
9664   if (!gtk_text_buffer_get_char_count (buffer))
9665     {
9666       *start_offset = 0;
9667       *end_offset = 0;
9668       return g_strdup ("");
9669     }
9670   gtk_text_buffer_get_iter_at_offset (buffer, &start, offset);
9671 
9672   end = start;
9673 
9674   switch (boundary_type)
9675     {
9676     case ATK_TEXT_BOUNDARY_CHAR:
9677       gtk_text_iter_forward_char (&end);
9678       break;
9679     case ATK_TEXT_BOUNDARY_WORD_START:
9680       if (!gtk_text_iter_starts_word (&start))
9681         gtk_text_iter_backward_word_start (&start);
9682       if (gtk_text_iter_inside_word (&end))
9683         gtk_text_iter_forward_word_end (&end);
9684       while (!gtk_text_iter_starts_word (&end))
9685         {
9686           if (!gtk_text_iter_forward_char (&end))
9687             break;
9688         }
9689       break;
9690     case ATK_TEXT_BOUNDARY_WORD_END:
9691       if (gtk_text_iter_inside_word (&start) &&
9692           !gtk_text_iter_starts_word (&start))
9693         gtk_text_iter_backward_word_start (&start);
9694       while (!gtk_text_iter_ends_word (&start))
9695         {
9696           if (!gtk_text_iter_backward_char (&start))
9697             break;
9698         }
9699       gtk_text_iter_forward_word_end (&end);
9700       break;
9701     case ATK_TEXT_BOUNDARY_SENTENCE_START:
9702       if (!gtk_text_iter_starts_sentence (&start))
9703         gtk_text_iter_backward_sentence_start (&start);
9704       if (gtk_text_iter_inside_sentence (&end))
9705         gtk_text_iter_forward_sentence_end (&end);
9706       while (!gtk_text_iter_starts_sentence (&end))
9707         {
9708           if (!gtk_text_iter_forward_char (&end))
9709             break;
9710         }
9711       break;
9712     case ATK_TEXT_BOUNDARY_SENTENCE_END:
9713       if (gtk_text_iter_inside_sentence (&start) &&
9714           !gtk_text_iter_starts_sentence (&start))
9715         gtk_text_iter_backward_sentence_start (&start);
9716       while (!gtk_text_iter_ends_sentence (&start))
9717         {
9718           if (!gtk_text_iter_backward_char (&start))
9719             break;
9720         }
9721       gtk_text_iter_forward_sentence_end (&end);
9722       break;
9723    case ATK_TEXT_BOUNDARY_LINE_START:
9724    case ATK_TEXT_BOUNDARY_LINE_END:
9725       break;
9726     }
9727 
9728 
9729   *start_offset = gtk_text_iter_get_offset (&start);
9730   *end_offset = gtk_text_iter_get_offset (&end);
9731 
9732   return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
9733 }
9734 
9735 static gchar*
9736 exo_icon_view_item_accessible_text_get_text_after_offset (AtkText         *text,
9737                                                           gint            offset,
9738                                                           AtkTextBoundary boundary_type,
9739                                                           gint            *start_offset,
9740                                                           gint            *end_offset)
9741 {
9742   ExoIconViewItemAccessible *item;
9743   GtkTextIter start, end;
9744   GtkTextBuffer *buffer;
9745 
9746   item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (text);
9747 
9748   if (!EXO_IS_ICON_VIEW (item->widget))
9749     return NULL;
9750 
9751   if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT))
9752     return NULL;
9753 
9754   buffer = item->text_buffer;
9755 
9756   if (!gtk_text_buffer_get_char_count (buffer))
9757     {
9758       *start_offset = 0;
9759       *end_offset = 0;
9760       return g_strdup ("");
9761     }
9762   gtk_text_buffer_get_iter_at_offset (buffer, &start, offset);
9763 
9764   end = start;
9765 
9766   switch (boundary_type)
9767     {
9768     case ATK_TEXT_BOUNDARY_CHAR:
9769       gtk_text_iter_forward_char(&start);
9770       gtk_text_iter_forward_chars(&end, 2);
9771       break;
9772     case ATK_TEXT_BOUNDARY_WORD_START:
9773       if (gtk_text_iter_inside_word (&end))
9774         gtk_text_iter_forward_word_end (&end);
9775       while (!gtk_text_iter_starts_word (&end))
9776         {
9777           if (!gtk_text_iter_forward_char (&end))
9778             break;
9779         }
9780       start = end;
9781       if (!gtk_text_iter_is_end (&end))
9782         {
9783           gtk_text_iter_forward_word_end (&end);
9784           while (!gtk_text_iter_starts_word (&end))
9785             {
9786               if (!gtk_text_iter_forward_char (&end))
9787                 break;
9788             }
9789         }
9790       break;
9791     case ATK_TEXT_BOUNDARY_WORD_END:
9792       gtk_text_iter_forward_word_end (&end);
9793       start = end;
9794       if (!gtk_text_iter_is_end (&end))
9795         gtk_text_iter_forward_word_end (&end);
9796       break;
9797     case ATK_TEXT_BOUNDARY_SENTENCE_START:
9798       if (gtk_text_iter_inside_sentence (&end))
9799         gtk_text_iter_forward_sentence_end (&end);
9800       while (!gtk_text_iter_starts_sentence (&end))
9801         {
9802           if (!gtk_text_iter_forward_char (&end))
9803             break;
9804         }
9805       start = end;
9806       if (!gtk_text_iter_is_end (&end))
9807         {
9808           gtk_text_iter_forward_sentence_end (&end);
9809           while (!gtk_text_iter_starts_sentence (&end))
9810             {
9811               if (!gtk_text_iter_forward_char (&end))
9812                 break;
9813             }
9814         }
9815       break;
9816     case ATK_TEXT_BOUNDARY_SENTENCE_END:
9817       gtk_text_iter_forward_sentence_end (&end);
9818       start = end;
9819       if (!gtk_text_iter_is_end (&end))
9820         gtk_text_iter_forward_sentence_end (&end);
9821       break;
9822    case ATK_TEXT_BOUNDARY_LINE_START:
9823    case ATK_TEXT_BOUNDARY_LINE_END:
9824       break;
9825     }
9826   *start_offset = gtk_text_iter_get_offset (&start);
9827   *end_offset = gtk_text_iter_get_offset (&end);
9828 
9829   return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
9830 }
9831 
9832 static gint
9833 exo_icon_view_item_accessible_text_get_character_count (AtkText *text)
9834 {
9835   ExoIconViewItemAccessible *item;
9836 
9837   item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (text);
9838 
9839   if (!EXO_IS_ICON_VIEW (item->widget))
9840     return 0;
9841 
9842   if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT))
9843     return 0;
9844 
9845   return gtk_text_buffer_get_char_count (item->text_buffer);
9846 }
9847 
9848 static void
9849 exo_icon_view_item_accessible_text_get_character_extents (AtkText      *text,
9850                                                           gint         offset,
9851                                                           gint         *x,
9852                                                           gint         *y,
9853                                                           gint         *width,
9854                                                           gint         *height,
9855                                                           AtkCoordType coord_type)
9856 {
9857   ExoIconViewItemAccessible *item;
9858 
9859   item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (text);
9860 
9861   if (!EXO_IS_ICON_VIEW (item->widget))
9862     return;
9863 
9864   if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT))
9865     return;
9866 
9867 }
9868 
9869 static gint
9870 exo_icon_view_item_accessible_text_get_offset_at_point (AtkText      *text,
9871                                                         gint          x,
9872                                                         gint          y,
9873                                                         AtkCoordType coord_type)
9874 {
9875   ExoIconViewItemAccessible *item;
9876   gint offset = 0;
9877 
9878   item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (text);
9879 
9880   if (!EXO_IS_ICON_VIEW (item->widget))
9881     return -1;
9882 
9883   if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT))
9884     return -1;
9885 
9886   return offset;
9887 }
9888 
9889 static void
9890 atk_text_item_interface_init (AtkTextIface *iface)
9891 {
9892   iface->get_text = exo_icon_view_item_accessible_text_get_text;
9893   iface->get_character_at_offset = exo_icon_view_item_accessible_text_get_character_at_offset;
9894   iface->get_text_before_offset = exo_icon_view_item_accessible_text_get_text_before_offset;
9895   iface->get_text_at_offset = exo_icon_view_item_accessible_text_get_text_at_offset;
9896   iface->get_text_after_offset = exo_icon_view_item_accessible_text_get_text_after_offset;
9897   iface->get_character_count = exo_icon_view_item_accessible_text_get_character_count;
9898   iface->get_character_extents = exo_icon_view_item_accessible_text_get_character_extents;
9899   iface->get_offset_at_point = exo_icon_view_item_accessible_text_get_offset_at_point;
9900 }
9901 
9902 static void
9903 exo_icon_view_item_accessible_get_extents (AtkComponent *component,
9904                                            gint         *x,
9905                                            gint         *y,
9906                                            gint         *width,
9907                                            gint         *height,
9908                                            AtkCoordType  coord_type)
9909 {
9910   ExoIconViewItemAccessible *item;
9911   AtkObject *parent_obj;
9912   gint l_x, l_y;
9913 
9914   g_return_if_fail (EXO_IS_ICON_VIEW_ITEM_ACCESSIBLE (component));
9915 
9916   item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (component);
9917   if (!GTK_IS_WIDGET (item->widget))
9918     return;
9919 
9920   if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT))
9921     return;
9922 
9923   *width = item->item->area.width;
9924   *height = item->item->area.height;
9925   if (exo_icon_view_item_accessible_is_showing (item))
9926     {
9927       parent_obj = gtk_widget_get_accessible (item->widget);
9928       atk_component_get_extents (ATK_COMPONENT (parent_obj), &l_x, &l_y, NULL,
9929                                  NULL, coord_type);
9930       *x = l_x + item->item->area.x;
9931       *y = l_y + item->item->area.y;
9932     }
9933   else
9934     {
9935       *x = G_MININT;
9936       *y = G_MININT;
9937     }
9938 }
9939 
9940 static gboolean
9941 exo_icon_view_item_accessible_grab_focus (AtkComponent *component)
9942 {
9943   ExoIconViewItemAccessible *item;
9944   GtkWidget *toplevel;
9945 
9946   g_return_val_if_fail (EXO_IS_ICON_VIEW_ITEM_ACCESSIBLE (component), FALSE);
9947 
9948   item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (component);
9949   if (!GTK_IS_WIDGET (item->widget))
9950     return FALSE;
9951 
9952   gtk_widget_grab_focus (item->widget);
9953   exo_icon_view_set_cursor_item (EXO_ICON_VIEW (item->widget), item->item, -1);
9954   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (item->widget));
9955   if (gtk_widget_is_toplevel (toplevel))
9956     gtk_window_present (GTK_WINDOW (toplevel));
9957 
9958   return TRUE;
9959 }
9960 
9961 static void
9962 atk_component_item_interface_init (AtkComponentIface *iface)
9963 {
9964   iface->get_extents = exo_icon_view_item_accessible_get_extents;
9965   iface->grab_focus = exo_icon_view_item_accessible_grab_focus;
9966 }
9967 
9968 static gboolean
9969 exo_icon_view_item_accessible_add_state (ExoIconViewItemAccessible *item,
9970                                          AtkStateType               state_type,
9971                                          gboolean                   emit_signal)
9972 {
9973   gboolean rc;
9974 
9975   rc = atk_state_set_add_state (item->state_set, state_type);
9976   /*
9977    * The signal should only be generated if the value changed,
9978    * not when the item is set up.  So states that are set
9979    * initially should pass FALSE as the emit_signal argument.
9980    */
9981 
9982   if (emit_signal)
9983     {
9984       atk_object_notify_state_change (ATK_OBJECT (item), state_type, TRUE);
9985       /* If state_type is ATK_STATE_VISIBLE, additional notification */
9986       if (state_type == ATK_STATE_VISIBLE)
9987         g_signal_emit_by_name (item, "visible-data-changed");
9988     }
9989 
9990   return rc;
9991 }
9992 
9993 static gboolean
9994 exo_icon_view_item_accessible_remove_state (ExoIconViewItemAccessible *item,
9995                                             AtkStateType               state_type,
9996                                             gboolean                   emit_signal)
9997 {
9998   if (atk_state_set_contains_state (item->state_set, state_type))
9999     {
10000       gboolean rc;
10001 
10002       rc = atk_state_set_remove_state (item->state_set, state_type);
10003       /*
10004        * The signal should only be generated if the value changed,
10005        * not when the item is set up.  So states that are set
10006        * initially should pass FALSE as the emit_signal argument.
10007        */
10008 
10009       if (emit_signal)
10010         {
10011           atk_object_notify_state_change (ATK_OBJECT (item), state_type, FALSE);
10012           /* If state_type is ATK_STATE_VISIBLE, additional notification */
10013           if (state_type == ATK_STATE_VISIBLE)
10014             g_signal_emit_by_name (item, "visible-data-changed");
10015         }
10016 
10017       return rc;
10018     }
10019   else
10020     return FALSE;
10021 }
10022 
10023 static gboolean
10024 exo_icon_view_item_accessible_is_showing (ExoIconViewItemAccessible *item)
10025 {
10026   ExoIconView *icon_view;
10027   GtkAllocation allocation;
10028   GdkRectangle visible_rect;
10029   gboolean is_showing;
10030 
10031   /*
10032    * An item is considered "SHOWING" if any part of the item is in the
10033    * visible rectangle.
10034    */
10035 
10036   if (!EXO_IS_ICON_VIEW (item->widget))
10037     return FALSE;
10038 
10039   if (item->item == NULL)
10040     return FALSE;
10041 
10042   gtk_widget_get_allocation (item->widget, &allocation);
10043 
10044   icon_view = EXO_ICON_VIEW (item->widget);
10045   visible_rect.x = 0;
10046   if (icon_view->priv->hadjustment)
10047     visible_rect.x += gtk_adjustment_get_value (icon_view->priv->hadjustment);
10048   visible_rect.y = 0;
10049   if (icon_view->priv->hadjustment)
10050     visible_rect.y += gtk_adjustment_get_value (icon_view->priv->vadjustment);
10051   visible_rect.width = allocation.width;
10052   visible_rect.height = allocation.height;
10053 
10054   if (((item->item->area.x + item->item->area.width) < visible_rect.x) ||
10055      ((item->item->area.y + item->item->area.height) < (visible_rect.y)) ||
10056      (item->item->area.x > (visible_rect.x + visible_rect.width)) ||
10057      (item->item->area.y > (visible_rect.y + visible_rect.height)))
10058     is_showing =  FALSE;
10059   else
10060     is_showing = TRUE;
10061 
10062   return is_showing;
10063 }
10064 
10065 static gboolean
10066 exo_icon_view_item_accessible_set_visibility (ExoIconViewItemAccessible *item,
10067                                               gboolean                   emit_signal)
10068 {
10069   if (exo_icon_view_item_accessible_is_showing (item))
10070     return exo_icon_view_item_accessible_add_state (item, ATK_STATE_SHOWING,
10071 						    emit_signal);
10072   else
10073     return exo_icon_view_item_accessible_remove_state (item, ATK_STATE_SHOWING,
10074 						       emit_signal);
10075 }
10076 
10077 static void
10078 exo_icon_view_item_accessible_object_init (ExoIconViewItemAccessible *item)
10079 {
10080   gint i;
10081 
10082   item->state_set = atk_state_set_new ();
10083 
10084   atk_state_set_add_state (item->state_set, ATK_STATE_ENABLED);
10085   atk_state_set_add_state (item->state_set, ATK_STATE_FOCUSABLE);
10086   atk_state_set_add_state (item->state_set, ATK_STATE_SENSITIVE);
10087   atk_state_set_add_state (item->state_set, ATK_STATE_SELECTABLE);
10088   atk_state_set_add_state (item->state_set, ATK_STATE_VISIBLE);
10089 
10090   for (i = 0; i < LAST_ACTION; i++)
10091     item->action_descriptions[i] = NULL;
10092 
10093   item->image_description = NULL;
10094 
10095   item->action_idle_handler = 0;
10096 }
10097 
10098 static void
10099 exo_icon_view_item_accessible_finalize (GObject *object)
10100 {
10101   ExoIconViewItemAccessible *item;
10102   gint i;
10103 
10104   g_return_if_fail (EXO_IS_ICON_VIEW_ITEM_ACCESSIBLE (object));
10105 
10106   item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (object);
10107 
10108   if (item->widget)
10109     g_object_remove_weak_pointer (G_OBJECT (item->widget), (gpointer) &item->widget);
10110 
10111   if (item->state_set)
10112     g_object_unref (item->state_set);
10113 
10114   if (item->text_buffer)
10115      g_object_unref (item->text_buffer);
10116 
10117   for (i = 0; i < LAST_ACTION; i++)
10118     g_free (item->action_descriptions[i]);
10119 
10120   g_free (item->image_description);
10121 
10122   if (item->action_idle_handler)
10123     {
10124       g_source_remove (item->action_idle_handler);
10125       item->action_idle_handler = 0;
10126     }
10127 
10128   G_OBJECT_CLASS (accessible_item_parent_class)->finalize (object);
10129 }
10130 
10131 static AtkObject*
10132 exo_icon_view_item_accessible_get_parent (AtkObject *obj)
10133 {
10134   ExoIconViewItemAccessible *item;
10135 
10136   g_return_val_if_fail (EXO_IS_ICON_VIEW_ITEM_ACCESSIBLE (obj), NULL);
10137   item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (obj);
10138 
10139   if (item->widget)
10140     return gtk_widget_get_accessible (item->widget);
10141   else
10142     return NULL;
10143 }
10144 
10145 static gint
10146 exo_icon_view_item_accessible_get_index_in_parent (AtkObject *obj)
10147 {
10148   ExoIconViewItemAccessible *item;
10149 
10150   g_return_val_if_fail (EXO_IS_ICON_VIEW_ITEM_ACCESSIBLE (obj), 0);
10151   item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (obj);
10152 
10153   return item->item->index;
10154 }
10155 
10156 static AtkStateSet *
10157 exo_icon_view_item_accessible_ref_state_set (AtkObject *obj)
10158 {
10159   ExoIconViewItemAccessible *item;
10160   ExoIconView *icon_view;
10161 
10162   item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (obj);
10163   g_return_val_if_fail (item->state_set, NULL);
10164 
10165   if (!item->widget)
10166     return NULL;
10167 
10168   icon_view = EXO_ICON_VIEW (item->widget);
10169   if (icon_view->priv->cursor_item == item->item)
10170     atk_state_set_add_state (item->state_set, ATK_STATE_FOCUSED);
10171   else
10172     atk_state_set_remove_state (item->state_set, ATK_STATE_FOCUSED);
10173   if (item->item->selected)
10174     atk_state_set_add_state (item->state_set, ATK_STATE_SELECTED);
10175   else
10176     atk_state_set_remove_state (item->state_set, ATK_STATE_SELECTED);
10177 
10178   return g_object_ref (item->state_set);
10179 }
10180 
10181 static void
10182 exo_icon_view_item_accessible_class_init (AtkObjectClass *klass)
10183 {
10184   GObjectClass *gobject_class;
10185 
10186   accessible_item_parent_class = g_type_class_peek_parent (klass);
10187 
10188   gobject_class = (GObjectClass *)klass;
10189 
10190   gobject_class->finalize = exo_icon_view_item_accessible_finalize;
10191 
10192   klass->get_index_in_parent = exo_icon_view_item_accessible_get_index_in_parent;
10193   klass->get_parent = exo_icon_view_item_accessible_get_parent;
10194   klass->ref_state_set = exo_icon_view_item_accessible_ref_state_set;
10195 }
10196 
10197 static GType
10198 exo_icon_view_item_accessible_get_type (void)
10199 {
10200   static GType type = 0;
10201 
10202   if (!type)
10203     {
10204       const GTypeInfo tinfo =
10205       {
10206         sizeof (ExoIconViewItemAccessibleClass),
10207         (GBaseInitFunc) NULL, /* base init */
10208         (GBaseFinalizeFunc) NULL, /* base finalize */
10209         (GClassInitFunc) exo_icon_view_item_accessible_class_init, /* class init */
10210         (GClassFinalizeFunc) NULL, /* class finalize */
10211         NULL, /* class data */
10212         sizeof (ExoIconViewItemAccessible), /* instance size */
10213         0, /* nb preallocs */
10214         (GInstanceInitFunc) exo_icon_view_item_accessible_object_init, /* instance init */
10215         NULL /* value table */
10216       };
10217 
10218       const GInterfaceInfo atk_component_info =
10219       {
10220         (GInterfaceInitFunc) atk_component_item_interface_init,
10221         (GInterfaceFinalizeFunc) NULL,
10222         NULL
10223       };
10224       const GInterfaceInfo atk_action_info =
10225       {
10226         (GInterfaceInitFunc) atk_action_item_interface_init,
10227         (GInterfaceFinalizeFunc) NULL,
10228         NULL
10229       };
10230       const GInterfaceInfo atk_image_info =
10231       {
10232         (GInterfaceInitFunc) atk_image_item_interface_init,
10233         (GInterfaceFinalizeFunc) NULL,
10234         NULL
10235       };
10236       const GInterfaceInfo atk_text_info =
10237       {
10238         (GInterfaceInitFunc) atk_text_item_interface_init,
10239         (GInterfaceFinalizeFunc) NULL,
10240         NULL
10241       };
10242 
10243       type = g_type_register_static (ATK_TYPE_OBJECT,
10244                                      I_("ExoIconViewItemAccessible"), &tinfo, 0);
10245       g_type_add_interface_static (type, ATK_TYPE_COMPONENT,
10246                                    &atk_component_info);
10247       g_type_add_interface_static (type, ATK_TYPE_ACTION,
10248                                    &atk_action_info);
10249       g_type_add_interface_static (type, ATK_TYPE_IMAGE,
10250                                    &atk_image_info);
10251       g_type_add_interface_static (type, ATK_TYPE_TEXT,
10252                                    &atk_text_info);
10253     }
10254 
10255   return type;
10256 }
10257 
10258 #define EXO_TYPE_ICON_VIEW_ACCESSIBLE      (exo_icon_view_accessible_get_type ())
10259 #define EXO_ICON_VIEW_ACCESSIBLE(obj)      (G_TYPE_CHECK_INSTANCE_CAST ((obj), EXO_TYPE_ICON_VIEW_ACCESSIBLE, ExoIconViewAccessible))
10260 #define EXO_IS_ICON_VIEW_ACCESSIBLE(obj)   (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EXO_TYPE_ICON_VIEW_ACCESSIBLE))
10261 
10262 static GType exo_icon_view_accessible_get_type (void);
10263 
10264 typedef struct
10265 {
10266    AtkObject parent;
10267 } ExoIconViewAccessible;
10268 
10269 typedef struct
10270 {
10271   AtkObject *item;
10272   gint       index;
10273 } ExoIconViewItemAccessibleInfo;
10274 
10275 typedef struct
10276 {
10277   GList *items;
10278 
10279   GtkAdjustment *old_hadj;
10280   GtkAdjustment *old_vadj;
10281 
10282   GtkTreeModel *model;
10283 
10284 } ExoIconViewAccessiblePrivate;
10285 
10286 static ExoIconViewAccessiblePrivate *
10287 exo_icon_view_accessible_get_priv (AtkObject *accessible)
10288 {
10289   return g_object_get_qdata (G_OBJECT (accessible),
10290                              accessible_private_data_quark);
10291 }
10292 
10293 static void
10294 exo_icon_view_item_accessible_info_new (AtkObject *accessible,
10295                                         AtkObject *item,
10296                                         gint       index)
10297 {
10298   ExoIconViewItemAccessibleInfo *info;
10299   ExoIconViewItemAccessibleInfo *tmp_info;
10300   ExoIconViewAccessiblePrivate *priv;
10301   GList *items;
10302 
10303   info = g_new (ExoIconViewItemAccessibleInfo, 1);
10304   info->item = item;
10305   info->index = index;
10306 
10307   priv = exo_icon_view_accessible_get_priv (accessible);
10308   items = priv->items;
10309   while (items)
10310     {
10311       tmp_info = items->data;
10312       if (tmp_info->index > index)
10313         break;
10314       items = items->next;
10315     }
10316   priv->items = g_list_insert_before (priv->items, items, info);
10317   priv->old_hadj = NULL;
10318   priv->old_vadj = NULL;
10319 }
10320 
10321 static gint
10322 exo_icon_view_accessible_get_n_children (AtkObject *accessible)
10323 {
10324   ExoIconView *icon_view;
10325   GtkWidget *widget;
10326 
10327   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
10328   if (!widget)
10329       return 0;
10330 
10331   icon_view = EXO_ICON_VIEW (widget);
10332 
10333   return g_list_length (icon_view->priv->items);
10334 }
10335 
10336 static AtkObject *
10337 exo_icon_view_accessible_find_child (AtkObject *accessible,
10338                                      gint       index)
10339 {
10340   ExoIconViewAccessiblePrivate *priv;
10341   ExoIconViewItemAccessibleInfo *info;
10342   GList *items;
10343 
10344   priv = exo_icon_view_accessible_get_priv (accessible);
10345   items = priv->items;
10346 
10347   while (items)
10348     {
10349       info = items->data;
10350       if (info->index == index)
10351         return info->item;
10352       items = items->next;
10353     }
10354   return NULL;
10355 }
10356 
10357 static AtkObject *
10358 exo_icon_view_accessible_ref_child (AtkObject *accessible,
10359                                     gint       index)
10360 {
10361   ExoIconView *icon_view;
10362   GtkWidget *widget;
10363   GList *icons;
10364   AtkObject *obj;
10365   ExoIconViewItemAccessible *a11y_item;
10366 
10367   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
10368   if (!widget)
10369     return NULL;
10370 
10371   icon_view = EXO_ICON_VIEW (widget);
10372   icons = g_list_nth (icon_view->priv->items, index);
10373   obj = NULL;
10374   if (icons)
10375     {
10376       ExoIconViewItem *item = icons->data;
10377 
10378       g_return_val_if_fail (item->index == index, NULL);
10379       obj = exo_icon_view_accessible_find_child (accessible, index);
10380       if (!obj)
10381         {
10382           gchar *text;
10383 
10384           obj = g_object_new (exo_icon_view_item_accessible_get_type (), NULL);
10385           exo_icon_view_item_accessible_info_new (accessible,
10386                                                   obj,
10387                                                   index);
10388           obj->role = ATK_ROLE_ICON;
10389           a11y_item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (obj);
10390           a11y_item->item = item;
10391           a11y_item->widget = widget;
10392           a11y_item->text_buffer = gtk_text_buffer_new (NULL);
10393 
10394 	  exo_icon_view_set_cell_data (icon_view, item);
10395           text = get_text (icon_view, item);
10396           if (text)
10397             {
10398               gtk_text_buffer_set_text (a11y_item->text_buffer, text, -1);
10399               g_free (text);
10400             }
10401 
10402           exo_icon_view_item_accessible_set_visibility (a11y_item, FALSE);
10403           g_object_add_weak_pointer (G_OBJECT (widget), (gpointer) &(a11y_item->widget));
10404        }
10405       g_object_ref (obj);
10406     }
10407   return obj;
10408 }
10409 
10410 static void
10411 exo_icon_view_accessible_traverse_items (ExoIconViewAccessible *view,
10412                                          GList                 *list)
10413 {
10414   ExoIconViewAccessiblePrivate *priv;
10415   ExoIconViewItemAccessibleInfo *info;
10416   ExoIconViewItemAccessible *item;
10417   GList *items;
10418 
10419   priv =  exo_icon_view_accessible_get_priv (ATK_OBJECT (view));
10420   if (priv->items)
10421     {
10422       GtkWidget *widget;
10423       gboolean act_on_item;
10424 
10425       widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (view));
10426       if (widget == NULL)
10427         return;
10428 
10429       items = priv->items;
10430 
10431       act_on_item = (list == NULL);
10432 
10433       while (items)
10434         {
10435 
10436           info = (ExoIconViewItemAccessibleInfo *)items->data;
10437           item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (info->item);
10438 
10439           if (act_on_item == FALSE && list == items)
10440             act_on_item = TRUE;
10441 
10442           if (act_on_item)
10443 	    exo_icon_view_item_accessible_set_visibility (item, TRUE);
10444 
10445           items = items->next;
10446        }
10447    }
10448 }
10449 
10450 static void
10451 exo_icon_view_accessible_adjustment_changed (GtkAdjustment *adjustment,
10452                                              ExoIconView   *icon_view)
10453 {
10454   AtkObject *obj;
10455   ExoIconViewAccessible *view;
10456 
10457   /*
10458    * The scrollbars have changed
10459    */
10460   obj = gtk_widget_get_accessible (GTK_WIDGET (icon_view));
10461   view = EXO_ICON_VIEW_ACCESSIBLE (obj);
10462 
10463   exo_icon_view_accessible_traverse_items (view, NULL);
10464 }
10465 
10466 static void
10467 exo_icon_view_accessible_set_scroll_adjustments (GtkWidget      *widget,
10468                                                  GtkAdjustment *hadj,
10469                                                  GtkAdjustment *vadj)
10470 {
10471   AtkObject *atk_obj;
10472   ExoIconViewAccessiblePrivate *priv;
10473 
10474   atk_obj = gtk_widget_get_accessible (widget);
10475   priv = exo_icon_view_accessible_get_priv (atk_obj);
10476 
10477   if (priv->old_hadj != hadj)
10478     {
10479       if (priv->old_hadj)
10480         {
10481           g_object_remove_weak_pointer (G_OBJECT (priv->old_hadj),
10482                                         (gpointer *)&priv->old_hadj);
10483 
10484           g_signal_handlers_disconnect_by_func (priv->old_hadj,
10485                                                 (gpointer) exo_icon_view_accessible_adjustment_changed,
10486                                                 widget);
10487         }
10488       priv->old_hadj = hadj;
10489       if (priv->old_hadj)
10490         {
10491           g_object_add_weak_pointer (G_OBJECT (priv->old_hadj),
10492                                      (gpointer *)&priv->old_hadj);
10493           g_signal_connect (hadj,
10494                             "value-changed",
10495                             G_CALLBACK (exo_icon_view_accessible_adjustment_changed),
10496                             widget);
10497         }
10498     }
10499   if (priv->old_vadj != vadj)
10500     {
10501       if (priv->old_vadj)
10502         {
10503           g_object_remove_weak_pointer (G_OBJECT (priv->old_vadj),
10504                                         (gpointer *)&priv->old_vadj);
10505 
10506           g_signal_handlers_disconnect_by_func (priv->old_vadj,
10507                                                 (gpointer) exo_icon_view_accessible_adjustment_changed,
10508                                                 widget);
10509         }
10510       priv->old_vadj = vadj;
10511       if (priv->old_vadj)
10512         {
10513           g_object_add_weak_pointer (G_OBJECT (priv->old_vadj),
10514                                      (gpointer *)&priv->old_vadj);
10515           g_signal_connect (vadj,
10516                             "value-changed",
10517                             G_CALLBACK (exo_icon_view_accessible_adjustment_changed),
10518                             widget);
10519         }
10520     }
10521 }
10522 
10523 static void
10524 exo_icon_view_accessible_model_row_changed (GtkTreeModel *tree_model,
10525                                             GtkTreePath  *path,
10526                                             GtkTreeIter  *iter,
10527                                             gpointer      user_data)
10528 {
10529   AtkObject *atk_obj;
10530   gint index;
10531   GtkWidget *widget;
10532   ExoIconView *icon_view;
10533   ExoIconViewItem *item;
10534   //ExoIconViewAccessible *a11y_view;
10535   ExoIconViewItemAccessible *a11y_item;
10536   const gchar *name;
10537   gchar *text;
10538 
10539   atk_obj = gtk_widget_get_accessible (GTK_WIDGET (user_data));
10540   //a11y_view = EXO_ICON_VIEW_ACCESSIBLE (atk_obj);
10541   index = gtk_tree_path_get_indices(path)[0];
10542   a11y_item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (
10543       exo_icon_view_accessible_find_child (atk_obj, index));
10544 
10545   if (a11y_item)
10546     {
10547       widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_obj));
10548       icon_view = EXO_ICON_VIEW (widget);
10549       item = a11y_item->item;
10550 
10551       name = atk_object_get_name (ATK_OBJECT (a11y_item));
10552 
10553       if (!name || strcmp (name, "") == 0)
10554         {
10555           exo_icon_view_set_cell_data (icon_view, item);
10556           text = get_text (icon_view, item);
10557           if (text)
10558             {
10559               gtk_text_buffer_set_text (a11y_item->text_buffer, text, -1);
10560               g_free (text);
10561             }
10562         }
10563     }
10564 
10565   g_signal_emit_by_name (atk_obj, "visible-data-changed");
10566 
10567   return;
10568 }
10569 
10570 static void
10571 exo_icon_view_accessible_model_row_inserted (GtkTreeModel *tree_model,
10572                                              GtkTreePath  *path,
10573                                              GtkTreeIter  *iter,
10574                                              gpointer     user_data)
10575 {
10576   ExoIconViewAccessiblePrivate *priv;
10577   ExoIconViewItemAccessibleInfo *info;
10578   ExoIconViewAccessible *view;
10579   ExoIconViewItemAccessible *item;
10580   GList *items;
10581   GList *tmp_list;
10582   AtkObject *atk_obj;
10583   gint index;
10584 
10585   index = gtk_tree_path_get_indices(path)[0];
10586   atk_obj = gtk_widget_get_accessible (GTK_WIDGET (user_data));
10587   view = EXO_ICON_VIEW_ACCESSIBLE (atk_obj);
10588   priv = exo_icon_view_accessible_get_priv (atk_obj);
10589 
10590   items = priv->items;
10591   tmp_list = NULL;
10592   while (items)
10593     {
10594       info = items->data;
10595       item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (info->item);
10596       if (info->index != item->item->index)
10597         {
10598           if (info->index < index)
10599             g_warning ("Unexpected index value on insertion %d %d", index, info->index);
10600 
10601           if (tmp_list == NULL)
10602             tmp_list = items;
10603 
10604           info->index = item->item->index;
10605         }
10606 
10607       items = items->next;
10608     }
10609   exo_icon_view_accessible_traverse_items (view, tmp_list);
10610   g_signal_emit_by_name (atk_obj, "children-changed::add",
10611                          index, NULL, NULL);
10612   return;
10613 }
10614 
10615 static void
10616 exo_icon_view_accessible_model_row_deleted (GtkTreeModel *tree_model,
10617                                             GtkTreePath  *path,
10618                                             gpointer     user_data)
10619 {
10620   ExoIconViewAccessiblePrivate *priv;
10621   ExoIconViewItemAccessibleInfo *info;
10622   ExoIconViewAccessible *view;
10623   ExoIconViewItemAccessible *item;
10624   GList *items;
10625   GList *tmp_list;
10626   GList *deleted_item;
10627   AtkObject *atk_obj;
10628   gint index;
10629 
10630   index = gtk_tree_path_get_indices(path)[0];
10631   atk_obj = gtk_widget_get_accessible (GTK_WIDGET (user_data));
10632   view = EXO_ICON_VIEW_ACCESSIBLE (atk_obj);
10633   priv = exo_icon_view_accessible_get_priv (atk_obj);
10634 
10635   items = priv->items;
10636   tmp_list = NULL;
10637   deleted_item = NULL;
10638   info = NULL;
10639   while (items)
10640     {
10641       info = items->data;
10642       item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (info->item);
10643       if (info->index == index)
10644         {
10645           deleted_item = items;
10646         }
10647       if (info->index != item->item->index)
10648         {
10649           if (tmp_list == NULL)
10650             tmp_list = items;
10651 
10652           info->index = item->item->index;
10653         }
10654 
10655       items = items->next;
10656     }
10657   exo_icon_view_accessible_traverse_items (view, tmp_list);
10658   if (deleted_item)
10659     {
10660       info = deleted_item->data;
10661       exo_icon_view_item_accessible_add_state (EXO_ICON_VIEW_ITEM_ACCESSIBLE (info->item), ATK_STATE_DEFUNCT, TRUE);
10662       g_signal_emit_by_name (atk_obj, "children-changed::remove",
10663                              index, NULL, NULL);
10664       priv->items = g_list_remove_link (priv->items, deleted_item);
10665       g_free (info);
10666     }
10667 
10668   return;
10669 }
10670 
10671 static gint
10672 exo_icon_view_accessible_item_compare (ExoIconViewItemAccessibleInfo *i1,
10673                                        ExoIconViewItemAccessibleInfo *i2)
10674 {
10675   return i1->index - i2->index;
10676 }
10677 
10678 static void
10679 exo_icon_view_accessible_model_rows_reordered (GtkTreeModel *tree_model,
10680                                                GtkTreePath  *path,
10681                                                GtkTreeIter  *iter,
10682                                                gint         *new_order,
10683                                                gpointer     user_data)
10684 {
10685   ExoIconViewAccessiblePrivate *priv;
10686   ExoIconViewItemAccessibleInfo *info;
10687   ExoIconView *icon_view;
10688   ExoIconViewItemAccessible *item;
10689   GList *items;
10690   AtkObject *atk_obj;
10691   gint *order;
10692   gint length, i;
10693 
10694   atk_obj = gtk_widget_get_accessible (GTK_WIDGET (user_data));
10695   icon_view = EXO_ICON_VIEW (user_data);
10696   priv = exo_icon_view_accessible_get_priv (atk_obj);
10697 
10698   length = gtk_tree_model_iter_n_children (tree_model, NULL);
10699 
10700   order = g_new (gint, length);
10701   for (i = 0; i < length; i++)
10702     order [new_order[i]] = i;
10703 
10704   items = priv->items;
10705   while (items)
10706     {
10707       info = items->data;
10708       item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (info->item);
10709       info->index = order[info->index];
10710       item->item = g_list_nth_data (icon_view->priv->items, info->index);
10711       items = items->next;
10712     }
10713   g_free (order);
10714   priv->items = g_list_sort (priv->items,
10715                              (GCompareFunc)exo_icon_view_accessible_item_compare);
10716 
10717   return;
10718 }
10719 
10720 static void
10721 exo_icon_view_accessible_disconnect_model_signals (GtkTreeModel *model,
10722                                                    GtkWidget *widget)
10723 {
10724   GObject *obj;
10725 
10726   obj = G_OBJECT (model);
10727   g_signal_handlers_disconnect_by_func (obj, (gpointer) exo_icon_view_accessible_model_row_changed, widget);
10728   g_signal_handlers_disconnect_by_func (obj, (gpointer) exo_icon_view_accessible_model_row_inserted, widget);
10729   g_signal_handlers_disconnect_by_func (obj, (gpointer) exo_icon_view_accessible_model_row_deleted, widget);
10730   g_signal_handlers_disconnect_by_func (obj, (gpointer) exo_icon_view_accessible_model_rows_reordered, widget);
10731 }
10732 
10733 static void
10734 exo_icon_view_accessible_connect_model_signals (ExoIconView *icon_view)
10735 {
10736   GObject *obj;
10737 
10738   obj = G_OBJECT (icon_view->priv->model);
10739   g_signal_connect_data (obj, "row-changed",
10740                          (GCallback) exo_icon_view_accessible_model_row_changed,
10741                          icon_view, NULL, 0);
10742   g_signal_connect_data (obj, "row-inserted",
10743                          (GCallback) exo_icon_view_accessible_model_row_inserted,
10744                          icon_view, NULL, G_CONNECT_AFTER);
10745   g_signal_connect_data (obj, "row-deleted",
10746                          (GCallback) exo_icon_view_accessible_model_row_deleted,
10747                          icon_view, NULL, G_CONNECT_AFTER);
10748   g_signal_connect_data (obj, "rows-reordered",
10749                          (GCallback) exo_icon_view_accessible_model_rows_reordered,
10750                          icon_view, NULL, G_CONNECT_AFTER);
10751 }
10752 
10753 static void
10754 exo_icon_view_accessible_clear_cache (ExoIconViewAccessiblePrivate *priv)
10755 {
10756   ExoIconViewItemAccessibleInfo *info;
10757   GList *items;
10758 
10759   items = priv->items;
10760   while (items)
10761     {
10762       info = (ExoIconViewItemAccessibleInfo *) items->data;
10763       g_object_unref (info->item);
10764       g_free (items->data);
10765       items = items->next;
10766     }
10767   g_list_free (priv->items);
10768   priv->items = NULL;
10769 }
10770 
10771 static void
10772 exo_icon_view_accessible_notify_gtk (GObject *obj,
10773                                      GParamSpec *pspec)
10774 {
10775   ExoIconView *icon_view;
10776   GtkWidget *widget;
10777   AtkObject *atk_obj;
10778   ExoIconViewAccessiblePrivate *priv;
10779 
10780   if (strcmp (pspec->name, "model") == 0)
10781     {
10782       widget = GTK_WIDGET (obj);
10783       atk_obj = gtk_widget_get_accessible (widget);
10784       priv = exo_icon_view_accessible_get_priv (atk_obj);
10785       if (priv->model)
10786         {
10787           g_object_remove_weak_pointer (G_OBJECT (priv->model),
10788                                         (gpointer *)&priv->model);
10789           exo_icon_view_accessible_disconnect_model_signals (priv->model, widget);
10790         }
10791       exo_icon_view_accessible_clear_cache (priv);
10792 
10793       icon_view = EXO_ICON_VIEW (obj);
10794       priv->model = icon_view->priv->model;
10795       /* If there is no model the ExoIconView is probably being destroyed */
10796       if (priv->model)
10797         {
10798           g_object_add_weak_pointer (G_OBJECT (priv->model), (gpointer *)&priv->model);
10799           exo_icon_view_accessible_connect_model_signals (icon_view);
10800         }
10801     }
10802 
10803   return;
10804 }
10805 
10806 static void
10807 exo_icon_view_accessible_initialize (AtkObject *accessible,
10808                                      gpointer   data)
10809 {
10810   ExoIconViewAccessiblePrivate *priv;
10811   ExoIconView *icon_view;
10812 
10813   if (ATK_OBJECT_CLASS (accessible_parent_class)->initialize)
10814     ATK_OBJECT_CLASS (accessible_parent_class)->initialize (accessible, data);
10815 
10816   priv = g_new0 (ExoIconViewAccessiblePrivate, 1);
10817   g_object_set_qdata (G_OBJECT (accessible),
10818                       accessible_private_data_quark,
10819                       priv);
10820 
10821   icon_view = EXO_ICON_VIEW (data);
10822   if (icon_view->priv->hadjustment)
10823     {
10824       priv->old_hadj = icon_view->priv->hadjustment;
10825       g_object_add_weak_pointer (G_OBJECT (priv->old_hadj), (gpointer *)&priv->old_hadj);
10826       g_signal_connect (icon_view->priv->hadjustment,
10827                         "value-changed",
10828                         G_CALLBACK (exo_icon_view_accessible_adjustment_changed),
10829                         icon_view);
10830     }
10831   if (icon_view->priv->vadjustment)
10832     {
10833       priv->old_vadj = icon_view->priv->vadjustment;
10834       g_object_add_weak_pointer (G_OBJECT (priv->old_vadj), (gpointer *)&priv->old_vadj);
10835       g_signal_connect (icon_view->priv->vadjustment,
10836                         "value-changed",
10837                         G_CALLBACK (exo_icon_view_accessible_adjustment_changed),
10838                         icon_view);
10839     }
10840   g_signal_connect_after (data,
10841                           "set-scroll-adjustments",
10842                           G_CALLBACK (exo_icon_view_accessible_set_scroll_adjustments),
10843                           NULL);
10844   g_signal_connect (data,
10845                     "notify",
10846                     G_CALLBACK (exo_icon_view_accessible_notify_gtk),
10847                     NULL);
10848 
10849   priv->model = icon_view->priv->model;
10850   if (priv->model)
10851     {
10852       g_object_add_weak_pointer (G_OBJECT (priv->model), (gpointer *)&priv->model);
10853       exo_icon_view_accessible_connect_model_signals (icon_view);
10854     }
10855 
10856   accessible->role = ATK_ROLE_LAYERED_PANE;
10857 }
10858 
10859 static void
10860 exo_icon_view_accessible_finalize (GObject *object)
10861 {
10862   ExoIconViewAccessiblePrivate *priv;
10863 
10864   priv = exo_icon_view_accessible_get_priv (ATK_OBJECT (object));
10865   exo_icon_view_accessible_clear_cache (priv);
10866 
10867   g_free (priv);
10868 
10869   G_OBJECT_CLASS (accessible_parent_class)->finalize (object);
10870 }
10871 
10872 #if !GTK_CHECK_VERSION(3, 0, 0)
10873 static void
10874 exo_icon_view_accessible_destroyed (GtkWidget *widget,
10875                                     GtkAccessible *accessible)
10876 {
10877   AtkObject *atk_obj;
10878   ExoIconViewAccessiblePrivate *priv;
10879 
10880   atk_obj = ATK_OBJECT (accessible);
10881   priv = exo_icon_view_accessible_get_priv (atk_obj);
10882   if (priv->old_hadj)
10883     {
10884       g_object_remove_weak_pointer (G_OBJECT (priv->old_hadj),
10885                                     (gpointer *)&priv->old_hadj);
10886 
10887       g_signal_handlers_disconnect_by_func (priv->old_hadj,
10888                                             (gpointer) exo_icon_view_accessible_adjustment_changed,
10889                                             widget);
10890       priv->old_hadj = NULL;
10891     }
10892   if (priv->old_vadj)
10893     {
10894       g_object_remove_weak_pointer (G_OBJECT (priv->old_vadj),
10895                                     (gpointer *)&priv->old_vadj);
10896 
10897       g_signal_handlers_disconnect_by_func (priv->old_vadj,
10898                                             (gpointer) exo_icon_view_accessible_adjustment_changed,
10899                                             widget);
10900       priv->old_vadj = NULL;
10901     }
10902 }
10903 
10904 static void
10905 exo_icon_view_accessible_connect_widget_destroyed (GtkAccessible *accessible)
10906 {
10907   if (gtk_accessible_get_widget (accessible))
10908     {
10909       g_signal_connect_after (accessible->widget,
10910                               "destroy",
10911                               G_CALLBACK (exo_icon_view_accessible_destroyed),
10912                               accessible);
10913     }
10914   GTK_ACCESSIBLE_CLASS (accessible_parent_class)->connect_widget_destroyed (accessible);
10915 }
10916 #endif
10917 
10918 static void
10919 exo_icon_view_accessible_class_init (AtkObjectClass *klass)
10920 {
10921   GObjectClass *gobject_class;
10922 #if !GTK_CHECK_VERSION(3, 0, 0)
10923   GtkAccessibleClass *accessible_class;
10924 #endif
10925 
10926   accessible_parent_class = g_type_class_peek_parent (klass);
10927 
10928   gobject_class = (GObjectClass *)klass;
10929 
10930   gobject_class->finalize = exo_icon_view_accessible_finalize;
10931 
10932   klass->get_n_children = exo_icon_view_accessible_get_n_children;
10933   klass->ref_child = exo_icon_view_accessible_ref_child;
10934   klass->initialize = exo_icon_view_accessible_initialize;
10935 
10936 #if !GTK_CHECK_VERSION(3, 0, 0)
10937   accessible_class = (GtkAccessibleClass *)klass;
10938   accessible_class->connect_widget_destroyed = exo_icon_view_accessible_connect_widget_destroyed;
10939 #endif
10940 
10941   accessible_private_data_quark = g_quark_from_static_string ("icon_view-accessible-private-data");
10942 }
10943 
10944 static AtkObject*
10945 exo_icon_view_accessible_ref_accessible_at_point (AtkComponent *component,
10946                                                   gint          x,
10947                                                   gint          y,
10948                                                   AtkCoordType  coord_type)
10949 {
10950   GtkWidget *widget;
10951   ExoIconView *icon_view;
10952   ExoIconViewItem *item;
10953   gint x_pos, y_pos;
10954 
10955   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
10956   if (widget == NULL)
10957   /* State is defunct */
10958     return NULL;
10959 
10960   icon_view = EXO_ICON_VIEW (widget);
10961   atk_component_get_extents (component, &x_pos, &y_pos, NULL, NULL, coord_type);
10962   item = exo_icon_view_get_item_at_coords (icon_view, x - x_pos, y - y_pos, TRUE, NULL);
10963   if (item)
10964     return exo_icon_view_accessible_ref_child (ATK_OBJECT (component), item->index);
10965 
10966   return NULL;
10967 }
10968 
10969 static void
10970 atk_component_interface_init (AtkComponentIface *iface)
10971 {
10972   iface->ref_accessible_at_point = exo_icon_view_accessible_ref_accessible_at_point;
10973 }
10974 
10975 static gboolean
10976 exo_icon_view_accessible_add_selection (AtkSelection *selection,
10977                                         gint i)
10978 {
10979   GtkWidget *widget;
10980   ExoIconView *icon_view;
10981   ExoIconViewItem *item;
10982 
10983   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
10984   if (widget == NULL)
10985     return FALSE;
10986 
10987   icon_view = EXO_ICON_VIEW (widget);
10988 
10989   item = g_list_nth_data (icon_view->priv->items, i);
10990 
10991   if (!item)
10992     return FALSE;
10993 
10994   exo_icon_view_select_item (icon_view, item);
10995 
10996   return TRUE;
10997 }
10998 
10999 static gboolean
11000 exo_icon_view_accessible_clear_selection (AtkSelection *selection)
11001 {
11002   GtkWidget *widget;
11003   ExoIconView *icon_view;
11004 
11005   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
11006   if (widget == NULL)
11007     return FALSE;
11008 
11009   icon_view = EXO_ICON_VIEW (widget);
11010   exo_icon_view_unselect_all (icon_view);
11011 
11012   return TRUE;
11013 }
11014 
11015 static AtkObject*
11016 exo_icon_view_accessible_ref_selection (AtkSelection *selection,
11017                                         gint          i)
11018 {
11019   GList *l;
11020   GtkWidget *widget;
11021   ExoIconView *icon_view;
11022   ExoIconViewItem *item;
11023 
11024   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
11025   if (widget == NULL)
11026     return NULL;
11027 
11028   icon_view = EXO_ICON_VIEW (widget);
11029 
11030   l = icon_view->priv->items;
11031   while (l)
11032     {
11033       item = l->data;
11034       if (item->selected)
11035         {
11036           if (i == 0)
11037 	    return atk_object_ref_accessible_child (gtk_widget_get_accessible (widget), item->index);
11038           else
11039             i--;
11040         }
11041       l = l->next;
11042     }
11043 
11044   return NULL;
11045 }
11046 
11047 static gint
11048 exo_icon_view_accessible_get_selection_count (AtkSelection *selection)
11049 {
11050   GtkWidget *widget;
11051   ExoIconView *icon_view;
11052   ExoIconViewItem *item;
11053   GList *l;
11054   gint count;
11055 
11056   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
11057   if (widget == NULL)
11058     return 0;
11059 
11060   icon_view = EXO_ICON_VIEW (widget);
11061 
11062   l = icon_view->priv->items;
11063   count = 0;
11064   while (l)
11065     {
11066       item = l->data;
11067 
11068       if (item->selected)
11069 	count++;
11070 
11071       l = l->next;
11072     }
11073 
11074   return count;
11075 }
11076 
11077 static gboolean
11078 exo_icon_view_accessible_is_child_selected (AtkSelection *selection,
11079                                             gint          i)
11080 {
11081   GtkWidget *widget;
11082   ExoIconView *icon_view;
11083   ExoIconViewItem *item;
11084 
11085   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
11086   if (widget == NULL)
11087     return FALSE;
11088 
11089   icon_view = EXO_ICON_VIEW (widget);
11090 
11091   item = g_list_nth_data (icon_view->priv->items, i);
11092   if (!item)
11093     return FALSE;
11094 
11095   return item->selected;
11096 }
11097 
11098 static gboolean
11099 exo_icon_view_accessible_remove_selection (AtkSelection *selection,
11100                                            gint          i)
11101 {
11102   GtkWidget *widget;
11103   ExoIconView *icon_view;
11104   ExoIconViewItem *item;
11105   GList *l;
11106   gint count;
11107 
11108   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
11109   if (widget == NULL)
11110     return FALSE;
11111 
11112   icon_view = EXO_ICON_VIEW (widget);
11113   l = icon_view->priv->items;
11114   count = 0;
11115   while (l)
11116     {
11117       item = l->data;
11118       if (item->selected)
11119         {
11120           if (count == i)
11121             {
11122               exo_icon_view_unselect_item (icon_view, item);
11123               return TRUE;
11124             }
11125           count++;
11126         }
11127       l = l->next;
11128     }
11129 
11130   return FALSE;
11131 }
11132 
11133 static gboolean
11134 exo_icon_view_accessible_select_all_selection (AtkSelection *selection)
11135 {
11136   GtkWidget *widget;
11137   ExoIconView *icon_view;
11138 
11139   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
11140   if (widget == NULL)
11141     return FALSE;
11142 
11143   icon_view = EXO_ICON_VIEW (widget);
11144   exo_icon_view_select_all (icon_view);
11145   return TRUE;
11146 }
11147 
11148 static void
11149 exo_icon_view_accessible_selection_interface_init (AtkSelectionIface *iface)
11150 {
11151   iface->add_selection = exo_icon_view_accessible_add_selection;
11152   iface->clear_selection = exo_icon_view_accessible_clear_selection;
11153   iface->ref_selection = exo_icon_view_accessible_ref_selection;
11154   iface->get_selection_count = exo_icon_view_accessible_get_selection_count;
11155   iface->is_child_selected = exo_icon_view_accessible_is_child_selected;
11156   iface->remove_selection = exo_icon_view_accessible_remove_selection;
11157   iface->select_all_selection = exo_icon_view_accessible_select_all_selection;
11158 }
11159 
11160 static GType exo_icon_view_accessible_get_type (void)
11161 {
11162   static GType type = 0;
11163 
11164   if (!type)
11165     {
11166       GTypeInfo tinfo =
11167       {
11168         0, /* class size */
11169         (GBaseInitFunc) NULL, /* base init */
11170         (GBaseFinalizeFunc) NULL, /* base finalize */
11171         (GClassInitFunc) exo_icon_view_accessible_class_init,
11172         (GClassFinalizeFunc) NULL, /* class finalize */
11173         NULL, /* class data */
11174         0, /* instance size */
11175         0, /* nb preallocs */
11176         (GInstanceInitFunc) NULL, /* instance init */
11177         NULL /* value table */
11178       };
11179       const GInterfaceInfo atk_component_info =
11180       {
11181         (GInterfaceInitFunc) atk_component_interface_init,
11182         (GInterfaceFinalizeFunc) NULL,
11183         NULL
11184       };
11185       const GInterfaceInfo atk_selection_info =
11186       {
11187         (GInterfaceInitFunc) exo_icon_view_accessible_selection_interface_init,
11188         (GInterfaceFinalizeFunc) NULL,
11189         NULL
11190       };
11191 
11192       /*
11193        * Figure out the size of the class and instance
11194        * we are deriving from
11195        */
11196       AtkObjectFactory *factory;
11197       GType derived_type;
11198       GTypeQuery query;
11199       GType derived_atk_type;
11200 
11201       derived_type = g_type_parent (EXO_TYPE_ICON_VIEW);
11202       factory = atk_registry_get_factory (atk_get_default_registry (),
11203                                           derived_type);
11204       derived_atk_type = atk_object_factory_get_accessible_type (factory);
11205       g_type_query (derived_atk_type, &query);
11206       tinfo.class_size = query.class_size;
11207       tinfo.instance_size = query.instance_size;
11208 
11209       type = g_type_register_static (derived_atk_type,
11210                                      I_("ExoIconViewAccessible"),
11211                                      &tinfo, 0);
11212       g_type_add_interface_static (type, ATK_TYPE_COMPONENT,
11213                                    &atk_component_info);
11214       g_type_add_interface_static (type, ATK_TYPE_SELECTION,
11215                                    &atk_selection_info);
11216     }
11217   return type;
11218 }
11219 
11220 static AtkObject *exo_icon_view_accessible_new (GObject *obj)
11221 {
11222   AtkObject *accessible;
11223 
11224   g_return_val_if_fail (GTK_IS_WIDGET (obj), NULL);
11225 
11226   accessible = g_object_new (exo_icon_view_accessible_get_type (), NULL);
11227   atk_object_initialize (accessible, obj);
11228 
11229   return accessible;
11230 }
11231 
11232 static GType exo_icon_view_accessible_factory_get_accessible_type (void)
11233 {
11234   return exo_icon_view_accessible_get_type ();
11235 }
11236 
11237 static AtkObject* exo_icon_view_accessible_factory_create_accessible (GObject *obj)
11238 {
11239   return exo_icon_view_accessible_new (obj);
11240 }
11241 
11242 static void exo_icon_view_accessible_factory_class_init (AtkObjectFactoryClass *klass)
11243 {
11244   klass->create_accessible = exo_icon_view_accessible_factory_create_accessible;
11245   klass->get_accessible_type = exo_icon_view_accessible_factory_get_accessible_type;
11246 }
11247 
11248 static GType exo_icon_view_accessible_factory_get_type (void)
11249 {
11250   static GType type = 0;
11251 
11252   if (!type)
11253     {
11254       const GTypeInfo tinfo =
11255       {
11256         sizeof (AtkObjectFactoryClass),
11257         NULL,           /* base_init */
11258         NULL,           /* base_finalize */
11259         (GClassInitFunc) exo_icon_view_accessible_factory_class_init,
11260         NULL,           /* class_finalize */
11261         NULL,           /* class_data */
11262         sizeof (AtkObjectFactory),
11263         0,             /* n_preallocs */
11264         NULL, NULL
11265       };
11266 
11267       type = g_type_register_static (ATK_TYPE_OBJECT_FACTORY,
11268                                     I_("ExoIconViewAccessibleFactory"),
11269                                     &tinfo, 0);
11270     }
11271   return type;
11272 }
11273 
11274 static AtkObject *exo_icon_view_get_accessible (GtkWidget *widget)
11275 {
11276   static gboolean first_time = TRUE;
11277 
11278   if (first_time)
11279     {
11280       AtkObjectFactory *factory;
11281       AtkRegistry *registry;
11282       GType derived_type;
11283       GType derived_atk_type;
11284 
11285       /*
11286        * Figure out whether accessibility is enabled by looking at the
11287        * type of the accessible object which would be created for
11288        * the parent type of ExoIconView.
11289        */
11290       derived_type = g_type_parent (EXO_TYPE_ICON_VIEW);
11291 
11292       registry = atk_get_default_registry ();
11293       factory = atk_registry_get_factory (registry,
11294                                           derived_type);
11295       derived_atk_type = atk_object_factory_get_accessible_type (factory);
11296       if (g_type_is_a (derived_atk_type, GTK_TYPE_ACCESSIBLE))
11297         atk_registry_set_factory_type (registry,
11298                                        EXO_TYPE_ICON_VIEW,
11299                                        exo_icon_view_accessible_factory_get_type ());
11300       first_time = FALSE;
11301     }
11302   return GTK_WIDGET_CLASS (exo_icon_view_parent_class)->get_accessible (widget);
11303 }
11304 
11305 /*
11306 #define __EXO_ICON_VIEW_C__
11307 #include <exo/exo-aliasdef.c>
11308 */
11309