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 Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21  * MA 02110-1301 USA
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27 
28 #ifdef HAVE_MATH_H
29 #include <math.h>
30 #endif
31 #ifdef HAVE_MEMORY_H
32 #include <memory.h>
33 #endif
34 #ifdef HAVE_STDLIB_H
35 #include <stdlib.h>
36 #endif
37 #ifdef HAVE_STRING_H
38 #include <string.h>
39 #endif
40 
41 #include <gdk/gdkkeysyms.h>
42 
43 #include <exo/exo-config.h>
44 #include <exo/exo-enum-types.h>
45 #include <exo/exo-icon-view.h>
46 #include <exo/exo-cell-renderer-icon.h>
47 #include <exo/exo-marshal.h>
48 #include <exo/exo-private.h>
49 #include <exo/exo-string.h>
50 #include <exo/exo-alias.h>
51 
52 /**
53  * SECTION: exo-icon-view
54  * @title: ExoIconView
55  * @short_description: A widget which displays a list of icons in a grid
56  * @include: exo/exo.h
57  *
58  * #ExoIconView provides an alternative view on a list model.
59  * It displays the model as a grid of icons with labels. Like
60  * #GtkTreeView, it allows to select one or multiple items
61  * (depending on the selection mode, see exo_icon_view_set_selection_mode()).
62  * In addition to selection with the arrow keys, #ExoIconView supports
63  * rubberband selection, which is controlled by dragging the pointer.
64  **/
65 
66 /* resurrect dead gdk apis for Gtk3
67  * This is easier than using #ifs everywhere
68  */
69 
70 #define GdkRectangle cairo_rectangle_int_t
71 #define GdkRegion    cairo_region_t
72 #define gdk_region_rectangle(rect) cairo_region_create_rectangle (rect)
73 #define gdk_region_destroy(region) cairo_region_destroy (region)
74 #define gdk_region_subtract(dst, other) cairo_region_subtract (dst, other)
75 #define gdk_region_union_with_rect(dst, rect) cairo_region_union_rectangle (dst, rect)
76 
77 #ifdef gdk_cursor_unref
78 #undef gdk_cursor_unref
79 #endif
80 #define gdk_cursor_unref(cursor) g_object_unref (cursor)
81 
82 /* the search dialog timeout (in ms) */
83 #define EXO_ICON_VIEW_SEARCH_DIALOG_TIMEOUT (5000)
84 
85 #define SCROLL_EDGE_SIZE 15
86 
87 
88 
89 /* Property identifiers */
90 enum
91 {
92   PROP_0,
93   PROP_PIXBUF_COLUMN,
94   PROP_ICON_COLUMN,
95   PROP_TEXT_COLUMN,
96   PROP_MARKUP_COLUMN,
97   PROP_SELECTION_MODE,
98   PROP_LAYOUT_MODE,
99   PROP_ORIENTATION,
100   PROP_MODEL,
101   PROP_COLUMNS,
102   PROP_ITEM_WIDTH,
103   PROP_SPACING,
104   PROP_ROW_SPACING,
105   PROP_COLUMN_SPACING,
106   PROP_MARGIN,
107   PROP_REORDERABLE,
108   PROP_SINGLE_CLICK,
109   PROP_SINGLE_CLICK_TIMEOUT,
110   PROP_ENABLE_SEARCH,
111   PROP_SEARCH_COLUMN,
112   PROP_HADJUSTMENT,
113   PROP_VADJUSTMENT,
114   PROP_VSCROLL_POLICY,
115   PROP_HSCROLL_POLICY
116 };
117 
118 /* Signal identifiers */
119 enum
120 {
121   ITEM_ACTIVATED,
122   SELECTION_CHANGED,
123   SELECT_ALL,
124   UNSELECT_ALL,
125   SELECT_CURSOR_ITEM,
126   TOGGLE_CURSOR_ITEM,
127   MOVE_CURSOR,
128   ACTIVATE_CURSOR_ITEM,
129   START_INTERACTIVE_SEARCH,
130   LAST_SIGNAL
131 };
132 
133 /* Icon view flags */
134 typedef enum
135 {
136   EXO_ICON_VIEW_DRAW_KEYFOCUS = (1l << 0),  /* whether to draw keyboard focus */
137   EXO_ICON_VIEW_ITERS_PERSIST = (1l << 1),  /* whether current model provides persistent iterators */
138 } ExoIconViewFlags;
139 
140 #define EXO_ICON_VIEW_SET_FLAG(icon_view, flag)   G_STMT_START{ (EXO_ICON_VIEW (icon_view)->priv->flags |= flag); }G_STMT_END
141 #define EXO_ICON_VIEW_UNSET_FLAG(icon_view, flag) G_STMT_START{ (EXO_ICON_VIEW (icon_view)->priv->flags &= ~(flag)); }G_STMT_END
142 #define EXO_ICON_VIEW_FLAG_SET(icon_view, flag)   ((EXO_ICON_VIEW (icon_view)->priv->flags & (flag)) == (flag))
143 
144 
145 
146 typedef struct _ExoIconViewCellInfo ExoIconViewCellInfo;
147 typedef struct _ExoIconViewChild    ExoIconViewChild;
148 typedef struct _ExoIconViewItem     ExoIconViewItem;
149 
150 
151 
152 #define EXO_ICON_VIEW_CELL_INFO(obj)   ((ExoIconViewCellInfo *) (obj))
153 #define EXO_ICON_VIEW_CHILD(obj)       ((ExoIconViewChild *) (obj))
154 #define EXO_ICON_VIEW_ITEM(obj)        ((ExoIconViewItem *) (obj))
155 
156 
157 
158 static void                 exo_icon_view_cell_layout_init               (GtkCellLayoutIface     *iface);
159 static void                 exo_icon_view_dispose                        (GObject                *object);
160 static void                 exo_icon_view_finalize                       (GObject                *object);
161 static void                 exo_icon_view_get_property                   (GObject                *object,
162                                                                           guint                   prop_id,
163                                                                           GValue                 *value,
164                                                                           GParamSpec             *pspec);
165 static void                 exo_icon_view_set_property                   (GObject                *object,
166                                                                           guint                   prop_id,
167                                                                           const GValue           *value,
168                                                                           GParamSpec             *pspec);
169 static void                 exo_icon_view_realize                        (GtkWidget              *widget);
170 static void                 exo_icon_view_unrealize                      (GtkWidget              *widget);
171 static void                 exo_icon_view_get_preferred_width            (GtkWidget              *widget,
172                                                                           gint                   *minimal_width,
173                                                                           gint                   *natural_width);
174 static void                 exo_icon_view_get_preferred_height           (GtkWidget              *widget,
175                                                                           gint                   *minimal_height,
176                                                                           gint                   *natural_height);
177 static void                 exo_icon_view_size_allocate                  (GtkWidget              *widget,
178                                                                           GtkAllocation          *allocation);
179 static gboolean             exo_icon_view_draw                           (GtkWidget              *widget,
180                                                                           cairo_t                *cr);
181 static gboolean             exo_icon_view_motion_notify_event            (GtkWidget              *widget,
182                                                                           GdkEventMotion         *event);
183 static gboolean             exo_icon_view_button_press_event             (GtkWidget              *widget,
184                                                                           GdkEventButton         *event);
185 static gboolean             exo_icon_view_button_release_event           (GtkWidget              *widget,
186                                                                           GdkEventButton         *event);
187 static gboolean             exo_icon_view_scroll_event                   (GtkWidget              *widget,
188                                                                           GdkEventScroll         *event);
189 static gboolean             exo_icon_view_key_press_event                (GtkWidget              *widget,
190                                                                           GdkEventKey            *event);
191 static gboolean             exo_icon_view_focus_out_event                (GtkWidget              *widget,
192                                                                           GdkEventFocus          *event);
193 static gboolean             exo_icon_view_leave_notify_event             (GtkWidget              *widget,
194                                                                           GdkEventCrossing       *event);
195 static void                 exo_icon_view_remove                         (GtkContainer           *container,
196                                                                           GtkWidget              *widget);
197 static void                 exo_icon_view_forall                         (GtkContainer           *container,
198                                                                           gboolean                include_internals,
199                                                                           GtkCallback             callback,
200                                                                           gpointer                callback_data);
201 static void                 exo_icon_view_set_adjustments                (ExoIconView            *icon_view,
202                                                                           GtkAdjustment          *hadj,
203                                                                           GtkAdjustment          *vadj);
204 static void                 exo_icon_view_real_select_all                (ExoIconView            *icon_view);
205 static void                 exo_icon_view_real_unselect_all              (ExoIconView            *icon_view);
206 static void                 exo_icon_view_real_select_cursor_item        (ExoIconView            *icon_view);
207 static void                 exo_icon_view_real_toggle_cursor_item        (ExoIconView            *icon_view);
208 static gboolean             exo_icon_view_real_activate_cursor_item      (ExoIconView            *icon_view);
209 static gboolean             exo_icon_view_real_start_interactive_search  (ExoIconView            *icon_view);
210 static void                 exo_icon_view_adjustment_changed             (GtkAdjustment          *adjustment,
211                                                                           ExoIconView            *icon_view);
212 static gint                 exo_icon_view_layout_cols                    (ExoIconView            *icon_view,
213                                                                           gint                    item_height,
214                                                                           gint                   *x,
215                                                                           gint                   *maximum_height,
216                                                                           gint                    max_rows);
217 static gint                 exo_icon_view_layout_rows                    (ExoIconView            *icon_view,
218                                                                           gint                    item_width,
219                                                                           gint                   *y,
220                                                                           gint                   *maximum_width,
221                                                                           gint                    max_cols);
222 static void                 exo_icon_view_layout                         (ExoIconView            *icon_view);
223 static void                 exo_icon_view_paint_item                     (ExoIconView            *icon_view,
224                                                                           ExoIconViewItem        *item,
225                                                                           cairo_t                *cr,
226                                                                           gint                    x,
227                                                                           gint                    y,
228                                                                           gboolean                draw_focus);
229 static void                 exo_icon_view_queue_draw_item                (ExoIconView            *icon_view,
230                                                                           ExoIconViewItem        *item);
231 static void                 exo_icon_view_queue_layout                   (ExoIconView            *icon_view);
232 static void                 exo_icon_view_set_cursor_item                (ExoIconView            *icon_view,
233                                                                           ExoIconViewItem        *item,
234                                                                           gint                    cursor_cell);
235 static void                 exo_icon_view_start_rubberbanding            (ExoIconView            *icon_view,
236                                                                           gint                    x,
237                                                                           gint                    y);
238 static void                 exo_icon_view_stop_rubberbanding             (ExoIconView            *icon_view);
239 static void                 exo_icon_view_update_rubberband_selection    (ExoIconView            *icon_view);
240 static gboolean             exo_icon_view_item_hit_test                  (ExoIconView            *icon_view,
241                                                                           ExoIconViewItem        *item,
242                                                                           gint                    x,
243                                                                           gint                    y,
244                                                                           gint                    width,
245                                                                           gint                    height);
246 static gboolean             exo_icon_view_unselect_all_internal          (ExoIconView            *icon_view);
247 static void                 exo_icon_view_calculate_item_size            (ExoIconView            *icon_view,
248                                                                           ExoIconViewItem        *item);
249 static void                 exo_icon_view_calculate_item_size2           (ExoIconView            *icon_view,
250                                                                           ExoIconViewItem        *item,
251                                                                           gint                   *max_width,
252                                                                           gint                   *max_height);
253 static void                 exo_icon_view_update_rubberband              (gpointer                data);
254 static void                 exo_icon_view_invalidate_sizes               (ExoIconView            *icon_view);
255 static void                 exo_icon_view_add_move_binding               (GtkBindingSet          *binding_set,
256                                                                           guint                   keyval,
257                                                                           guint                   modmask,
258                                                                           GtkMovementStep         step,
259                                                                           gint                    count);
260 static gboolean             exo_icon_view_real_move_cursor               (ExoIconView            *icon_view,
261                                                                           GtkMovementStep         step,
262                                                                           gint                    count);
263 static void                 exo_icon_view_move_cursor_up_down            (ExoIconView            *icon_view,
264                                                                           gint                    count);
265 static void                 exo_icon_view_move_cursor_page_up_down       (ExoIconView            *icon_view,
266                                                                           gint                    count);
267 static void                 exo_icon_view_move_cursor_left_right         (ExoIconView            *icon_view,
268                                                                           gint                    count);
269 static void                 exo_icon_view_move_cursor_start_end          (ExoIconView            *icon_view,
270                                                                           gint                    count);
271 static void                 exo_icon_view_scroll_to_item                 (ExoIconView            *icon_view,
272                                                                           ExoIconViewItem        *item);
273 static void                 exo_icon_view_select_item                    (ExoIconView            *icon_view,
274                                                                           ExoIconViewItem        *item);
275 static void                 exo_icon_view_unselect_item                  (ExoIconView            *icon_view,
276                                                                           ExoIconViewItem        *item);
277 static gboolean             exo_icon_view_select_all_between             (ExoIconView            *icon_view,
278                                                                           ExoIconViewItem        *anchor,
279                                                                           ExoIconViewItem        *cursor);
280 static ExoIconViewItem *    exo_icon_view_get_item_at_coords             (const ExoIconView      *icon_view,
281                                                                           gint                    x,
282                                                                           gint                    y,
283                                                                           gboolean                only_in_cell,
284                                                                           ExoIconViewCellInfo   **cell_at_pos);
285 static void                 exo_icon_view_get_cell_area                  (ExoIconView            *icon_view,
286                                                                           ExoIconViewItem        *item,
287                                                                           ExoIconViewCellInfo    *cell_info,
288                                                                           GdkRectangle           *cell_area);
289 static ExoIconViewCellInfo *exo_icon_view_get_cell_info                  (ExoIconView            *icon_view,
290                                                                           GtkCellRenderer        *renderer);
291 static void                 exo_icon_view_set_cell_data                  (const ExoIconView      *icon_view,
292                                                                           ExoIconViewItem        *item);
293 static void                 exo_icon_view_cell_layout_pack_start         (GtkCellLayout          *layout,
294                                                                           GtkCellRenderer        *renderer,
295                                                                           gboolean                expand);
296 static void                 exo_icon_view_cell_layout_pack_end           (GtkCellLayout          *layout,
297                                                                           GtkCellRenderer        *renderer,
298                                                                           gboolean                expand);
299 static void                 exo_icon_view_cell_layout_add_attribute      (GtkCellLayout          *layout,
300                                                                           GtkCellRenderer        *renderer,
301                                                                           const gchar            *attribute,
302                                                                           gint                    column);
303 static void                 exo_icon_view_cell_layout_clear              (GtkCellLayout          *layout);
304 static void                 exo_icon_view_cell_layout_clear_attributes   (GtkCellLayout          *layout,
305                                                                           GtkCellRenderer        *renderer);
306 static void                 exo_icon_view_cell_layout_set_cell_data_func (GtkCellLayout          *layout,
307                                                                           GtkCellRenderer        *cell,
308                                                                           GtkCellLayoutDataFunc   func,
309                                                                           gpointer                func_data,
310                                                                           GDestroyNotify          destroy);
311 static void                 exo_icon_view_cell_layout_reorder            (GtkCellLayout          *layout,
312                                                                           GtkCellRenderer        *cell,
313                                                                           gint                    position);
314 static void                 exo_icon_view_item_activate_cell             (ExoIconView            *icon_view,
315                                                                           ExoIconViewItem        *item,
316                                                                           ExoIconViewCellInfo    *cell_info,
317                                                                           GdkEvent               *event);
318 static void                 exo_icon_view_put                            (ExoIconView            *icon_view,
319                                                                           GtkWidget              *widget,
320                                                                           ExoIconViewItem        *item,
321                                                                           gint                    cell);
322 static void                 exo_icon_view_remove_widget                  (GtkCellEditable        *editable,
323                                                                           ExoIconView            *icon_view);
324 static void                 exo_icon_view_start_editing                  (ExoIconView            *icon_view,
325                                                                           ExoIconViewItem        *item,
326                                                                           ExoIconViewCellInfo    *cell_info,
327                                                                           GdkEvent               *event);
328 static void                 exo_icon_view_stop_editing                   (ExoIconView            *icon_view,
329                                                                           gboolean                cancel_editing);
330 static void                 exo_icon_view_set_pixbuf_column              (ExoIconView            *icon_view,
331                                                                           gint                    column);
332 static void                 exo_icon_view_set_icon_column                (ExoIconView            *icon_view,
333                                                                           gint                    column);
334 
335 static void                 exo_icon_view_get_work_area_dimensions       (GdkWindow              *window,
336                                                                           GdkRectangle           *dimensions);
337 
338 /* Source side drag signals */
339 static void exo_icon_view_drag_begin       (GtkWidget        *widget,
340                                             GdkDragContext   *context);
341 static void exo_icon_view_drag_end         (GtkWidget        *widget,
342                                             GdkDragContext   *context);
343 static void exo_icon_view_drag_data_get    (GtkWidget        *widget,
344                                             GdkDragContext   *context,
345                                             GtkSelectionData *selection_data,
346                                             guint             info,
347                                             guint             drag_time);
348 static void exo_icon_view_drag_data_delete (GtkWidget        *widget,
349                                             GdkDragContext   *context);
350 
351 /* Target side drag signals */
352 static void     exo_icon_view_drag_leave         (GtkWidget        *widget,
353                                                   GdkDragContext   *context,
354                                                   guint             drag_time);
355 static gboolean exo_icon_view_drag_motion        (GtkWidget        *widget,
356                                                   GdkDragContext   *context,
357                                                   gint              x,
358                                                   gint              y,
359                                                   guint             drag_time);
360 static gboolean exo_icon_view_drag_drop          (GtkWidget        *widget,
361                                                   GdkDragContext   *context,
362                                                   gint              x,
363                                                   gint              y,
364                                                   guint             drag_time);
365 static void     exo_icon_view_drag_data_received (GtkWidget        *widget,
366                                                   GdkDragContext   *context,
367                                                   gint              x,
368                                                   gint              y,
369                                                   GtkSelectionData *selection_data,
370                                                   guint             info,
371                                                   guint             drag_time);
372 static gboolean exo_icon_view_maybe_begin_drag   (ExoIconView      *icon_view,
373                                                   GdkEventMotion   *event);
374 
375 static void     remove_scroll_timeout            (ExoIconView *icon_view);
376 
377 /* single-click autoselection support */
378 static gboolean exo_icon_view_single_click_timeout          (gpointer user_data);
379 static void     exo_icon_view_single_click_timeout_destroy  (gpointer user_data);
380 
381 /* Interactive search support */
382 static void     exo_icon_view_search_activate           (GtkEntry       *entry,
383                                                          ExoIconView    *icon_view);
384 static void     exo_icon_view_search_dialog_hide        (GtkWidget      *search_dialog,
385                                                          ExoIconView    *icon_view);
386 static void     exo_icon_view_search_ensure_directory   (ExoIconView    *icon_view);
387 static void     exo_icon_view_search_init               (GtkWidget      *search_entry,
388                                                          ExoIconView    *icon_view);
389 static gboolean exo_icon_view_search_iter               (ExoIconView    *icon_view,
390                                                          GtkTreeModel   *model,
391                                                          GtkTreeIter    *iter,
392                                                          const gchar    *text,
393                                                          gint           *count,
394                                                          gint            n);
395 static void     exo_icon_view_search_move               (GtkWidget      *widget,
396                                                          ExoIconView    *icon_view,
397                                                          gboolean        move_up);
398 static gboolean exo_icon_view_search_start              (ExoIconView    *icon_view,
399                                                          gboolean        keybinding);
400 static gboolean exo_icon_view_search_equal_func         (GtkTreeModel   *model,
401                                                          gint            column,
402                                                          const gchar    *key,
403                                                          GtkTreeIter    *iter,
404                                                          gpointer        user_data);
405 static void     exo_icon_view_search_position_func      (ExoIconView    *icon_view,
406                                                          GtkWidget      *search_dialog,
407                                                          gpointer        user_data);
408 static gboolean exo_icon_view_search_button_press_event (GtkWidget      *widget,
409                                                          GdkEventButton *event,
410                                                          ExoIconView    *icon_view);
411 static gboolean exo_icon_view_search_delete_event       (GtkWidget      *widget,
412                                                          GdkEventAny    *event,
413                                                          ExoIconView    *icon_view);
414 static gboolean exo_icon_view_search_key_press_event    (GtkWidget      *widget,
415                                                          GdkEventKey    *event,
416                                                          ExoIconView    *icon_view);
417 static gboolean exo_icon_view_search_scroll_event       (GtkWidget      *widget,
418                                                          GdkEventScroll *event,
419                                                          ExoIconView    *icon_view);
420 static gboolean exo_icon_view_search_timeout            (gpointer        user_data);
421 static void     exo_icon_view_search_timeout_destroy    (gpointer        user_data);
422 
423 
424 
425 struct _ExoIconViewCellInfo
426 {
427   GtkCellRenderer      *cell;
428   guint                 expand : 1;
429   guint                 pack : 1;
430   guint                 editing : 1;
431   gint                  position;
432   GSList               *attributes;
433   GtkCellLayoutDataFunc func;
434   gpointer              func_data;
435   GDestroyNotify        destroy;
436   gboolean              is_text;
437 };
438 
439 struct _ExoIconViewChild
440 {
441   ExoIconViewItem *item;
442   GtkWidget       *widget;
443   gint             cell;
444 };
445 
446 struct _ExoIconViewItem
447 {
448   GtkTreeIter iter;
449 
450   /* Bounding box (a value of -1 for width indicates
451    * that the item needs to be layouted first)
452    */
453   GdkRectangle area;
454 
455   /* Individual cells.
456    * box[i] is the actual area occupied by cell i,
457    * before, after are used to calculate the cell
458    * area relative to the box.
459    * See exo_icon_view_get_cell_area().
460    */
461   gint n_cells;
462   GdkRectangle *box;
463   gint *before;
464   gint *after;
465 
466   guint row : ((sizeof (guint) / 2) * 8) - 1;
467   guint col : ((sizeof (guint) / 2) * 8) - 1;
468   guint selected : 1;
469   guint selected_before_rubberbanding : 1;
470 };
471 
472 struct _ExoIconViewPrivate
473 {
474   gint width, height;
475   gint rows, cols;
476 
477   GtkSelectionMode selection_mode;
478 
479   ExoIconViewLayoutMode layout_mode;
480 
481   GdkWindow *bin_window;
482 
483   GList *children;
484 
485   GtkTreeModel *model;
486 
487   GList *items;
488 
489   GtkAdjustment *hadjustment;
490   GtkAdjustment *vadjustment;
491 
492   GtkScrollablePolicy hscroll_policy;
493   GtkScrollablePolicy vscroll_policy;
494 
495   guint layout_idle_id;
496 
497   gboolean doing_rubberband;
498   gint rubberband_x_1, rubberband_y_1;
499   gint rubberband_x2, rubberband_y2;
500 
501   guint scroll_timeout_id;
502   gint scroll_value_diff;
503   gint event_last_x, event_last_y;
504 
505   ExoIconViewItem *anchor_item;
506   ExoIconViewItem *cursor_item;
507   ExoIconViewItem *edited_item;
508   GtkCellEditable *editable;
509   ExoIconViewItem *prelit_item;
510 
511   ExoIconViewItem *last_single_clicked;
512 
513   GList *cell_list;
514   gint n_cells;
515 
516   gint cursor_cell;
517 
518   GtkOrientation orientation;
519 
520   gint columns;
521   gint item_width;
522   gint spacing;
523   gint row_spacing;
524   gint column_spacing;
525   gint margin;
526 
527   gint text_column;
528   gint markup_column;
529   gint pixbuf_column;
530   gint icon_column;
531 
532   gint pixbuf_cell;
533   gint text_cell;
534 
535   /* Drag-and-drop. */
536   GdkModifierType start_button_mask;
537   gint pressed_button;
538   gint press_start_x;
539   gint press_start_y;
540 
541   GtkTargetList *source_targets;
542   GdkDragAction source_actions;
543 
544   GtkTargetList *dest_targets;
545   GdkDragAction dest_actions;
546 
547   GtkTreeRowReference *dest_item;
548   ExoIconViewDropPosition dest_pos;
549 
550   /* delayed scrolling */
551   GtkTreeRowReference          *scroll_to_path;
552   gfloat                        scroll_to_row_align;
553   gfloat                        scroll_to_col_align;
554   guint                         scroll_to_use_align : 1;
555 
556   /* misc flags */
557   guint                         source_set : 1;
558   guint                         dest_set : 1;
559   guint                         reorderable : 1;
560   guint                         empty_view_drop :1;
561 
562   guint                         ctrl_pressed : 1;
563   guint                         shift_pressed : 1;
564 
565   /* Single-click support
566    * The single_click_timeout is the timeout after which the
567    * prelited item will be automatically selected in single
568    * click mode (0 to disable).
569    */
570   guint                         single_click : 1;
571   guint                         single_click_timeout;
572   guint                         single_click_timeout_id;
573   guint                         single_click_timeout_state;
574 
575   /* Interactive search support */
576   guint                         enable_search : 1;
577   gint                          search_column;
578   gint                          search_selected_iter;
579   guint                         search_timeout_id;
580   gboolean                      search_disable_popdown;
581   ExoIconViewSearchEqualFunc    search_equal_func;
582   gpointer                      search_equal_data;
583   GDestroyNotify                search_equal_destroy;
584   ExoIconViewSearchPositionFunc search_position_func;
585   gpointer                      search_position_data;
586   GDestroyNotify                search_position_destroy;
587   gint                          search_entry_changed_id;
588   GtkWidget                    *search_entry;
589   GtkWidget                    *search_window;
590 
591   /* ExoIconViewFlags */
592   guint flags;
593 };
594 
595 
596 
597 #include <exo/exo-icon-view-accessible.c>
598 
599 
600 
601 static guint icon_view_signals[LAST_SIGNAL];
602 
603 
604 
G_DEFINE_TYPE_WITH_CODE(ExoIconView,exo_icon_view,GTK_TYPE_CONTAINER,G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,exo_icon_view_cell_layout_init)G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE,NULL)G_ADD_PRIVATE (ExoIconView))605 G_DEFINE_TYPE_WITH_CODE (ExoIconView, exo_icon_view, GTK_TYPE_CONTAINER,
606     G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT, exo_icon_view_cell_layout_init)
607     G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL)
608     G_ADD_PRIVATE (ExoIconView))
609 
610 static AtkObject *
611 exo_icon_view_get_accessible (GtkWidget *widget)
612 {
613   static gboolean initited = FALSE;
614   GType derived_type;
615   AtkObjectFactory *factory;
616   AtkRegistry *registry;
617   GType derived_atk_type;
618 
619   if (!initited)
620     {
621       derived_type = g_type_parent (EXO_TYPE_ICON_VIEW);
622 
623       registry = atk_get_default_registry ();
624       factory = atk_registry_get_factory (registry, derived_type);
625       derived_atk_type = atk_object_factory_get_accessible_type (factory);
626 
627       if (g_type_is_a (derived_atk_type, GTK_TYPE_ACCESSIBLE))
628         {
629           atk_registry_set_factory_type (registry, EXO_TYPE_ICON_VIEW,
630                                          exo_icon_view_accessible_factory_get_type ());
631         }
632 
633       initited = TRUE;
634     }
635 
636   return GTK_WIDGET_CLASS (exo_icon_view_parent_class)->get_accessible (widget);
637 }
638 
639 static void
exo_icon_view_get_work_area_dimensions(GdkWindow * window,GdkRectangle * dimensions)640 exo_icon_view_get_work_area_dimensions (GdkWindow *window, GdkRectangle *dimensions)
641 {
642   GdkDisplay   *display;
643   GdkRectangle  geometry;
644 
645   GdkMonitor   *monitor;
646 
647   display = gdk_window_get_display (window);
648   monitor = gdk_display_get_monitor_at_window (display, window);
649   gdk_monitor_get_workarea (monitor, &geometry);
650 
651   if (dimensions != NULL)
652     {
653        dimensions->x = geometry.x;
654        dimensions->y = geometry.y;
655        dimensions->width = geometry.width;
656        dimensions->height = geometry.height;
657     }
658 }
659 
660 
661 
662 static void
exo_icon_view_class_init(ExoIconViewClass * klass)663 exo_icon_view_class_init (ExoIconViewClass *klass)
664 {
665   GtkContainerClass *gtkcontainer_class;
666   GtkWidgetClass    *gtkwidget_class;
667   GtkBindingSet     *gtkbinding_set;
668   GObjectClass      *gobject_class;
669 
670   gobject_class = G_OBJECT_CLASS (klass);
671   gobject_class->dispose = exo_icon_view_dispose;
672   gobject_class->finalize = exo_icon_view_finalize;
673   gobject_class->set_property = exo_icon_view_set_property;
674   gobject_class->get_property = exo_icon_view_get_property;
675 
676   gtkwidget_class = GTK_WIDGET_CLASS (klass);
677   gtkwidget_class->realize = exo_icon_view_realize;
678   gtkwidget_class->unrealize = exo_icon_view_unrealize;
679   gtkwidget_class->get_preferred_width = exo_icon_view_get_preferred_width;
680   gtkwidget_class->get_preferred_height = exo_icon_view_get_preferred_height;
681   gtkwidget_class->size_allocate = exo_icon_view_size_allocate;
682   gtkwidget_class->get_accessible = exo_icon_view_get_accessible;
683   gtkwidget_class->draw = exo_icon_view_draw;
684   gtkwidget_class->motion_notify_event = exo_icon_view_motion_notify_event;
685   gtkwidget_class->button_press_event = exo_icon_view_button_press_event;
686   gtkwidget_class->button_release_event = exo_icon_view_button_release_event;
687   gtkwidget_class->scroll_event = exo_icon_view_scroll_event;
688   gtkwidget_class->key_press_event = exo_icon_view_key_press_event;
689   gtkwidget_class->focus_out_event = exo_icon_view_focus_out_event;
690   gtkwidget_class->leave_notify_event = exo_icon_view_leave_notify_event;
691   gtkwidget_class->drag_begin = exo_icon_view_drag_begin;
692   gtkwidget_class->drag_end = exo_icon_view_drag_end;
693   gtkwidget_class->drag_data_get = exo_icon_view_drag_data_get;
694   gtkwidget_class->drag_data_delete = exo_icon_view_drag_data_delete;
695   gtkwidget_class->drag_leave = exo_icon_view_drag_leave;
696   gtkwidget_class->drag_motion = exo_icon_view_drag_motion;
697   gtkwidget_class->drag_drop = exo_icon_view_drag_drop;
698   gtkwidget_class->drag_data_received = exo_icon_view_drag_data_received;
699 
700   gtkcontainer_class = GTK_CONTAINER_CLASS (klass);
701   gtkcontainer_class->remove = exo_icon_view_remove;
702   gtkcontainer_class->forall = exo_icon_view_forall;
703 
704   klass->set_scroll_adjustments = exo_icon_view_set_adjustments;
705   klass->select_all = exo_icon_view_real_select_all;
706   klass->unselect_all = exo_icon_view_real_unselect_all;
707   klass->select_cursor_item = exo_icon_view_real_select_cursor_item;
708   klass->toggle_cursor_item = exo_icon_view_real_toggle_cursor_item;
709   klass->move_cursor = exo_icon_view_real_move_cursor;
710   klass->activate_cursor_item = exo_icon_view_real_activate_cursor_item;
711   klass->start_interactive_search = exo_icon_view_real_start_interactive_search;
712 
713   /**
714    * ExoIconView:column-spacing:
715    *
716    * The column-spacing property specifies the space which is inserted between
717    * the columns of the icon view.
718    *
719    * Since: 0.3.1
720    **/
721   g_object_class_install_property (gobject_class,
722                                    PROP_COLUMN_SPACING,
723                                    g_param_spec_int ("column-spacing",
724                                                      _("Column Spacing"),
725                                                      _("Space which is inserted between grid column"),
726                                                      0, G_MAXINT, 6,
727                                                      EXO_PARAM_READWRITE));
728 
729   /**
730    * ExoIconView:columns:
731    *
732    * The columns property contains the number of the columns in which the
733    * items should be displayed. If it is -1, the number of columns will
734    * be chosen automatically to fill the available area.
735    *
736    * Since: 0.3.1
737    **/
738   g_object_class_install_property (gobject_class,
739                                    PROP_COLUMNS,
740                                    g_param_spec_int ("columns",
741                                                      _("Number of columns"),
742                                                      _("Number of columns to display"),
743                                                      -1, G_MAXINT, -1,
744                                                      EXO_PARAM_READWRITE));
745 
746   /**
747    * ExoIconView:enable-search:
748    *
749    * View allows user to search through columns interactively.
750    *
751    * Since: 0.3.1.3
752    **/
753   g_object_class_install_property (gobject_class,
754                                    PROP_ENABLE_SEARCH,
755                                    g_param_spec_boolean ("enable-search",
756                                                          _("Enable Search"),
757                                                          _("View allows user to search through columns interactively"),
758                                                          TRUE,
759                                                          EXO_PARAM_READWRITE));
760 
761 
762   /**
763    * ExoIconView:item-width:
764    *
765    * The item-width property specifies the width to use for each item.
766    * If it is set to -1, the icon view will automatically determine a
767    * suitable item size.
768    *
769    * Since: 0.3.1
770    **/
771   g_object_class_install_property (gobject_class,
772                                    PROP_ITEM_WIDTH,
773                                    g_param_spec_int ("item-width",
774                                                      _("Width for each item"),
775                                                      _("The width used for each item"),
776                                                      -1, G_MAXINT, -1,
777                                                      EXO_PARAM_READWRITE));
778 
779   /**
780    * ExoIconView:layout-mode:
781    *
782    * The layout-mode property specifies the way items are layed out in
783    * the #ExoIconView. This can be either %EXO_ICON_VIEW_LAYOUT_ROWS,
784    * which is the default, where items are layed out horizontally in
785    * rows from top to bottom, or %EXO_ICON_VIEW_LAYOUT_COLS, where items
786    * are layed out vertically in columns from left to right.
787    *
788    * Since: 0.3.1.5
789    **/
790   g_object_class_install_property (gobject_class,
791                                    PROP_LAYOUT_MODE,
792                                    g_param_spec_enum ("layout-mode",
793                                                       _("Layout mode"),
794                                                       _("The layout mode"),
795                                                       EXO_TYPE_ICON_VIEW_LAYOUT_MODE,
796                                                       EXO_ICON_VIEW_LAYOUT_ROWS,
797                                                       EXO_PARAM_READWRITE));
798 
799   /**
800    * ExoIconView:margin:
801    *
802    * The margin property specifies the space which is inserted
803    * at the edges of the icon view.
804    *
805    * Since: 0.3.1
806    **/
807   g_object_class_install_property (gobject_class,
808                                    PROP_MARGIN,
809                                    g_param_spec_int ("margin",
810                                                      _("Margin"),
811                                                      _("Space which is inserted at the edges of the icon view"),
812                                                      0, G_MAXINT, 6,
813                                                      EXO_PARAM_READWRITE));
814 
815   /**
816    * ExoIconView:markup-column:
817    *
818    * The markup-column property contains the number of the model column
819    * containing markup information to be displayed. The markup column must be
820    * of type #G_TYPE_STRING. If this property and the text-column property
821    * are both set to column numbers, it overrides the text column.
822    * If both are set to -1, no texts are displayed.
823    **/
824   g_object_class_install_property (gobject_class,
825                                    PROP_MARKUP_COLUMN,
826                                    g_param_spec_int ("markup-column",
827                                                      _("Markup column"),
828                                                      _("Model column used to retrieve the text if using Pango markup"),
829                                                      -1, G_MAXINT, -1,
830                                                      EXO_PARAM_READWRITE));
831 
832   /**
833    * ExoIconView:model:
834    *
835    * The model property contains the #GtkTreeModel, which should be
836    * display by this icon view. Setting this property to %NULL turns
837    * off the display of anything.
838    **/
839   g_object_class_install_property (gobject_class,
840                                    PROP_MODEL,
841                                    g_param_spec_object ("model",
842                                                         _("Icon View Model"),
843                                                         _("The model for the icon view"),
844                                                         GTK_TYPE_TREE_MODEL,
845                                                         EXO_PARAM_READWRITE));
846 
847   /**
848    * ExoIconView:orientation:
849    *
850    * The orientation property specifies how the cells (i.e. the icon and
851    * the text) of the item are positioned relative to each other.
852    **/
853   g_object_class_install_property (gobject_class,
854                                    PROP_ORIENTATION,
855                                    g_param_spec_enum ("orientation",
856                                                       _("Orientation"),
857                                                       _("How the text and icon of each item are positioned relative to each other"),
858                                                       GTK_TYPE_ORIENTATION,
859                                                       GTK_ORIENTATION_VERTICAL,
860                                                       EXO_PARAM_READWRITE));
861 
862   /**
863    * ExoIconView:pixbuf-column:
864    *
865    * The ::pixbuf-column property contains the number of the model column
866    * containing the pixbufs which are displayed. The pixbuf column must be
867    * of type #GDK_TYPE_PIXBUF. Setting this property to -1 turns off the
868    * display of pixbufs.
869    **/
870   g_object_class_install_property (gobject_class,
871                                    PROP_PIXBUF_COLUMN,
872                                    g_param_spec_int ("pixbuf-column",
873                                                      _("Pixbuf column"),
874                                                      _("Model column used to retrieve the icon pixbuf from"),
875                                                      -1, G_MAXINT, -1,
876                                                      EXO_PARAM_READWRITE));
877 
878   /**
879    * ExoIconView:icon-column:
880    *
881    * The ::icon-column property contains the number of the model column
882    * containing an absolute path to an image file to render. The icon column
883    * must be of type #G_TYPE_STRING. Setting this property to -1 turns off
884    * the display of icons.
885    *
886    * Since: 0.10.2
887    **/
888   g_object_class_install_property (gobject_class,
889                                    PROP_ICON_COLUMN,
890                                    g_param_spec_int ("icon-column",
891                                                      _("Icon column"),
892                                                      _("Model column used to retrieve the absolute path of an image file to render"),
893                                                      -1, G_MAXINT, -1,
894                                                      EXO_PARAM_READWRITE));
895 
896   /**
897    * ExoIconView:reorderable:
898    *
899    * The reorderable property specifies if the items can be reordered
900    * by Drag and Drop.
901    *
902    * Since: 0.3.1
903    **/
904   g_object_class_install_property (gobject_class,
905                                    PROP_REORDERABLE,
906                                    g_param_spec_boolean ("reorderable",
907                                                          _("Reorderable"),
908                                                          _("View is reorderable"),
909                                                          FALSE,
910                                                          EXO_PARAM_READWRITE));
911 
912   /**
913    * ExoIconView:row-spacing:
914    *
915    * The row-spacing property specifies the space which is inserted between
916    * the rows of the icon view.
917    *
918    * Since: 0.3.1
919    **/
920   g_object_class_install_property (gobject_class,
921                                    PROP_ROW_SPACING,
922                                    g_param_spec_int ("row-spacing",
923                                                      _("Row Spacing"),
924                                                      _("Space which is inserted between grid rows"),
925                                                      0, G_MAXINT, 6,
926                                                      EXO_PARAM_READWRITE));
927 
928   /**
929    * ExoIconView:search-column:
930    *
931    * Model column to search through when searching through code.
932    *
933    * Since: 0.3.1.3
934    **/
935   g_object_class_install_property (gobject_class,
936                                    PROP_SEARCH_COLUMN,
937                                    g_param_spec_int ("search-column",
938                                                      _("Search Column"),
939                                                      _("Model column to search through when searching through item"),
940                                                      -1, G_MAXINT, -1,
941                                                      EXO_PARAM_READWRITE));
942 
943   /**
944    * ExoIconView:selection-mode:
945    *
946    * The selection-mode property specifies the selection mode of
947    * icon view. If the mode is #GTK_SELECTION_MULTIPLE, rubberband selection
948    * is enabled, for the other modes, only keyboard selection is possible.
949    **/
950   g_object_class_install_property (gobject_class,
951                                    PROP_SELECTION_MODE,
952                                    g_param_spec_enum ("selection-mode",
953                                                       _("Selection mode"),
954                                                       _("The selection mode"),
955                                                       GTK_TYPE_SELECTION_MODE,
956                                                       GTK_SELECTION_SINGLE,
957                                                       EXO_PARAM_READWRITE));
958 
959   /**
960    * ExoIconView:single-click:
961    *
962    * Determines whether items can be activated by single or double clicks.
963    *
964    * Since: 0.3.1.3
965    **/
966   g_object_class_install_property (gobject_class,
967                                    PROP_SINGLE_CLICK,
968                                    g_param_spec_boolean ("single-click",
969                                                          _("Single Click"),
970                                                          _("Whether the items in the view can be activated with single clicks"),
971                                                          FALSE,
972                                                          EXO_PARAM_READWRITE));
973 
974   /**
975    * ExoIconView:single-click-timeout:
976    *
977    * The amount of time in milliseconds after which a prelited item (an item
978    * which is hovered by the mouse cursor) will be selected automatically in
979    * single click mode. A value of %0 disables the automatic selection.
980    *
981    * Since: 0.3.1.5
982    **/
983   g_object_class_install_property (gobject_class,
984                                    PROP_SINGLE_CLICK_TIMEOUT,
985                                    g_param_spec_uint ("single-click-timeout",
986                                                       _("Single Click Timeout"),
987                                                       _("The amount of time after which the item under the mouse cursor will be selected automatically in single click mode"),
988                                                       0, G_MAXUINT, 0,
989                                                       EXO_PARAM_READWRITE));
990 
991   /**
992    * ExoIconView:spacing:
993    *
994    * The spacing property specifies the space which is inserted between
995    * the cells (i.e. the icon and the text) of an item.
996    *
997    * Since: 0.3.1
998    **/
999   g_object_class_install_property (gobject_class,
1000                                    PROP_SPACING,
1001                                    g_param_spec_int ("spacing",
1002                                                      _("Spacing"),
1003                                                      _("Space which is inserted between cells of an item"),
1004                                                      0, G_MAXINT, 0,
1005                                                      EXO_PARAM_READWRITE));
1006 
1007   /**
1008    * ExoIconView:text-column:
1009    *
1010    * The text-column property contains the number of the model column
1011    * containing the texts which are displayed. The text column must be
1012    * of type #G_TYPE_STRING. If this property and the markup-column
1013    * property are both set to -1, no texts are displayed.
1014    **/
1015   g_object_class_install_property (gobject_class,
1016                                    PROP_TEXT_COLUMN,
1017                                    g_param_spec_int ("text-column",
1018                                                      _("Text column"),
1019                                                      _("Model column used to retrieve the text from"),
1020                                                      -1, G_MAXINT, -1,
1021                                                      EXO_PARAM_READWRITE));
1022 
1023   g_object_class_override_property (gobject_class, PROP_HADJUSTMENT, "hadjustment");
1024   g_object_class_override_property (gobject_class, PROP_VADJUSTMENT, "vadjustment");
1025   g_object_class_override_property (gobject_class, PROP_HSCROLL_POLICY, "hscroll-policy");
1026   g_object_class_override_property (gobject_class, PROP_VSCROLL_POLICY, "vscroll-policy");
1027 
1028   /**
1029    * ExoIconView::item-activated:
1030    * @icon_view : a #ExoIconView.
1031    * @path      : the #GtkTreePath of the activated item.
1032    *
1033    * The ::item-activated signal is emitted when the method
1034    * exo_icon_view_item_activated() is called, when the user double clicks
1035    * an item with the "activate-on-single-click" property set to %FALSE, or
1036    * when the user single clicks an item when the "activate-on-single-click"
1037    * property set to %TRUE. It is also emitted when a non-editable item is
1038    * selected and one of the keys: Space, Return or Enter is pressed.
1039    **/
1040   icon_view_signals[ITEM_ACTIVATED] =
1041     g_signal_new (I_("item-activated"),
1042                   G_TYPE_FROM_CLASS (gobject_class),
1043                   G_SIGNAL_RUN_LAST,
1044                   G_STRUCT_OFFSET (ExoIconViewClass, item_activated),
1045                   NULL, NULL,
1046                   g_cclosure_marshal_VOID__BOXED,
1047                   G_TYPE_NONE, 1,
1048                   GTK_TYPE_TREE_PATH);
1049 
1050   /**
1051    * ExoIconView::selection-changed:
1052    * @icon_view : a #ExoIconView.
1053    *
1054    * The ::selection-changed signal is emitted when the selection
1055    * (i.e. the set of selected items) changes.
1056    **/
1057   icon_view_signals[SELECTION_CHANGED] =
1058     g_signal_new (I_("selection-changed"),
1059                   G_TYPE_FROM_CLASS (gobject_class),
1060                   G_SIGNAL_RUN_FIRST,
1061                   G_STRUCT_OFFSET (ExoIconViewClass, selection_changed),
1062                   NULL, NULL,
1063                   g_cclosure_marshal_VOID__VOID,
1064                   G_TYPE_NONE, 0);
1065 
1066   /**
1067    * ExoIconView::select-all:
1068    * @icon_view : a #ExoIconView.
1069    *
1070    * A #GtkBindingSignal which gets emitted when the user selects all items.
1071    *
1072    * Applications should not connect to it, but may emit it with
1073    * g_signal_emit_by_name() if they need to control selection
1074    * programmatically.
1075    *
1076    * The default binding for this signal is Ctrl-a.
1077    **/
1078   icon_view_signals[SELECT_ALL] =
1079     g_signal_new (I_("select-all"),
1080                   G_TYPE_FROM_CLASS (gobject_class),
1081                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1082                   G_STRUCT_OFFSET (ExoIconViewClass, select_all),
1083                   NULL, NULL,
1084                   g_cclosure_marshal_VOID__VOID,
1085                   G_TYPE_NONE, 0);
1086 
1087   /**
1088    * ExoIconView::unselect-all:
1089    * @icon_view : a #ExoIconView.
1090    *
1091    * A #GtkBindingSignal which gets emitted when the user unselects all items.
1092    *
1093    * Applications should not connect to it, but may emit it with
1094    * g_signal_emit_by_name() if they need to control selection
1095    * programmatically.
1096    *
1097    * The default binding for this signal is Ctrl-Shift-a.
1098    **/
1099   icon_view_signals[UNSELECT_ALL] =
1100     g_signal_new (I_("unselect-all"),
1101                   G_TYPE_FROM_CLASS (gobject_class),
1102                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1103                   G_STRUCT_OFFSET (ExoIconViewClass, unselect_all),
1104                   NULL, NULL,
1105                   g_cclosure_marshal_VOID__VOID,
1106                   G_TYPE_NONE, 0);
1107 
1108   /**
1109    * ExoIconView::select-cursor-item:
1110    * @icon_view : a #ExoIconView.
1111    *
1112    * A #GtkBindingSignal which gets emitted when the user selects the item
1113    * that is currently focused.
1114    *
1115    * Applications should not connect to it, but may emit it with
1116    * g_signal_emit_by_name() if they need to control selection
1117    * programmatically.
1118    *
1119    * There is no default binding for this signal.
1120    **/
1121   icon_view_signals[SELECT_CURSOR_ITEM] =
1122     g_signal_new (I_("select-cursor-item"),
1123                   G_TYPE_FROM_CLASS (gobject_class),
1124                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1125                   G_STRUCT_OFFSET (ExoIconViewClass, select_cursor_item),
1126                   NULL, NULL,
1127                   g_cclosure_marshal_VOID__VOID,
1128                   G_TYPE_NONE, 0);
1129 
1130   /**
1131    * ExoIconView::toggle-cursor-item:
1132    * @icon_view : a #ExoIconView.
1133    *
1134    * A #GtkBindingSignal which gets emitted when the user toggles whether
1135    * the currently focused item is selected or not. The exact effect of
1136    * this depend on the selection mode.
1137    *
1138    * Applications should not connect to it, but may emit it with
1139    * g_signal_emit_by_name() if they need to control selection
1140    * programmatically.
1141    *
1142    * There is no default binding for this signal is Ctrl-Space.
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    * A #GtkBindingSignal which gets emitted when the user activates the
1158    * currently focused item.
1159    *
1160    * Applications should not connect to it, but may emit it with
1161    * g_signal_emit_by_name() if they need to control activation
1162    * programmatically.
1163    *
1164    * The default bindings for this signal are Space, Return and Enter.
1165    **/
1166   icon_view_signals[ACTIVATE_CURSOR_ITEM] =
1167     g_signal_new (I_("activate-cursor-item"),
1168                   G_TYPE_FROM_CLASS (gobject_class),
1169                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1170                   G_STRUCT_OFFSET (ExoIconViewClass, activate_cursor_item),
1171                   NULL, NULL,
1172                   _exo_marshal_BOOLEAN__VOID,
1173                   G_TYPE_BOOLEAN, 0);
1174 
1175   /**
1176    * ExoIconView::start-interactive-search:
1177    * @icon_view : a #ExoIconView.
1178    *
1179    * The ::start-interative-search signal is emitted when the user starts
1180    * typing to jump to an item in the icon view.
1181    **/
1182   icon_view_signals[START_INTERACTIVE_SEARCH] =
1183     g_signal_new (I_("start-interactive-search"),
1184                   G_TYPE_FROM_CLASS (gobject_class),
1185                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1186                   G_STRUCT_OFFSET (ExoIconViewClass, start_interactive_search),
1187                   NULL, NULL,
1188                   _exo_marshal_BOOLEAN__VOID,
1189                   G_TYPE_BOOLEAN, 0);
1190 
1191   /**
1192    * ExoIconView::move-cursor:
1193    * @icon_view : a #ExoIconView.
1194    * @step      : the granularity of the move, as a #GtkMovementStep
1195    * @count     : the number of @step units to move
1196    *
1197    * The ::move-cursor signal is a keybinding signal which gets emitted when
1198    * the user initiates a cursor movement.
1199    *
1200    * Applications should not connect to it, but may emit it with
1201    * g_signal_emit_by_name() if they need to control the cursor
1202    * programmatically.
1203    *
1204    * The default bindings for this signal include
1205    * * Arrow keys which move by individual steps
1206    * * Home/End keys which move to the first/last item
1207    * * PageUp/PageDown which move by "pages" All of these will extend the
1208    * selection when combined with the Shift modifier.
1209    **/
1210   icon_view_signals[MOVE_CURSOR] =
1211     g_signal_new (I_("move-cursor"),
1212                   G_TYPE_FROM_CLASS (gobject_class),
1213                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1214                   G_STRUCT_OFFSET (ExoIconViewClass, move_cursor),
1215                   NULL, NULL,
1216                   _exo_marshal_BOOLEAN__ENUM_INT,
1217                   G_TYPE_BOOLEAN, 2,
1218                   GTK_TYPE_MOVEMENT_STEP,
1219                   G_TYPE_INT);
1220 
1221   /* Key bindings */
1222   gtkbinding_set = gtk_binding_set_by_class (klass);
1223   gtk_binding_entry_add_signal (gtkbinding_set, GDK_KEY_a, GDK_CONTROL_MASK, "select-all", 0);
1224   gtk_binding_entry_add_signal (gtkbinding_set, GDK_KEY_a, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "unselect-all", 0);
1225   gtk_binding_entry_add_signal (gtkbinding_set, GDK_KEY_space, GDK_CONTROL_MASK, "toggle-cursor-item", 0);
1226   gtk_binding_entry_add_signal (gtkbinding_set, GDK_KEY_space, 0, "activate-cursor-item", 0);
1227   gtk_binding_entry_add_signal (gtkbinding_set, GDK_KEY_Return, 0, "activate-cursor-item", 0);
1228   gtk_binding_entry_add_signal (gtkbinding_set, GDK_KEY_ISO_Enter, 0, "activate-cursor-item", 0);
1229   gtk_binding_entry_add_signal (gtkbinding_set, GDK_KEY_KP_Enter, 0, "activate-cursor-item", 0);
1230   gtk_binding_entry_add_signal (gtkbinding_set, GDK_KEY_f, GDK_CONTROL_MASK, "start-interactive-search", 0);
1231   gtk_binding_entry_add_signal (gtkbinding_set, GDK_KEY_F, GDK_CONTROL_MASK, "start-interactive-search", 0);
1232 
1233   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_Up, 0, GTK_MOVEMENT_DISPLAY_LINES, -1);
1234   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_KP_Up, 0, GTK_MOVEMENT_DISPLAY_LINES, -1);
1235   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_Down, 0, GTK_MOVEMENT_DISPLAY_LINES, 1);
1236   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_KP_Down, 0, GTK_MOVEMENT_DISPLAY_LINES, 1);
1237   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_p, GDK_CONTROL_MASK, GTK_MOVEMENT_DISPLAY_LINES, -1);
1238   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_n, GDK_CONTROL_MASK, GTK_MOVEMENT_DISPLAY_LINES, 1);
1239   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_Home, 0, GTK_MOVEMENT_BUFFER_ENDS, -1);
1240   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_KP_Home, 0, GTK_MOVEMENT_BUFFER_ENDS, -1);
1241   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_End, 0, GTK_MOVEMENT_BUFFER_ENDS, 1);
1242   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_KP_End, 0, GTK_MOVEMENT_BUFFER_ENDS, 1);
1243   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_Page_Up, 0, GTK_MOVEMENT_PAGES, -1);
1244   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_KP_Page_Up, 0, GTK_MOVEMENT_PAGES, -1);
1245   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_Page_Down, 0, GTK_MOVEMENT_PAGES, 1);
1246   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_KP_Page_Down, 0, GTK_MOVEMENT_PAGES, 1);
1247   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_Right, 0, GTK_MOVEMENT_VISUAL_POSITIONS, 1);
1248   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_Left, 0, GTK_MOVEMENT_VISUAL_POSITIONS, -1);
1249   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_KP_Right, 0, GTK_MOVEMENT_VISUAL_POSITIONS, 1);
1250   exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_KP_Left, 0, GTK_MOVEMENT_VISUAL_POSITIONS, -1);
1251 }
1252 
1253 
1254 
1255 static void
exo_icon_view_cell_layout_init(GtkCellLayoutIface * iface)1256 exo_icon_view_cell_layout_init (GtkCellLayoutIface *iface)
1257 {
1258   iface->pack_start = exo_icon_view_cell_layout_pack_start;
1259   iface->pack_end = exo_icon_view_cell_layout_pack_end;
1260   iface->clear = exo_icon_view_cell_layout_clear;
1261   iface->add_attribute = exo_icon_view_cell_layout_add_attribute;
1262   iface->set_cell_data_func = exo_icon_view_cell_layout_set_cell_data_func;
1263   iface->clear_attributes = exo_icon_view_cell_layout_clear_attributes;
1264   iface->reorder = exo_icon_view_cell_layout_reorder;
1265 }
1266 
1267 
1268 
1269 static void
exo_icon_view_init(ExoIconView * icon_view)1270 exo_icon_view_init (ExoIconView *icon_view)
1271 {
1272   gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (icon_view)),
1273                                GTK_STYLE_CLASS_VIEW);
1274 
1275   icon_view->priv = exo_icon_view_get_instance_private (icon_view);
1276 
1277   icon_view->priv->selection_mode = GTK_SELECTION_SINGLE;
1278   icon_view->priv->pressed_button = -1;
1279   icon_view->priv->press_start_x = -1;
1280   icon_view->priv->press_start_y = -1;
1281   icon_view->priv->text_column = -1;
1282   icon_view->priv->markup_column = -1;
1283   icon_view->priv->pixbuf_column = -1;
1284   icon_view->priv->icon_column = -1;
1285   icon_view->priv->text_cell = -1;
1286   icon_view->priv->pixbuf_cell = -1;
1287 
1288   gtk_widget_set_can_focus (GTK_WIDGET (icon_view), TRUE);
1289 
1290   exo_icon_view_set_adjustments (icon_view, NULL, NULL);
1291 
1292   icon_view->priv->cursor_cell = -1;
1293 
1294   icon_view->priv->orientation = GTK_ORIENTATION_VERTICAL;
1295 
1296   icon_view->priv->columns = -1;
1297   icon_view->priv->item_width = -1;
1298   icon_view->priv->row_spacing = 6;
1299   icon_view->priv->column_spacing = 6;
1300   icon_view->priv->margin = 6;
1301 
1302   icon_view->priv->enable_search = TRUE;
1303   icon_view->priv->search_column = -1;
1304   icon_view->priv->search_equal_func = exo_icon_view_search_equal_func;
1305   icon_view->priv->search_position_func = exo_icon_view_search_position_func;
1306 
1307   icon_view->priv->flags = EXO_ICON_VIEW_DRAW_KEYFOCUS;
1308 }
1309 
1310 
1311 
1312 static void
exo_icon_view_dispose(GObject * object)1313 exo_icon_view_dispose (GObject *object)
1314 {
1315   ExoIconView *icon_view = EXO_ICON_VIEW (object);
1316 
1317   /* cancel any pending search timeout */
1318   if (G_UNLIKELY (icon_view->priv->search_timeout_id != 0))
1319     g_source_remove (icon_view->priv->search_timeout_id);
1320 
1321   /* destroy the interactive search dialog */
1322   if (G_UNLIKELY (icon_view->priv->search_window != NULL))
1323     {
1324       gtk_widget_destroy (icon_view->priv->search_window);
1325       icon_view->priv->search_entry = NULL;
1326       icon_view->priv->search_window = NULL;
1327     }
1328 
1329   /* drop search equal and position functions (if any) */
1330   exo_icon_view_set_search_equal_func (icon_view, NULL, NULL, NULL);
1331   exo_icon_view_set_search_position_func (icon_view, NULL, NULL, NULL);
1332 
1333   /* reset the drag dest item */
1334   exo_icon_view_set_drag_dest_item (icon_view, NULL, EXO_ICON_VIEW_NO_DROP);
1335 
1336   /* drop the scroll to path (if any) */
1337   if (G_UNLIKELY (icon_view->priv->scroll_to_path != NULL))
1338     {
1339       gtk_tree_row_reference_free (icon_view->priv->scroll_to_path);
1340       icon_view->priv->scroll_to_path = NULL;
1341     }
1342 
1343   /* reset the model (also stops any active editing) */
1344   exo_icon_view_set_model (icon_view, NULL);
1345 
1346   /* drop the scroll timer */
1347   remove_scroll_timeout (icon_view);
1348 
1349   (*G_OBJECT_CLASS (exo_icon_view_parent_class)->dispose) (object);
1350 }
1351 
1352 
1353 
1354 static void
exo_icon_view_finalize(GObject * object)1355 exo_icon_view_finalize (GObject *object)
1356 {
1357   ExoIconView *icon_view = EXO_ICON_VIEW (object);
1358 
1359   /* drop the scroll adjustments */
1360   g_object_unref (G_OBJECT (icon_view->priv->hadjustment));
1361   g_object_unref (G_OBJECT (icon_view->priv->vadjustment));
1362 
1363   /* drop the cell renderers */
1364   exo_icon_view_cell_layout_clear (GTK_CELL_LAYOUT (icon_view));
1365 
1366   /* be sure to cancel the single click timeout */
1367   if (G_UNLIKELY (icon_view->priv->single_click_timeout_id != 0))
1368     g_source_remove (icon_view->priv->single_click_timeout_id);
1369 
1370   /* kill the layout idle source (it's important to have this last!) */
1371   if (G_UNLIKELY (icon_view->priv->layout_idle_id != 0))
1372     g_source_remove (icon_view->priv->layout_idle_id);
1373 
1374   (*G_OBJECT_CLASS (exo_icon_view_parent_class)->finalize) (object);
1375 }
1376 
1377 
1378 static void
exo_icon_view_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1379 exo_icon_view_get_property (GObject      *object,
1380                             guint         prop_id,
1381                             GValue       *value,
1382                             GParamSpec   *pspec)
1383 {
1384   const ExoIconViewPrivate *priv = EXO_ICON_VIEW (object)->priv;
1385 
1386   switch (prop_id)
1387     {
1388     case PROP_COLUMN_SPACING:
1389       g_value_set_int (value, priv->column_spacing);
1390       break;
1391 
1392     case PROP_COLUMNS:
1393       g_value_set_int (value, priv->columns);
1394       break;
1395 
1396     case PROP_ENABLE_SEARCH:
1397       g_value_set_boolean (value, priv->enable_search);
1398       break;
1399 
1400     case PROP_ITEM_WIDTH:
1401       g_value_set_int (value, priv->item_width);
1402       break;
1403 
1404     case PROP_MARGIN:
1405       g_value_set_int (value, priv->margin);
1406       break;
1407 
1408     case PROP_MARKUP_COLUMN:
1409       g_value_set_int (value, priv->markup_column);
1410       break;
1411 
1412     case PROP_MODEL:
1413       g_value_set_object (value, priv->model);
1414       break;
1415 
1416     case PROP_ORIENTATION:
1417       g_value_set_enum (value, priv->orientation);
1418       break;
1419 
1420     case PROP_PIXBUF_COLUMN:
1421       g_value_set_int (value, priv->pixbuf_column);
1422       break;
1423 
1424     case PROP_ICON_COLUMN:
1425       g_value_set_int (value, priv->icon_column);
1426       break;
1427 
1428     case PROP_REORDERABLE:
1429       g_value_set_boolean (value, priv->reorderable);
1430       break;
1431 
1432     case PROP_ROW_SPACING:
1433       g_value_set_int (value, priv->row_spacing);
1434       break;
1435 
1436     case PROP_SEARCH_COLUMN:
1437       g_value_set_int (value, priv->search_column);
1438       break;
1439 
1440     case PROP_SELECTION_MODE:
1441       g_value_set_enum (value, priv->selection_mode);
1442       break;
1443 
1444     case PROP_SINGLE_CLICK:
1445       g_value_set_boolean (value, priv->single_click);
1446       break;
1447 
1448     case PROP_SINGLE_CLICK_TIMEOUT:
1449       g_value_set_uint (value, priv->single_click_timeout);
1450       break;
1451 
1452     case PROP_SPACING:
1453       g_value_set_int (value, priv->spacing);
1454       break;
1455 
1456     case PROP_TEXT_COLUMN:
1457       g_value_set_int (value, priv->text_column);
1458       break;
1459 
1460     case PROP_LAYOUT_MODE:
1461       g_value_set_enum (value, priv->layout_mode);
1462       break;
1463 
1464     case PROP_HADJUSTMENT:
1465       g_value_set_object (value, priv->hadjustment);
1466       break;
1467 
1468     case PROP_VADJUSTMENT:
1469       g_value_set_object (value, priv->vadjustment);
1470       break;
1471 
1472     case PROP_HSCROLL_POLICY:
1473       g_value_set_enum (value, priv->hscroll_policy);
1474       break;
1475 
1476     case PROP_VSCROLL_POLICY:
1477       g_value_set_enum (value, priv->vscroll_policy);
1478       break;
1479 
1480     default:
1481       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1482       break;
1483     }
1484 }
1485 
1486 
1487 
1488 static void
exo_icon_view_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1489 exo_icon_view_set_property (GObject      *object,
1490                             guint         prop_id,
1491                             const GValue *value,
1492                             GParamSpec   *pspec)
1493 {
1494   ExoIconView *icon_view = EXO_ICON_VIEW (object);
1495 
1496   switch (prop_id)
1497     {
1498     case PROP_COLUMN_SPACING:
1499       exo_icon_view_set_column_spacing (icon_view, g_value_get_int (value));
1500       break;
1501 
1502     case PROP_COLUMNS:
1503       exo_icon_view_set_columns (icon_view, g_value_get_int (value));
1504       break;
1505 
1506     case PROP_ENABLE_SEARCH:
1507       exo_icon_view_set_enable_search (icon_view, g_value_get_boolean (value));
1508       break;
1509 
1510     case PROP_ITEM_WIDTH:
1511       exo_icon_view_set_item_width (icon_view, g_value_get_int (value));
1512       break;
1513 
1514     case PROP_MARGIN:
1515       exo_icon_view_set_margin (icon_view, g_value_get_int (value));
1516       break;
1517 
1518     case PROP_MODEL:
1519       exo_icon_view_set_model (icon_view, g_value_get_object (value));
1520       break;
1521 
1522     case PROP_ORIENTATION:
1523       exo_icon_view_set_orientation (icon_view, g_value_get_enum (value));
1524       break;
1525 
1526     case PROP_PIXBUF_COLUMN:
1527       exo_icon_view_set_pixbuf_column (icon_view, g_value_get_int (value));
1528       break;
1529 
1530     case PROP_ICON_COLUMN:
1531       exo_icon_view_set_icon_column (icon_view, g_value_get_int (value));
1532       break;
1533 
1534     case PROP_REORDERABLE:
1535       exo_icon_view_set_reorderable (icon_view, g_value_get_boolean (value));
1536       break;
1537 
1538     case PROP_ROW_SPACING:
1539       exo_icon_view_set_row_spacing (icon_view, g_value_get_int (value));
1540       break;
1541 
1542     case PROP_SEARCH_COLUMN:
1543       exo_icon_view_set_search_column (icon_view, g_value_get_int (value));
1544       break;
1545 
1546     case PROP_SELECTION_MODE:
1547       exo_icon_view_set_selection_mode (icon_view, g_value_get_enum (value));
1548       break;
1549 
1550     case PROP_SINGLE_CLICK:
1551       exo_icon_view_set_single_click (icon_view, g_value_get_boolean (value));
1552       break;
1553 
1554     case PROP_SINGLE_CLICK_TIMEOUT:
1555       exo_icon_view_set_single_click_timeout (icon_view, g_value_get_uint (value));
1556       break;
1557 
1558     case PROP_SPACING:
1559       exo_icon_view_set_spacing (icon_view, g_value_get_int (value));
1560       break;
1561 
1562     case PROP_LAYOUT_MODE:
1563       exo_icon_view_set_layout_mode (icon_view, g_value_get_enum (value));
1564       break;
1565 
1566     case PROP_HADJUSTMENT:
1567       exo_icon_view_set_adjustments (icon_view, g_value_get_object (value), icon_view->priv->vadjustment);
1568       break;
1569 
1570     case PROP_VADJUSTMENT:
1571       exo_icon_view_set_adjustments (icon_view, icon_view->priv->hadjustment, g_value_get_object (value));
1572       break;
1573 
1574     case PROP_HSCROLL_POLICY:
1575       if (icon_view->priv->hscroll_policy != (GtkScrollablePolicy)g_value_get_enum (value))
1576         {
1577           icon_view->priv->hscroll_policy = g_value_get_enum (value);
1578           gtk_widget_queue_resize (GTK_WIDGET (icon_view));
1579           g_object_notify_by_pspec (object, pspec);
1580         }
1581       break;
1582 
1583     case PROP_VSCROLL_POLICY:
1584       if (icon_view->priv->vscroll_policy != (GtkScrollablePolicy)g_value_get_enum (value))
1585         {
1586           icon_view->priv->vscroll_policy = g_value_get_enum (value);
1587           gtk_widget_queue_resize (GTK_WIDGET (icon_view));
1588           g_object_notify_by_pspec (object, pspec);
1589         }
1590       break;
1591 
1592     default:
1593       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1594       break;
1595     }
1596 }
1597 
1598 
1599 
1600 static void
exo_icon_view_realize(GtkWidget * widget)1601 exo_icon_view_realize (GtkWidget *widget)
1602 {
1603   ExoIconViewPrivate *priv = EXO_ICON_VIEW (widget)->priv;
1604   GdkWindowAttr       attributes;
1605   gint                attributes_mask;
1606   GtkAllocation       allocation;
1607 
1608   gtk_widget_set_realized (widget, TRUE);
1609   gtk_widget_get_allocation (widget, &allocation);
1610 
1611   /* Allocate the clipping window */
1612   attributes.window_type = GDK_WINDOW_CHILD;
1613   attributes.x = allocation.x;
1614   attributes.y = allocation.y;
1615   attributes.width = allocation.width;
1616   attributes.height = allocation.height;
1617   attributes.wclass = GDK_INPUT_OUTPUT;
1618   attributes.visual = gtk_widget_get_visual (widget);
1619 #define GDK_WA_COLORMAP 0
1620   attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
1621   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1622   gtk_widget_set_window (widget, gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask));
1623   gdk_window_set_user_data (gtk_widget_get_window (widget), widget);
1624 
1625   /* Allocate the icons window */
1626   attributes.x = 0;
1627   attributes.y = 0;
1628   attributes.width = MAX (priv->width, allocation.width);
1629   attributes.height = MAX (priv->height, allocation.height);
1630   attributes.event_mask = GDK_EXPOSURE_MASK
1631                         | GDK_SCROLL_MASK
1632                         | GDK_SMOOTH_SCROLL_MASK
1633                         | GDK_POINTER_MOTION_MASK
1634                         | GDK_LEAVE_NOTIFY_MASK
1635                         | GDK_BUTTON_PRESS_MASK
1636                         | GDK_BUTTON_RELEASE_MASK
1637                         | GDK_KEY_PRESS_MASK
1638                         | GDK_KEY_RELEASE_MASK
1639                         | gtk_widget_get_events (widget);
1640   priv->bin_window = gdk_window_new (gtk_widget_get_window (widget), &attributes, attributes_mask);
1641   gdk_window_set_user_data (priv->bin_window, widget);
1642 
1643   /* map the icons window */
1644   gdk_window_show (priv->bin_window);
1645 }
1646 
1647 static void
exo_icon_view_unrealize(GtkWidget * widget)1648 exo_icon_view_unrealize (GtkWidget *widget)
1649 {
1650   ExoIconViewPrivate *priv = EXO_ICON_VIEW (widget)->priv;
1651 
1652   /* drop the icons window */
1653   gdk_window_set_user_data (priv->bin_window, NULL);
1654   gdk_window_destroy (priv->bin_window);
1655   priv->bin_window = NULL;
1656 
1657   /* let GtkWidget destroy children and widget->window */
1658   if (GTK_WIDGET_CLASS (exo_icon_view_parent_class)->unrealize)
1659     (*GTK_WIDGET_CLASS (exo_icon_view_parent_class)->unrealize) (widget);
1660 }
1661 
1662 static void
exo_icon_view_get_preferred_width(GtkWidget * widget,gint * minimal_width,gint * natural_width)1663 exo_icon_view_get_preferred_width (GtkWidget *widget,
1664                                    gint      *minimal_width,
1665                                    gint      *natural_width)
1666 {
1667   const ExoIconViewPrivate *priv = EXO_ICON_VIEW (widget)->priv;
1668   ExoIconViewChild         *child;
1669   gint                      child_minimal, child_natural;
1670   GList                    *lp;
1671 
1672   /* well, this is easy */
1673   if (priv->item_width < 0)
1674     *minimal_width = priv->width;
1675   *natural_width = priv->width;
1676 
1677   /* handle the child widgets */
1678   for (lp = priv->children; lp != NULL; lp = lp->next)
1679     {
1680       child = lp->data;
1681       if (gtk_widget_get_visible (child->widget))
1682         gtk_widget_get_preferred_width (child->widget, &child_minimal, &child_natural);
1683     }
1684 }
1685 
1686 static void
exo_icon_view_get_preferred_height(GtkWidget * widget,gint * minimal_height,gint * natural_height)1687 exo_icon_view_get_preferred_height (GtkWidget *widget,
1688                                     gint      *minimal_height,
1689                                     gint      *natural_height)
1690 {
1691   const ExoIconViewPrivate *priv = EXO_ICON_VIEW (widget)->priv;
1692   ExoIconViewChild         *child;
1693   gint                      child_minimal, child_natural;
1694   GList                    *lp;
1695 
1696   /* well, this is easy */
1697   *minimal_height = priv->height;
1698   *natural_height = priv->height;
1699 
1700   /* handle the child widgets */
1701   for (lp = priv->children; lp != NULL; lp = lp->next)
1702     {
1703       child = lp->data;
1704       if (gtk_widget_get_visible (child->widget))
1705         gtk_widget_get_preferred_height (child->widget, &child_minimal, &child_natural);
1706     }
1707 }
1708 
1709 static void
exo_icon_view_allocate_children(ExoIconView * icon_view)1710 exo_icon_view_allocate_children (ExoIconView *icon_view)
1711 {
1712   const ExoIconViewPrivate *priv = icon_view->priv;
1713   const ExoIconViewChild   *child;
1714   GtkAllocation             allocation;
1715   const GList              *lp;
1716   gint                      focus_line_width;
1717   gint                      focus_padding;
1718 
1719   for (lp = priv->children; lp != NULL; lp = lp->next)
1720     {
1721       child = EXO_ICON_VIEW_CHILD (lp->data);
1722 
1723       /* totally ignore our child's requisition */
1724       if (child->cell < 0)
1725         allocation = child->item->area;
1726       else
1727         allocation = child->item->box[child->cell];
1728 
1729       /* increase the item area by focus width/padding */
1730       gtk_widget_style_get (GTK_WIDGET (icon_view), "focus-line-width", &focus_line_width, "focus-padding", &focus_padding, NULL);
1731       allocation.x = MAX (0, allocation.x - (focus_line_width + focus_padding));
1732       allocation.y = MAX (0, allocation.y - (focus_line_width + focus_padding));
1733       allocation.width = MIN (priv->width - allocation.x, allocation.width + 2 * (focus_line_width + focus_padding));
1734       allocation.height = MIN (priv->height - allocation.y, allocation.height + 2 * (focus_line_width + focus_padding));
1735 
1736       /* allocate the area to the child */
1737       gtk_widget_size_allocate (child->widget, &allocation);
1738     }
1739 }
1740 
1741 
1742 
1743 static void
exo_icon_view_size_allocate(GtkWidget * widget,GtkAllocation * allocation)1744 exo_icon_view_size_allocate (GtkWidget     *widget,
1745                              GtkAllocation *allocation)
1746 {
1747   GtkAdjustment *hadjustment;
1748   GtkAdjustment *vadjustment;
1749   ExoIconView   *icon_view = EXO_ICON_VIEW (widget);
1750 
1751   /* apply the new size allocation */
1752   gtk_widget_set_allocation (widget, allocation);
1753 
1754   /* move/resize the clipping window, the icons window
1755    * will be handled by exo_icon_view_layout().
1756    */
1757   if (gtk_widget_get_realized (widget))
1758     gdk_window_move_resize (gtk_widget_get_window (widget), allocation->x, allocation->y, allocation->width, allocation->height);
1759 
1760   /* layout the items */
1761   exo_icon_view_layout (icon_view);
1762 
1763   /* allocate space to the widgets (editing) */
1764   exo_icon_view_allocate_children (icon_view);
1765 
1766   /* update the horizontal scroll adjustment accordingly */
1767   hadjustment = icon_view->priv->hadjustment;
1768   gtk_adjustment_set_page_size (hadjustment, allocation->width);
1769   gtk_adjustment_set_page_increment (hadjustment, allocation->width * 0.9);
1770   gtk_adjustment_set_step_increment (hadjustment, allocation->width * 0.1);
1771   gtk_adjustment_set_lower (hadjustment, 0);
1772   gtk_adjustment_set_upper (hadjustment, MAX (allocation->width, icon_view->priv->width));
1773   if (gtk_adjustment_get_value (hadjustment) > gtk_adjustment_get_upper (hadjustment) - gtk_adjustment_get_lower (hadjustment))
1774     gtk_adjustment_set_value (hadjustment, MAX (0, gtk_adjustment_get_upper (hadjustment) - gtk_adjustment_get_page_size (hadjustment)));
1775 
1776   /* update the vertical scroll adjustment accordingly */
1777   vadjustment = icon_view->priv->vadjustment;
1778   gtk_adjustment_set_page_size (vadjustment, allocation->height);
1779   gtk_adjustment_set_page_increment (vadjustment, allocation->height * 0.9);
1780   gtk_adjustment_set_step_increment (vadjustment, allocation->height * 0.1);
1781   gtk_adjustment_set_lower (vadjustment, 0);
1782   gtk_adjustment_set_upper (vadjustment, MAX (allocation->height, icon_view->priv->height));
1783   if (gtk_adjustment_get_value (vadjustment) > gtk_adjustment_get_upper (vadjustment) - gtk_adjustment_get_page_size (vadjustment))
1784     gtk_adjustment_set_value (vadjustment, MAX (0, gtk_adjustment_get_upper (vadjustment) - gtk_adjustment_get_page_size (vadjustment)));
1785 }
1786 
1787 
1788 
1789 static gboolean
exo_icon_view_draw(GtkWidget * widget,cairo_t * cr)1790 exo_icon_view_draw (GtkWidget *widget,
1791                     cairo_t   *cr)
1792 {
1793   ExoIconViewDropPosition dest_pos;
1794   ExoIconViewPrivate     *priv = EXO_ICON_VIEW (widget)->priv;
1795   ExoIconViewItem        *dest_item = NULL;
1796   ExoIconViewItem        *item;
1797   ExoIconView            *icon_view = EXO_ICON_VIEW (widget);
1798   GtkTreePath            *path;
1799   GdkRectangle            rect;
1800   GdkRectangle            clip;
1801   GdkRectangle            paint_area;
1802   const GList            *lp;
1803   gint                    dest_index = -1;
1804   GtkStyleContext        *context;
1805 
1806   /* verify that the expose happened on the icon window */
1807   if (!gtk_cairo_should_draw_window (cr, priv->bin_window))
1808     return FALSE;
1809 
1810   /* don't handle expose if the layout isn't done yet; the layout
1811    * method will schedule a redraw when done.
1812    */
1813   if (G_UNLIKELY (priv->layout_idle_id != 0))
1814     return FALSE;
1815 
1816   /* "returns [...] FALSE if all of cr is clipped and all drawing can be skipped" [sic] */
1817   if (!gdk_cairo_get_clip_rectangle (cr, &clip))
1818     return FALSE;
1819 
1820   context = gtk_widget_get_style_context (widget);
1821 
1822   /* draw a background according to the css theme */
1823   gtk_render_background (context, cr,
1824                          0, 0,
1825                          gtk_widget_get_allocated_width (widget),
1826                          gtk_widget_get_allocated_height (widget));
1827 
1828   /* transform coordinates so our old calculations work */
1829   gtk_cairo_transform_to_window (cr, widget, icon_view->priv->bin_window);
1830 
1831   /* retrieve the clipping rectangle again, with the transformed coordinates */
1832   gdk_cairo_get_clip_rectangle (cr, &clip);
1833 
1834   /* scroll to the previously remembered path (if any) */
1835   if (G_UNLIKELY (priv->scroll_to_path != NULL))
1836     {
1837       /* grab the path from the reference and invalidate the reference */
1838       path = gtk_tree_row_reference_get_path (priv->scroll_to_path);
1839       gtk_tree_row_reference_free (priv->scroll_to_path);
1840       priv->scroll_to_path = NULL;
1841 
1842       /* check if the reference was still valid */
1843       if (G_LIKELY (path != NULL))
1844         {
1845           /* try to scroll again */
1846           exo_icon_view_scroll_to_path (icon_view, path,
1847                                         priv->scroll_to_use_align,
1848                                         priv->scroll_to_row_align,
1849                                         priv->scroll_to_col_align);
1850 
1851           /* release the path */
1852           gtk_tree_path_free (path);
1853         }
1854     }
1855 
1856   /* check if we need to draw a drag indicator */
1857   exo_icon_view_get_drag_dest_item (icon_view, &path, &dest_pos);
1858   if (G_UNLIKELY (path != NULL))
1859     {
1860       dest_index = gtk_tree_path_get_indices (path)[0];
1861       gtk_tree_path_free (path);
1862     }
1863 
1864   /* paint all items that are affected by the expose event */
1865   for (lp = priv->items; lp != NULL; lp = lp->next)
1866     {
1867       item = EXO_ICON_VIEW_ITEM (lp->data);
1868 
1869       /* FIXME: padding? */
1870       paint_area.x      = item->area.x;
1871       paint_area.y      = item->area.y;
1872       paint_area.width  = item->area.width;
1873       paint_area.height = item->area.height;
1874 
1875       /* check whether we are clipped fully */
1876       if (!gdk_rectangle_intersect (&paint_area, &clip, NULL))
1877         continue;
1878 
1879       /* paint the item */
1880       exo_icon_view_paint_item (icon_view, item, cr, item->area.x, item->area.y, TRUE);
1881       if (G_UNLIKELY (dest_index >= 0 && dest_item == NULL))
1882         {
1883           if (dest_index == g_list_index (priv->items, item))
1884             dest_item = item;
1885         }
1886     }
1887 
1888   /* draw the drag indicator */
1889   if (G_UNLIKELY (dest_item != NULL))
1890     {
1891       switch (dest_pos)
1892         {
1893         case EXO_ICON_VIEW_DROP_INTO:
1894           gtk_render_focus (context,
1895                             cr,
1896                             dest_item->area.x, dest_item->area.y,
1897                             dest_item->area.width, dest_item->area.height);
1898           break;
1899 
1900         case EXO_ICON_VIEW_DROP_ABOVE:
1901           gtk_render_focus (context,
1902                             cr,
1903                             dest_item->area.x, dest_item->area.y - 1,
1904                             dest_item->area.width, 2);
1905           break;
1906 
1907         case EXO_ICON_VIEW_DROP_LEFT:
1908           gtk_render_focus (context,
1909                             cr,
1910                             dest_item->area.x - 1, dest_item->area.y,
1911                             2, dest_item->area.height);
1912           break;
1913 
1914         case EXO_ICON_VIEW_DROP_BELOW:
1915           gtk_render_focus (context,
1916                             cr,
1917                             dest_item->area.x, dest_item->area.y + dest_item->area.height - 1,
1918                             dest_item->area.width, 2);
1919           break;
1920 
1921         case EXO_ICON_VIEW_DROP_RIGHT:
1922           gtk_render_focus (context,
1923                             cr,
1924                             dest_item->area.x + dest_item->area.width - 1, dest_item->area.y,
1925                             2, dest_item->area.height);
1926 
1927         case EXO_ICON_VIEW_NO_DROP:
1928           break;
1929 
1930         default:
1931           g_assert_not_reached ();
1932         }
1933     }
1934 
1935   /* draw the rubberband border */
1936   if (G_UNLIKELY (priv->doing_rubberband))
1937     {
1938       cairo_save (cr);
1939 
1940       rect.x = MIN (icon_view->priv->rubberband_x_1, icon_view->priv->rubberband_x2);
1941       rect.y = MIN (icon_view->priv->rubberband_y_1, icon_view->priv->rubberband_y2);
1942       rect.width = ABS (icon_view->priv->rubberband_x_1 - icon_view->priv->rubberband_x2) + 1;
1943       rect.height = ABS (icon_view->priv->rubberband_y_1 - icon_view->priv->rubberband_y2) + 1;
1944 
1945       gtk_style_context_save (context);
1946       gtk_style_context_add_class (context, GTK_STYLE_CLASS_RUBBERBAND);
1947 
1948       gdk_cairo_rectangle (cr, &rect);
1949       cairo_clip (cr);
1950 
1951       gtk_render_background (context, cr,
1952                              rect.x, rect.y,
1953                              rect.width, rect.height);
1954       gtk_render_frame (context, cr,
1955                         rect.x, rect.y,
1956                         rect.width, rect.height);
1957 
1958       gtk_style_context_restore (context);
1959       cairo_restore (cr);
1960     }
1961 
1962   /* let the GtkContainer forward the draw event to all children */
1963   GTK_WIDGET_CLASS (exo_icon_view_parent_class)->draw (widget, cr);
1964 
1965   return FALSE;
1966 }
1967 
1968 
1969 
1970 static gboolean
rubberband_scroll_timeout(gpointer user_data)1971 rubberband_scroll_timeout (gpointer user_data)
1972 {
1973   GtkAdjustment *adjustment;
1974   ExoIconView   *icon_view = EXO_ICON_VIEW (user_data);
1975   gdouble        value;
1976 
1977   /* determine the adjustment for the scroll direction */
1978   adjustment = (icon_view->priv->layout_mode == EXO_ICON_VIEW_LAYOUT_ROWS)
1979              ? icon_view->priv->vadjustment
1980              : icon_view->priv->hadjustment;
1981 
1982   /* determine the new scroll value */
1983   value = MIN (gtk_adjustment_get_value (adjustment) + icon_view->priv->scroll_value_diff, gtk_adjustment_get_upper (adjustment) - gtk_adjustment_get_page_size (adjustment));
1984 
1985   /* apply the new value */
1986   gtk_adjustment_set_value (adjustment, value);
1987 
1988   /* update the rubberband */
1989   exo_icon_view_update_rubberband (icon_view);
1990 
1991   return TRUE;
1992 }
1993 
1994 
1995 static gboolean
exo_icon_view_motion_notify_event(GtkWidget * widget,GdkEventMotion * event)1996 exo_icon_view_motion_notify_event (GtkWidget      *widget,
1997                                    GdkEventMotion *event)
1998 {
1999   ExoIconViewItem *item;
2000   ExoIconView     *icon_view = EXO_ICON_VIEW (widget);
2001   GdkCursor       *cursor;
2002   gint             size;
2003   gint             abso;
2004   GtkAllocation    allocation;
2005 
2006   exo_icon_view_maybe_begin_drag (icon_view, event);
2007   gtk_widget_get_allocation (widget, &allocation);
2008 
2009   if (icon_view->priv->doing_rubberband)
2010     {
2011       if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2012         icon_view->priv->ctrl_pressed = TRUE;
2013       if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2014         icon_view->priv->shift_pressed = TRUE;
2015 
2016       exo_icon_view_update_rubberband (widget);
2017 
2018       if (icon_view->priv->layout_mode == EXO_ICON_VIEW_LAYOUT_ROWS)
2019         {
2020           abso = event->y - icon_view->priv->height *
2021              (gtk_adjustment_get_value (icon_view->priv->vadjustment) /
2022              (gtk_adjustment_get_upper (icon_view->priv->vadjustment) -
2023               gtk_adjustment_get_lower (icon_view->priv->vadjustment)));
2024 
2025           size = allocation.height;
2026         }
2027       else
2028         {
2029           abso = event->x - icon_view->priv->width *
2030              (gtk_adjustment_get_value (icon_view->priv->hadjustment) /
2031              (gtk_adjustment_get_upper (icon_view->priv->hadjustment) -
2032               gtk_adjustment_get_lower (icon_view->priv->hadjustment)));
2033 
2034           size = allocation.width;
2035         }
2036 
2037       if (abso < 0 || abso > size)
2038         {
2039           if (abso < 0)
2040             icon_view->priv->scroll_value_diff = abso;
2041           else
2042             icon_view->priv->scroll_value_diff = abso - size;
2043           icon_view->priv->event_last_x = event->x;
2044           icon_view->priv->event_last_y = event->y;
2045 
2046           if (icon_view->priv->scroll_timeout_id == 0)
2047             icon_view->priv->scroll_timeout_id = gdk_threads_add_timeout (30, rubberband_scroll_timeout,
2048                                                                           icon_view);
2049         }
2050       else
2051         {
2052           remove_scroll_timeout (icon_view);
2053         }
2054     }
2055   else
2056     {
2057       item = exo_icon_view_get_item_at_coords (icon_view, event->x, event->y, TRUE, NULL);
2058       if (item != icon_view->priv->prelit_item)
2059         {
2060           if (G_LIKELY (icon_view->priv->prelit_item != NULL))
2061             exo_icon_view_queue_draw_item (icon_view, icon_view->priv->prelit_item);
2062           icon_view->priv->prelit_item = item;
2063           if (G_LIKELY (item != NULL))
2064             exo_icon_view_queue_draw_item (icon_view, item);
2065 
2066           /* check if we are in single click mode right now */
2067           if (G_UNLIKELY (icon_view->priv->single_click))
2068             {
2069               /* display a hand cursor when pointer is above an item */
2070               if (G_LIKELY (item != NULL))
2071                 {
2072                   /* hand2 seems to be what we should use */
2073                   cursor = gdk_cursor_new_for_display (gdk_window_get_display (event->window), GDK_HAND2);
2074                   gdk_window_set_cursor (event->window, cursor);
2075                   gdk_cursor_unref (cursor);
2076                 }
2077               else
2078                 {
2079                   /* reset the cursor */
2080                   gdk_window_set_cursor (event->window, NULL);
2081                 }
2082 
2083               /* check if autoselection is enabled */
2084               if (G_LIKELY (icon_view->priv->single_click_timeout > 0))
2085                 {
2086                   /* drop any running timeout */
2087                   if (G_LIKELY (icon_view->priv->single_click_timeout_id != 0))
2088                     g_source_remove (icon_view->priv->single_click_timeout_id);
2089 
2090                   /* remember the current event state */
2091                   icon_view->priv->single_click_timeout_state = event->state;
2092 
2093                   /* schedule a new timeout */
2094                   icon_view->priv->single_click_timeout_id = gdk_threads_add_timeout_full (G_PRIORITY_LOW, icon_view->priv->single_click_timeout,
2095                                                                                  exo_icon_view_single_click_timeout, icon_view,
2096                                                                                  exo_icon_view_single_click_timeout_destroy);
2097                 }
2098             }
2099         }
2100     }
2101 
2102   return TRUE;
2103 }
2104 
2105 
2106 
2107 static void
exo_icon_view_remove(GtkContainer * container,GtkWidget * widget)2108 exo_icon_view_remove (GtkContainer *container,
2109                       GtkWidget    *widget)
2110 {
2111   ExoIconViewChild *child;
2112   ExoIconView      *icon_view = EXO_ICON_VIEW (container);
2113   GList            *lp;
2114 
2115   for (lp = icon_view->priv->children; lp != NULL; lp = lp->next)
2116     {
2117       child = lp->data;
2118       if (G_LIKELY (child->widget == widget))
2119         {
2120           icon_view->priv->children = g_list_delete_link (icon_view->priv->children, lp);
2121           gtk_widget_unparent (widget);
2122           g_slice_free (ExoIconViewChild, child);
2123           return;
2124         }
2125     }
2126 }
2127 
2128 
2129 
2130 static void
exo_icon_view_forall(GtkContainer * container,gboolean include_internals,GtkCallback callback,gpointer callback_data)2131 exo_icon_view_forall (GtkContainer *container,
2132                       gboolean      include_internals,
2133                       GtkCallback   callback,
2134                       gpointer      callback_data)
2135 {
2136   ExoIconView *icon_view = EXO_ICON_VIEW (container);
2137   GList       *lp;
2138 
2139   for (lp = icon_view->priv->children; lp != NULL; lp = lp->next)
2140     (*callback) (((ExoIconViewChild *) lp->data)->widget, callback_data);
2141 }
2142 
2143 
2144 
2145 static void
exo_icon_view_item_activate_cell(ExoIconView * icon_view,ExoIconViewItem * item,ExoIconViewCellInfo * info,GdkEvent * event)2146 exo_icon_view_item_activate_cell (ExoIconView         *icon_view,
2147                                   ExoIconViewItem     *item,
2148                                   ExoIconViewCellInfo *info,
2149                                   GdkEvent            *event)
2150 {
2151   GtkCellRendererMode mode;
2152   GdkRectangle        cell_area;
2153   GtkTreePath        *path;
2154   gboolean            visible;
2155   gchar              *path_string;
2156 
2157   exo_icon_view_set_cell_data (icon_view, item);
2158 
2159   g_object_get (G_OBJECT (info->cell), "visible", &visible, "mode", &mode, NULL);
2160 
2161   if (G_UNLIKELY (visible && mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE))
2162     {
2163       exo_icon_view_get_cell_area (icon_view, item, info, &cell_area);
2164 
2165       path = gtk_tree_path_new_from_indices (g_list_index (icon_view->priv->items, item), -1);
2166       path_string = gtk_tree_path_to_string (path);
2167       gtk_tree_path_free (path);
2168 
2169       gtk_cell_renderer_activate (info->cell, event, GTK_WIDGET (icon_view), path_string, &cell_area, &cell_area, 0);
2170 
2171       g_free (path_string);
2172     }
2173 }
2174 
2175 
2176 
2177 static void
exo_icon_view_put(ExoIconView * icon_view,GtkWidget * widget,ExoIconViewItem * item,gint cell)2178 exo_icon_view_put (ExoIconView     *icon_view,
2179                    GtkWidget       *widget,
2180                    ExoIconViewItem *item,
2181                    gint             cell)
2182 {
2183   ExoIconViewChild *child;
2184 
2185   /* allocate the new child */
2186   child = g_slice_new (ExoIconViewChild);
2187   child->widget = widget;
2188   child->item = item;
2189   child->cell = cell;
2190 
2191   /* hook up the child */
2192   icon_view->priv->children = g_list_append (icon_view->priv->children, child);
2193 
2194   /* setup the parent for the child */
2195   if (gtk_widget_get_realized (GTK_WIDGET (icon_view)))
2196     gtk_widget_set_parent_window (child->widget, icon_view->priv->bin_window);
2197   gtk_widget_set_parent (widget, GTK_WIDGET (icon_view));
2198 }
2199 
2200 
2201 
2202 static void
exo_icon_view_remove_widget(GtkCellEditable * editable,ExoIconView * icon_view)2203 exo_icon_view_remove_widget (GtkCellEditable *editable,
2204                              ExoIconView     *icon_view)
2205 {
2206   ExoIconViewItem *item;
2207   GList           *lp;
2208 
2209   if (G_LIKELY (icon_view->priv->edited_item != NULL))
2210     {
2211       item = icon_view->priv->edited_item;
2212       icon_view->priv->edited_item = NULL;
2213       icon_view->priv->editable = NULL;
2214 
2215       for (lp = icon_view->priv->cell_list; lp != NULL; lp = lp->next)
2216         ((ExoIconViewCellInfo *) lp->data)->editing = FALSE;
2217 
2218       if (gtk_widget_has_focus (GTK_WIDGET (editable)))
2219         gtk_widget_grab_focus (GTK_WIDGET (icon_view));
2220 
2221       g_signal_handlers_disconnect_by_func (editable, exo_icon_view_remove_widget, icon_view);
2222       gtk_container_remove (GTK_CONTAINER (icon_view), GTK_WIDGET (editable));
2223 
2224       exo_icon_view_queue_draw_item (icon_view, item);
2225     }
2226 }
2227 
2228 
2229 
2230 static void
exo_icon_view_start_editing(ExoIconView * icon_view,ExoIconViewItem * item,ExoIconViewCellInfo * info,GdkEvent * event)2231 exo_icon_view_start_editing (ExoIconView         *icon_view,
2232                              ExoIconViewItem     *item,
2233                              ExoIconViewCellInfo *info,
2234                              GdkEvent            *event)
2235 {
2236   GtkCellRendererMode mode;
2237   GtkCellEditable    *editable;
2238   GdkRectangle        cell_area;
2239   GtkTreePath        *path;
2240   gboolean            visible;
2241   gchar              *path_string;
2242 
2243   /* setup cell data for the given item */
2244   exo_icon_view_set_cell_data (icon_view, item);
2245 
2246   /* check if the cell is visible and editable (given the updated cell data) */
2247   g_object_get (info->cell, "visible", &visible, "mode", &mode, NULL);
2248   if (G_LIKELY (visible && mode == GTK_CELL_RENDERER_MODE_EDITABLE))
2249     {
2250       /* draw keyboard focus while editing */
2251       EXO_ICON_VIEW_SET_FLAG (icon_view, EXO_ICON_VIEW_DRAW_KEYFOCUS);
2252 
2253       /* determine the cell area */
2254       exo_icon_view_get_cell_area (icon_view, item, info, &cell_area);
2255 
2256       /* determine the tree path */
2257       path = gtk_tree_path_new_from_indices (g_list_index (icon_view->priv->items, item), -1);
2258       path_string = gtk_tree_path_to_string (path);
2259       gtk_tree_path_free (path);
2260 
2261       /* allocate the editable from the cell renderer */
2262       editable = gtk_cell_renderer_start_editing (info->cell, event, GTK_WIDGET (icon_view), path_string, &cell_area, &cell_area, 0);
2263 
2264       /* ugly hack, but works */
2265       if (g_object_class_find_property (G_OBJECT_GET_CLASS (editable), "has-frame") != NULL)
2266         g_object_set (editable, "has-frame", TRUE, NULL);
2267 
2268       /* setup the editing widget */
2269       icon_view->priv->edited_item = item;
2270       icon_view->priv->editable = editable;
2271       info->editing = TRUE;
2272 
2273       exo_icon_view_put (icon_view, GTK_WIDGET (editable), item, info->position);
2274       gtk_cell_editable_start_editing (GTK_CELL_EDITABLE (editable), (GdkEvent *)event);
2275       gtk_widget_grab_focus (GTK_WIDGET (editable));
2276       g_signal_connect (G_OBJECT (editable), "remove-widget", G_CALLBACK (exo_icon_view_remove_widget), icon_view);
2277 
2278       /* cleanup */
2279       g_free (path_string);
2280     }
2281 }
2282 
2283 
2284 
2285 static void
exo_icon_view_stop_editing(ExoIconView * icon_view,gboolean cancel_editing)2286 exo_icon_view_stop_editing (ExoIconView *icon_view,
2287                             gboolean     cancel_editing)
2288 {
2289   ExoIconViewItem *item;
2290   GtkCellRenderer *cell = NULL;
2291   GList           *lp;
2292 
2293   if (icon_view->priv->edited_item == NULL)
2294     return;
2295 
2296   /*
2297    * This is very evil. We need to do this, because
2298    * gtk_cell_editable_editing_done may trigger exo_icon_view_row_changed
2299    * later on. If exo_icon_view_row_changed notices
2300    * icon_view->priv->edited_item != NULL, it'll call
2301    * exo_icon_view_stop_editing again. Bad things will happen then.
2302    *
2303    * Please read that again if you intend to modify anything here.
2304    */
2305 
2306   item = icon_view->priv->edited_item;
2307   icon_view->priv->edited_item = NULL;
2308 
2309   for (lp = icon_view->priv->cell_list; lp != NULL; lp = lp->next)
2310     {
2311       ExoIconViewCellInfo *info = lp->data;
2312       if (info->editing)
2313         {
2314           cell = info->cell;
2315           break;
2316         }
2317     }
2318 
2319   if (G_UNLIKELY (cell == NULL))
2320     return;
2321 
2322   gtk_cell_renderer_stop_editing (cell, cancel_editing);
2323   if (G_LIKELY (!cancel_editing))
2324     gtk_cell_editable_editing_done (icon_view->priv->editable);
2325 
2326   icon_view->priv->edited_item = item;
2327 
2328   gtk_cell_editable_remove_widget (icon_view->priv->editable);
2329 }
2330 
2331 
2332 
2333 static gboolean
exo_icon_view_button_press_event(GtkWidget * widget,GdkEventButton * event)2334 exo_icon_view_button_press_event (GtkWidget      *widget,
2335                                   GdkEventButton *event)
2336 {
2337   ExoIconViewCellInfo *info = NULL;
2338   GtkCellRendererMode  mode;
2339   ExoIconViewItem     *item;
2340   ExoIconView         *icon_view;
2341   GtkTreePath         *path;
2342   gboolean             dirty = FALSE;
2343   gint                 cursor_cell;
2344 
2345   icon_view = EXO_ICON_VIEW (widget);
2346 
2347   if (event->window != icon_view->priv->bin_window)
2348     return FALSE;
2349 
2350   /* stop any pending "single-click-timeout" */
2351   if (G_UNLIKELY (icon_view->priv->single_click_timeout_id != 0))
2352     g_source_remove (icon_view->priv->single_click_timeout_id);
2353 
2354   if (G_UNLIKELY (!gtk_widget_has_focus (widget)))
2355     gtk_widget_grab_focus (widget);
2356 
2357   if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
2358     {
2359       item = exo_icon_view_get_item_at_coords (icon_view,
2360                                                event->x, event->y,
2361                                                TRUE,
2362                                                &info);
2363       if (item != NULL)
2364         {
2365           g_object_get (info->cell, "mode", &mode, NULL);
2366 
2367           if (mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE ||
2368               mode == GTK_CELL_RENDERER_MODE_EDITABLE)
2369             cursor_cell = g_list_index (icon_view->priv->cell_list, info);
2370           else
2371             cursor_cell = -1;
2372 
2373           exo_icon_view_scroll_to_item (icon_view, item);
2374 
2375           if (icon_view->priv->selection_mode == GTK_SELECTION_NONE)
2376             {
2377               exo_icon_view_set_cursor_item (icon_view, item, cursor_cell);
2378             }
2379           else if (icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE &&
2380                    (event->state & GDK_SHIFT_MASK))
2381             {
2382               if (!(event->state & GDK_CONTROL_MASK))
2383                 exo_icon_view_unselect_all_internal (icon_view);
2384 
2385               exo_icon_view_set_cursor_item (icon_view, item, cursor_cell);
2386               if (!icon_view->priv->anchor_item)
2387                 icon_view->priv->anchor_item = item;
2388               else
2389                 exo_icon_view_select_all_between (icon_view,
2390                                                   icon_view->priv->anchor_item,
2391                                                   item);
2392               dirty = TRUE;
2393             }
2394           else
2395             {
2396               if ((icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE ||
2397                   ((icon_view->priv->selection_mode == GTK_SELECTION_SINGLE) && item->selected)) &&
2398                   (event->state & GDK_CONTROL_MASK))
2399                 {
2400                   item->selected = !item->selected;
2401                   exo_icon_view_queue_draw_item (icon_view, item);
2402                   dirty = TRUE;
2403                 }
2404               else
2405                 {
2406                   if (!item->selected)
2407                     {
2408                       exo_icon_view_unselect_all_internal (icon_view);
2409 
2410                       item->selected = TRUE;
2411                       exo_icon_view_queue_draw_item (icon_view, item);
2412                       dirty = TRUE;
2413                     }
2414                 }
2415               exo_icon_view_set_cursor_item (icon_view, item, cursor_cell);
2416               icon_view->priv->anchor_item = item;
2417             }
2418 
2419           /* Save press to possibly begin a drag */
2420           if (icon_view->priv->pressed_button < 0)
2421             {
2422               icon_view->priv->pressed_button = event->button;
2423               icon_view->priv->press_start_x = event->x;
2424               icon_view->priv->press_start_y = event->y;
2425             }
2426 
2427           icon_view->priv->last_single_clicked = item;
2428 
2429           /* cancel the current editing, if it exists */
2430           exo_icon_view_stop_editing (icon_view, TRUE);
2431 
2432           if (mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE)
2433             exo_icon_view_item_activate_cell (icon_view, item, info,
2434                                               (GdkEvent *)event);
2435           else if (mode == GTK_CELL_RENDERER_MODE_EDITABLE)
2436             exo_icon_view_start_editing (icon_view, item, info,
2437                                          (GdkEvent *)event);
2438         }
2439       else
2440         {
2441           /* cancel the current editing, if it exists */
2442           exo_icon_view_stop_editing (icon_view, TRUE);
2443 
2444           if (icon_view->priv->selection_mode != GTK_SELECTION_BROWSE &&
2445               !(event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)))
2446             {
2447               dirty = exo_icon_view_unselect_all_internal (icon_view);
2448             }
2449 
2450           if (icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE)
2451             exo_icon_view_start_rubberbanding (icon_view, event->x, event->y);
2452         }
2453     }
2454   else if (event->button == 1 && event->type == GDK_2BUTTON_PRESS)
2455     {
2456       /* ignore double-click events in single-click mode */
2457       if (G_LIKELY (!icon_view->priv->single_click))
2458         {
2459           item = exo_icon_view_get_item_at_coords (icon_view,
2460                                                    event->x, event->y,
2461                                                    TRUE,
2462                                                    NULL);
2463           if (G_LIKELY (item != NULL))
2464             {
2465               path = gtk_tree_path_new_from_indices (g_list_index (icon_view->priv->items, item), -1);
2466               exo_icon_view_item_activated (icon_view, path);
2467               gtk_tree_path_free (path);
2468             }
2469         }
2470 
2471       icon_view->priv->last_single_clicked = NULL;
2472       icon_view->priv->pressed_button = -1;
2473     }
2474 
2475   /* grab focus and stop drawing the keyboard focus indicator on single clicks */
2476   if (G_LIKELY (event->type != GDK_2BUTTON_PRESS && event->type != GDK_3BUTTON_PRESS))
2477     {
2478       if (!gtk_widget_has_focus (GTK_WIDGET (icon_view)))
2479         gtk_widget_grab_focus (GTK_WIDGET (icon_view));
2480       EXO_ICON_VIEW_UNSET_FLAG (icon_view, EXO_ICON_VIEW_DRAW_KEYFOCUS);
2481     }
2482 
2483   if (dirty)
2484     g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
2485 
2486   return event->button == 1;
2487 }
2488 
2489 
2490 
2491 static gboolean
exo_icon_view_button_release_event(GtkWidget * widget,GdkEventButton * event)2492 exo_icon_view_button_release_event (GtkWidget      *widget,
2493                                     GdkEventButton *event)
2494 {
2495   ExoIconViewItem *item;
2496   ExoIconView     *icon_view = EXO_ICON_VIEW (widget);
2497   GtkTreePath     *path;
2498 
2499   if (icon_view->priv->pressed_button == (gint) event->button)
2500     {
2501       if (G_LIKELY (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == 0)
2502         {
2503           /* determine the item at the mouse coords and check if this is the last single clicked one */
2504           item = exo_icon_view_get_item_at_coords (icon_view, event->x, event->y, TRUE, NULL);
2505           if (G_LIKELY (item != NULL && item == icon_view->priv->last_single_clicked))
2506             {
2507               if (icon_view->priv->single_click)
2508                 {
2509                   /* emit an "item-activated" signal for this item */
2510                   path = gtk_tree_path_new_from_indices (g_list_index (icon_view->priv->items, item), -1);
2511                   exo_icon_view_item_activated (icon_view, path);
2512                   gtk_tree_path_free (path);
2513                 }
2514               else
2515                 {
2516                   /* reduce the selection to just the clicked item  */
2517                   exo_icon_view_unselect_all_internal (icon_view);
2518                   exo_icon_view_select_item (icon_view, item);
2519                 }
2520             }
2521 
2522           /* reset the last single clicked item */
2523           icon_view->priv->last_single_clicked = NULL;
2524         }
2525 
2526       /* reset the pressed_button state */
2527       icon_view->priv->pressed_button = -1;
2528     }
2529 
2530   exo_icon_view_stop_rubberbanding (icon_view);
2531 
2532   remove_scroll_timeout (icon_view);
2533 
2534   return TRUE;
2535 }
2536 
2537 
2538 
2539 static gboolean
exo_icon_view_scroll_event(GtkWidget * widget,GdkEventScroll * event)2540 exo_icon_view_scroll_event (GtkWidget      *widget,
2541                             GdkEventScroll *event)
2542 {
2543   ExoIconView *icon_view = EXO_ICON_VIEW (widget);
2544 
2545   /* we don't care for scroll events in "rows" layout mode, as
2546    * that's completely handled by GtkScrolledWindow.
2547    */
2548   if (icon_view->priv->layout_mode != EXO_ICON_VIEW_LAYOUT_COLS)
2549     return FALSE;
2550 
2551   /* convert vertical scroll events to horizontal ones in "columns" layout mode */
2552   if (event->direction == GDK_SCROLL_UP)
2553     event->direction = GDK_SCROLL_LEFT;
2554   else if (event->direction == GDK_SCROLL_DOWN)
2555     event->direction = GDK_SCROLL_RIGHT;
2556   else if (event->direction == GDK_SCROLL_SMOOTH)
2557     {
2558       event->delta_x = event->delta_y;
2559       event->delta_y = 0.0;
2560     }
2561 
2562   /* scrolling will be handled by GtkScrolledWindow */
2563   return FALSE;
2564 }
2565 
2566 
2567 
2568 static gboolean
exo_icon_view_key_press_event(GtkWidget * widget,GdkEventKey * event)2569 exo_icon_view_key_press_event (GtkWidget   *widget,
2570                                GdkEventKey *event)
2571 {
2572   ExoIconView *icon_view = EXO_ICON_VIEW (widget);
2573   gboolean     retval;
2574 
2575   /* let the parent class handle the key bindings and stuff */
2576   if ((*GTK_WIDGET_CLASS (exo_icon_view_parent_class)->key_press_event) (widget, event))
2577     return TRUE;
2578 
2579   /* check if typeahead search is enabled */
2580   if (G_UNLIKELY (!icon_view->priv->enable_search))
2581     return FALSE;
2582 
2583   exo_icon_view_search_ensure_directory (icon_view);
2584 
2585   /* check if keypress results in a text change in search_entry; prevents showing the search
2586    * window when only modifier keys (shift, control, ...) are pressed */
2587   retval = gtk_entry_im_context_filter_keypress (GTK_ENTRY (icon_view->priv->search_entry), event);
2588 
2589   if (retval)
2590     {
2591       if (exo_icon_view_search_start (icon_view, FALSE))
2592         {
2593           gtk_entry_grab_focus_without_selecting (GTK_ENTRY (icon_view->priv->search_entry));
2594           return TRUE;
2595         }
2596       else
2597         {
2598           gtk_entry_set_text (GTK_ENTRY (icon_view->priv->search_entry), "");
2599           return FALSE;
2600         }
2601     }
2602 
2603   return FALSE;
2604 }
2605 
2606 
2607 
2608 static gboolean
exo_icon_view_focus_out_event(GtkWidget * widget,GdkEventFocus * event)2609 exo_icon_view_focus_out_event (GtkWidget     *widget,
2610                                GdkEventFocus *event)
2611 {
2612   ExoIconView *icon_view = EXO_ICON_VIEW (widget);
2613 
2614   /* be sure to cancel any single-click timeout */
2615   if (G_UNLIKELY (icon_view->priv->single_click_timeout_id != 0))
2616     g_source_remove (icon_view->priv->single_click_timeout_id);
2617 
2618   /* reset the cursor if we're still realized */
2619   if (G_LIKELY (icon_view->priv->bin_window != NULL))
2620     gdk_window_set_cursor (icon_view->priv->bin_window, NULL);
2621 
2622   /* destroy the interactive search dialog */
2623   if (G_UNLIKELY (icon_view->priv->search_window != NULL))
2624     exo_icon_view_search_dialog_hide (icon_view->priv->search_window, icon_view);
2625 
2626   /* schedule a redraw with the new focus state */
2627   gtk_widget_queue_draw (widget);
2628 
2629   return FALSE;
2630 }
2631 
2632 
2633 
2634 static gboolean
exo_icon_view_leave_notify_event(GtkWidget * widget,GdkEventCrossing * event)2635 exo_icon_view_leave_notify_event (GtkWidget        *widget,
2636                                   GdkEventCrossing *event)
2637 {
2638   ExoIconView         *icon_view = EXO_ICON_VIEW (widget);
2639 
2640   /* reset cursor to default */
2641   if (gtk_widget_get_realized (widget))
2642     gdk_window_set_cursor (gtk_widget_get_window (widget), NULL);
2643 
2644   /* reset the prelit item (if any) */
2645   if (G_LIKELY (icon_view->priv->prelit_item != NULL))
2646     {
2647       exo_icon_view_queue_draw_item (icon_view, icon_view->priv->prelit_item);
2648       icon_view->priv->prelit_item = NULL;
2649     }
2650 
2651   /* call the parent's leave_notify_event (if any) */
2652   if (GTK_WIDGET_CLASS (exo_icon_view_parent_class)->leave_notify_event != NULL)
2653     return (*GTK_WIDGET_CLASS (exo_icon_view_parent_class)->leave_notify_event) (widget, event);
2654 
2655   /* other signal handlers may be invoked */
2656   return FALSE;
2657 }
2658 
2659 
2660 
2661 static void
exo_icon_view_update_rubberband(gpointer data)2662 exo_icon_view_update_rubberband (gpointer data)
2663 {
2664   ExoIconView *icon_view;
2665   gint x, y;
2666   GdkRectangle old_area;
2667   GdkRectangle new_area;
2668   GdkRectangle common;
2669   GdkRegion *invalid_region;
2670   GdkSeat *seat;
2671   GdkDevice        *pointer_dev;
2672 
2673   icon_view = EXO_ICON_VIEW (data);
2674 
2675   seat = gdk_display_get_default_seat (gdk_window_get_display (icon_view->priv->bin_window));
2676   pointer_dev = gdk_seat_get_pointer (seat);
2677   gdk_window_get_device_position (icon_view->priv->bin_window, pointer_dev, &x, &y, NULL);
2678 
2679   x = MAX (x, 0);
2680   y = MAX (y, 0);
2681 
2682   old_area.x = MIN (icon_view->priv->rubberband_x_1,
2683                     icon_view->priv->rubberband_x2);
2684   old_area.y = MIN (icon_view->priv->rubberband_y_1,
2685                     icon_view->priv->rubberband_y2);
2686   old_area.width = ABS (icon_view->priv->rubberband_x2 -
2687                         icon_view->priv->rubberband_x_1) + 1;
2688   old_area.height = ABS (icon_view->priv->rubberband_y2 -
2689                          icon_view->priv->rubberband_y_1) + 1;
2690 
2691   new_area.x = MIN (icon_view->priv->rubberband_x_1, x);
2692   new_area.y = MIN (icon_view->priv->rubberband_y_1, y);
2693   new_area.width = ABS (x - icon_view->priv->rubberband_x_1) + 1;
2694   new_area.height = ABS (y - icon_view->priv->rubberband_y_1) + 1;
2695 
2696   invalid_region = gdk_region_rectangle (&old_area);
2697   gdk_region_union_with_rect (invalid_region, &new_area);
2698 
2699   gdk_rectangle_intersect (&old_area, &new_area, &common);
2700   if (common.width > 2 && common.height > 2)
2701     {
2702       GdkRegion *common_region;
2703 
2704       /* make sure the border is invalidated */
2705       common.x += 1;
2706       common.y += 1;
2707       common.width -= 2;
2708       common.height -= 2;
2709 
2710       common_region = gdk_region_rectangle (&common);
2711 
2712       gdk_region_subtract (invalid_region, common_region);
2713       gdk_region_destroy (common_region);
2714     }
2715 
2716   gdk_window_invalidate_region (icon_view->priv->bin_window, invalid_region, TRUE);
2717 
2718   gdk_region_destroy (invalid_region);
2719 
2720   icon_view->priv->rubberband_x2 = x;
2721   icon_view->priv->rubberband_y2 = y;
2722 
2723   exo_icon_view_update_rubberband_selection (icon_view);
2724 }
2725 
2726 
2727 
2728 static void
exo_icon_view_start_rubberbanding(ExoIconView * icon_view,gint x,gint y)2729 exo_icon_view_start_rubberbanding (ExoIconView  *icon_view,
2730                                    gint          x,
2731                                    gint          y)
2732 {
2733   gpointer  drag_data;
2734   GList    *items;
2735 
2736   /* be sure to disable any previously active rubberband */
2737   exo_icon_view_stop_rubberbanding (icon_view);
2738 
2739   for (items = icon_view->priv->items; items; items = items->next)
2740     {
2741       ExoIconViewItem *item = items->data;
2742       item->selected_before_rubberbanding = item->selected;
2743     }
2744 
2745   icon_view->priv->rubberband_x_1 = x;
2746   icon_view->priv->rubberband_y_1 = y;
2747   icon_view->priv->rubberband_x2 = x;
2748   icon_view->priv->rubberband_y2 = y;
2749 
2750   icon_view->priv->doing_rubberband = TRUE;
2751 
2752   gtk_grab_add (GTK_WIDGET (icon_view));
2753 
2754   /* be sure to disable Gtk+ DnD callbacks, because else rubberbanding will be interrupted */
2755   drag_data = g_object_get_data (G_OBJECT (icon_view), I_("gtk-site-data"));
2756   if (G_LIKELY (drag_data != NULL))
2757     {
2758       g_signal_handlers_block_matched (G_OBJECT (icon_view),
2759                                        G_SIGNAL_MATCH_DATA,
2760                                        0, 0, NULL, NULL,
2761                                        drag_data);
2762     }
2763 }
2764 
2765 
2766 
2767 static void
exo_icon_view_stop_rubberbanding(ExoIconView * icon_view)2768 exo_icon_view_stop_rubberbanding (ExoIconView *icon_view)
2769 {
2770   gpointer drag_data;
2771 
2772   if (G_LIKELY (icon_view->priv->doing_rubberband))
2773     {
2774       icon_view->priv->doing_rubberband = FALSE;
2775       icon_view->priv->ctrl_pressed = FALSE;
2776       icon_view->priv->shift_pressed = FALSE;
2777       gtk_grab_remove (GTK_WIDGET (icon_view));
2778       gtk_widget_queue_draw (GTK_WIDGET (icon_view));
2779 
2780       /* re-enable Gtk+ DnD callbacks again */
2781       drag_data = g_object_get_data (G_OBJECT (icon_view), I_("gtk-site-data"));
2782       if (G_LIKELY (drag_data != NULL))
2783         {
2784           g_signal_handlers_unblock_matched (G_OBJECT (icon_view),
2785                                              G_SIGNAL_MATCH_DATA,
2786                                              0, 0, NULL, NULL,
2787                                              drag_data);
2788         }
2789     }
2790 }
2791 
2792 
2793 
2794 static void
exo_icon_view_update_rubberband_selection(ExoIconView * icon_view)2795 exo_icon_view_update_rubberband_selection (ExoIconView *icon_view)
2796 {
2797   ExoIconViewItem *item;
2798   gboolean         selected;
2799   gboolean         changed = FALSE;
2800   gboolean         is_in;
2801   GList           *lp;
2802   gint             x, y;
2803   gint             width;
2804   gint             height;
2805 
2806   /* determine the new rubberband area */
2807   x = MIN (icon_view->priv->rubberband_x_1, icon_view->priv->rubberband_x2);
2808   y = MIN (icon_view->priv->rubberband_y_1, icon_view->priv->rubberband_y2);
2809   width = ABS (icon_view->priv->rubberband_x_1 - icon_view->priv->rubberband_x2);
2810   height = ABS (icon_view->priv->rubberband_y_1 - icon_view->priv->rubberband_y2);
2811 
2812   /* check all items */
2813   for (lp = icon_view->priv->items; lp != NULL; lp = lp->next)
2814     {
2815       item = EXO_ICON_VIEW_ITEM (lp->data);
2816 
2817       is_in = exo_icon_view_item_hit_test (icon_view, item, x, y, width, height);
2818 
2819       selected = is_in ^ item->selected_before_rubberbanding;
2820 
2821       if (G_UNLIKELY (item->selected != selected))
2822         {
2823           /* extend */
2824           if (icon_view->priv->shift_pressed && !icon_view->priv->ctrl_pressed)
2825             {
2826               if (!item->selected)
2827                 {
2828                   changed = TRUE;
2829                   item->selected = TRUE;
2830                 }
2831             }
2832           /* add/remove */
2833           else
2834             {
2835               changed = TRUE;
2836               item->selected = selected;
2837             }
2838 
2839           if (changed)
2840             exo_icon_view_queue_draw_item (icon_view, item);
2841         }
2842 
2843       if (item->selected)
2844         icon_view->priv->cursor_item = item;
2845     }
2846 
2847   if (G_LIKELY (changed))
2848     g_signal_emit (G_OBJECT (icon_view), icon_view_signals[SELECTION_CHANGED], 0);
2849 }
2850 
2851 
2852 
2853 static gboolean
exo_icon_view_item_hit_test(ExoIconView * icon_view,ExoIconViewItem * item,gint x,gint y,gint width,gint height)2854 exo_icon_view_item_hit_test (ExoIconView      *icon_view,
2855                              ExoIconViewItem  *item,
2856                              gint              x,
2857                              gint              y,
2858                              gint              width,
2859                              gint              height)
2860 {
2861   GList               *l;
2862   GdkRectangle         box;
2863   ExoIconViewCellInfo *info;
2864 
2865   for (l = icon_view->priv->cell_list; l; l = l->next)
2866     {
2867       info = l->data;
2868 
2869       if (!gtk_cell_renderer_get_visible (info->cell)
2870           || item->box == NULL)
2871         continue;
2872 
2873       box = item->box[info->position];
2874 
2875       if (MIN (x + width, box.x + box.width) - MAX (x, box.x) > 0
2876           && MIN (y + height, box.y + box.height) - MAX (y, box.y) > 0)
2877         return TRUE;
2878     }
2879 
2880   return FALSE;
2881 }
2882 
2883 
2884 
2885 static gboolean
exo_icon_view_unselect_all_internal(ExoIconView * icon_view)2886 exo_icon_view_unselect_all_internal (ExoIconView  *icon_view)
2887 {
2888   ExoIconViewItem *item;
2889   gboolean         dirty = FALSE;
2890   GList           *lp;
2891 
2892   if (G_LIKELY (icon_view->priv->selection_mode != GTK_SELECTION_NONE))
2893     {
2894       for (lp = icon_view->priv->items; lp != NULL; lp = lp->next)
2895         {
2896           item = EXO_ICON_VIEW_ITEM (lp->data);
2897           if (item->selected)
2898             {
2899               dirty = TRUE;
2900               item->selected = FALSE;
2901               exo_icon_view_queue_draw_item (icon_view, item);
2902             }
2903         }
2904     }
2905 
2906   return dirty;
2907 }
2908 
2909 
2910 
2911 static void
exo_icon_view_set_adjustments(ExoIconView * icon_view,GtkAdjustment * hadj,GtkAdjustment * vadj)2912 exo_icon_view_set_adjustments (ExoIconView   *icon_view,
2913                                GtkAdjustment *hadj,
2914                                GtkAdjustment *vadj)
2915 {
2916   gboolean need_adjust = FALSE;
2917 
2918   if (hadj)
2919     _exo_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
2920   else
2921     hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
2922   if (vadj)
2923     _exo_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
2924   else
2925     vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
2926 
2927   if (icon_view->priv->hadjustment && (icon_view->priv->hadjustment != hadj))
2928     {
2929       g_signal_handlers_disconnect_matched (icon_view->priv->hadjustment, G_SIGNAL_MATCH_DATA,
2930                                            0, 0, NULL, NULL, icon_view);
2931       g_object_unref (icon_view->priv->hadjustment);
2932     }
2933 
2934   if (icon_view->priv->vadjustment && (icon_view->priv->vadjustment != vadj))
2935     {
2936       g_signal_handlers_disconnect_matched (icon_view->priv->vadjustment, G_SIGNAL_MATCH_DATA,
2937                                             0, 0, NULL, NULL, icon_view);
2938       g_object_unref (icon_view->priv->vadjustment);
2939     }
2940 
2941   if (icon_view->priv->hadjustment != hadj)
2942     {
2943       icon_view->priv->hadjustment = hadj;
2944       g_object_ref_sink (icon_view->priv->hadjustment);
2945 
2946       g_signal_connect (icon_view->priv->hadjustment, "value-changed",
2947                         G_CALLBACK (exo_icon_view_adjustment_changed),
2948                         icon_view);
2949       need_adjust = TRUE;
2950     }
2951 
2952   if (icon_view->priv->vadjustment != vadj)
2953     {
2954       icon_view->priv->vadjustment = vadj;
2955       g_object_ref_sink (icon_view->priv->vadjustment);
2956 
2957       g_signal_connect (icon_view->priv->vadjustment, "value-changed",
2958                         G_CALLBACK (exo_icon_view_adjustment_changed),
2959                         icon_view);
2960       need_adjust = TRUE;
2961     }
2962 
2963   if (need_adjust)
2964     exo_icon_view_adjustment_changed (NULL, icon_view);
2965 }
2966 
2967 
2968 
2969 static void
exo_icon_view_real_select_all(ExoIconView * icon_view)2970 exo_icon_view_real_select_all (ExoIconView *icon_view)
2971 {
2972   exo_icon_view_select_all (icon_view);
2973 }
2974 
2975 
2976 
2977 static void
exo_icon_view_real_unselect_all(ExoIconView * icon_view)2978 exo_icon_view_real_unselect_all (ExoIconView *icon_view)
2979 {
2980   exo_icon_view_unselect_all (icon_view);
2981 }
2982 
2983 
2984 
2985 static void
exo_icon_view_real_select_cursor_item(ExoIconView * icon_view)2986 exo_icon_view_real_select_cursor_item (ExoIconView *icon_view)
2987 {
2988   exo_icon_view_unselect_all (icon_view);
2989 
2990   if (icon_view->priv->cursor_item != NULL)
2991     exo_icon_view_select_item (icon_view, icon_view->priv->cursor_item);
2992 }
2993 
2994 
2995 
2996 static gboolean
exo_icon_view_real_activate_cursor_item(ExoIconView * icon_view)2997 exo_icon_view_real_activate_cursor_item (ExoIconView *icon_view)
2998 {
2999   GtkTreePath *path;
3000   GtkCellRendererMode mode;
3001   ExoIconViewCellInfo *info = NULL;
3002 
3003   if (!icon_view->priv->cursor_item)
3004     return FALSE;
3005 
3006   info = g_list_nth_data (icon_view->priv->cell_list,
3007                           icon_view->priv->cursor_cell);
3008 
3009   if (info)
3010     {
3011       g_object_get (info->cell, "mode", &mode, NULL);
3012 
3013       if (mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE)
3014         {
3015           exo_icon_view_item_activate_cell (icon_view,
3016                                             icon_view->priv->cursor_item,
3017                                             info, NULL);
3018           return TRUE;
3019         }
3020       else if (mode == GTK_CELL_RENDERER_MODE_EDITABLE)
3021         {
3022           exo_icon_view_start_editing (icon_view,
3023                                        icon_view->priv->cursor_item,
3024                                        info, NULL);
3025           return TRUE;
3026         }
3027     }
3028 
3029   path = gtk_tree_path_new_from_indices (g_list_index (icon_view->priv->items, icon_view->priv->cursor_item), -1);
3030   exo_icon_view_item_activated (icon_view, path);
3031   gtk_tree_path_free (path);
3032 
3033   return TRUE;
3034 }
3035 
3036 
3037 
3038 static gboolean
exo_icon_view_real_start_interactive_search(ExoIconView * icon_view)3039 exo_icon_view_real_start_interactive_search (ExoIconView *icon_view)
3040 {
3041   return exo_icon_view_search_start (icon_view, TRUE);
3042 }
3043 
3044 
3045 
3046 static void
exo_icon_view_real_toggle_cursor_item(ExoIconView * icon_view)3047 exo_icon_view_real_toggle_cursor_item (ExoIconView *icon_view)
3048 {
3049   if (G_LIKELY (icon_view->priv->cursor_item != NULL))
3050     {
3051       switch (icon_view->priv->selection_mode)
3052         {
3053         case GTK_SELECTION_NONE:
3054           break;
3055 
3056         case GTK_SELECTION_BROWSE:
3057           exo_icon_view_select_item (icon_view, icon_view->priv->cursor_item);
3058           break;
3059 
3060         case GTK_SELECTION_SINGLE:
3061           if (icon_view->priv->cursor_item->selected)
3062             exo_icon_view_unselect_item (icon_view, icon_view->priv->cursor_item);
3063           else
3064             exo_icon_view_select_item (icon_view, icon_view->priv->cursor_item);
3065           break;
3066 
3067         case GTK_SELECTION_MULTIPLE:
3068           icon_view->priv->cursor_item->selected = !icon_view->priv->cursor_item->selected;
3069           g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
3070           exo_icon_view_queue_draw_item (icon_view, icon_view->priv->cursor_item);
3071           break;
3072 
3073         default:
3074           g_assert_not_reached ();
3075         }
3076     }
3077 }
3078 
3079 
3080 
3081 static void
exo_icon_view_adjustment_changed(GtkAdjustment * adjustment,ExoIconView * icon_view)3082 exo_icon_view_adjustment_changed (GtkAdjustment *adjustment,
3083                                   ExoIconView   *icon_view)
3084 {
3085   if (gtk_widget_get_realized (GTK_WIDGET (icon_view)))
3086     {
3087       gdk_window_move (icon_view->priv->bin_window, -gtk_adjustment_get_value (icon_view->priv->hadjustment), -gtk_adjustment_get_value (icon_view->priv->vadjustment));
3088 
3089       if (G_UNLIKELY (icon_view->priv->doing_rubberband))
3090         exo_icon_view_update_rubberband (GTK_WIDGET (icon_view));
3091     }
3092 }
3093 
3094 
3095 
3096 static GList*
exo_icon_view_layout_single_row(ExoIconView * icon_view,GList * first_item,gint item_width,gint row,gint * y,gint * maximum_width,gint max_cols)3097 exo_icon_view_layout_single_row (ExoIconView *icon_view,
3098                                  GList       *first_item,
3099                                  gint         item_width,
3100                                  gint         row,
3101                                  gint        *y,
3102                                  gint        *maximum_width,
3103                                  gint         max_cols)
3104 {
3105   ExoIconViewPrivate *priv = icon_view->priv;
3106   ExoIconViewItem    *item;
3107   gboolean            rtl;
3108   GList              *last_item;
3109   GList              *items = first_item;
3110   gint               *max_width;
3111   gint               *max_height;
3112   gint                focus_width;
3113   gint                current_width;
3114   gint                colspan;
3115   gint                col = 0;
3116   gint                x;
3117   gint                i;
3118   GtkAllocation       allocation;
3119 
3120   rtl = (gtk_widget_get_direction (GTK_WIDGET (icon_view)) == GTK_TEXT_DIR_RTL);
3121   gtk_widget_get_allocation (GTK_WIDGET (icon_view), &allocation);
3122 
3123   max_width = g_newa (gint, priv->n_cells);
3124   max_height = g_newa (gint, priv->n_cells);
3125   for (i = priv->n_cells; --i >= 0; )
3126     {
3127       max_width[i] = 0;
3128       max_height[i] = 0;
3129     }
3130 
3131   gtk_widget_style_get (GTK_WIDGET (icon_view),
3132                         "focus-line-width", &focus_width,
3133                         NULL);
3134 
3135   x = priv->margin + focus_width;
3136   current_width = 2 * (priv->margin + focus_width);
3137 
3138   for (items = first_item; items != NULL; items = items->next)
3139     {
3140       item = EXO_ICON_VIEW_ITEM (items->data);
3141 
3142       exo_icon_view_calculate_item_size (icon_view, item);
3143       colspan = 1 + (item->area.width - 1) / (item_width + priv->column_spacing);
3144 
3145       item->area.width = colspan * item_width + (colspan - 1) * priv->column_spacing;
3146 
3147       current_width += item->area.width + priv->column_spacing + 2 * focus_width;
3148 
3149       if (G_LIKELY (items != first_item))
3150         {
3151           if ((priv->columns <= 0 && current_width > allocation.width) ||
3152               (priv->columns > 0 && col >= priv->columns) ||
3153               (max_cols > 0 && col >= max_cols))
3154             break;
3155         }
3156 
3157       item->area.y = *y + focus_width;
3158       item->area.x = rtl ? allocation.width - item->area.width - x : x;
3159 
3160       x = current_width - (priv->margin + focus_width);
3161 
3162       for (i = 0; i < priv->n_cells; i++)
3163         {
3164           max_width[i] = MAX (max_width[i], item->box[i].width);
3165           max_height[i] = MAX (max_height[i], item->box[i].height);
3166         }
3167 
3168       if (current_width > *maximum_width)
3169         *maximum_width = current_width;
3170 
3171       item->row = row;
3172       item->col = col;
3173 
3174       col += colspan;
3175     }
3176 
3177   last_item = items;
3178 
3179   /* Now go through the row again and align the icons */
3180   for (items = first_item; items != last_item; items = items->next)
3181     {
3182       item = EXO_ICON_VIEW_ITEM (items->data);
3183 
3184       exo_icon_view_calculate_item_size2 (icon_view, item, max_width, max_height);
3185 
3186       /* We may want to readjust the new y coordinate. */
3187       if (item->area.y + item->area.height + focus_width + priv->row_spacing > *y)
3188         *y = item->area.y + item->area.height + focus_width + priv->row_spacing;
3189 
3190       if (G_UNLIKELY (rtl))
3191         item->col = col - 1 - item->col;
3192     }
3193 
3194   return last_item;
3195 }
3196 
3197 
3198 
3199 static GList*
exo_icon_view_layout_single_col(ExoIconView * icon_view,GList * first_item,gint item_height,gint col,gint * x,gint * maximum_height,gint max_rows)3200 exo_icon_view_layout_single_col (ExoIconView *icon_view,
3201                                  GList       *first_item,
3202                                  gint         item_height,
3203                                  gint         col,
3204                                  gint        *x,
3205                                  gint        *maximum_height,
3206                                  gint         max_rows)
3207 {
3208   ExoIconViewPrivate *priv = icon_view->priv;
3209   ExoIconViewItem    *item;
3210   GList              *items = first_item;
3211   GList              *last_item;
3212   gint               *max_width;
3213   gint               *max_height;
3214   gint                focus_width;
3215   gint                current_height;
3216   gint                rowspan;
3217   gint                row = 0;
3218   gint                y;
3219   gint                i;
3220   GtkAllocation       allocation;
3221 
3222   max_width = g_newa (gint, priv->n_cells);
3223   max_height = g_newa (gint, priv->n_cells);
3224   for (i = priv->n_cells; --i >= 0; )
3225     {
3226       max_width[i] = 0;
3227       max_height[i] = 0;
3228     }
3229 
3230   gtk_widget_style_get (GTK_WIDGET (icon_view),
3231                         "focus-line-width", &focus_width,
3232                         NULL);
3233   gtk_widget_get_allocation (GTK_WIDGET (icon_view), &allocation);
3234 
3235   y = priv->margin + focus_width;
3236   current_height = 2 * (priv->margin + focus_width);
3237 
3238   for (items = first_item; items != NULL; items = items->next)
3239     {
3240       item = EXO_ICON_VIEW_ITEM (items->data);
3241 
3242       exo_icon_view_calculate_item_size (icon_view, item);
3243 
3244       rowspan = 1 + (item->area.height - 1) / (item_height + priv->row_spacing);
3245 
3246       item->area.height = rowspan * item_height + (rowspan - 1) * priv->row_spacing;
3247 
3248       current_height += item->area.height + priv->row_spacing + 2 * focus_width;
3249 
3250       if (G_LIKELY (items != first_item))
3251         {
3252           if (current_height >= allocation.height ||
3253              (max_rows > 0 && row >= max_rows))
3254             break;
3255         }
3256 
3257       item->area.y = y + focus_width;
3258       item->area.x = *x;
3259 
3260       y = current_height - (priv->margin + focus_width);
3261 
3262       for (i = 0; i < priv->n_cells; i++)
3263         {
3264           max_width[i] = MAX (max_width[i], item->box[i].width);
3265           max_height[i] = MAX (max_height[i], item->box[i].height);
3266         }
3267 
3268       if (current_height > *maximum_height)
3269         *maximum_height = current_height;
3270 
3271       item->row = row;
3272       item->col = col;
3273 
3274       row += rowspan;
3275     }
3276 
3277   last_item = items;
3278 
3279   /* Now go through the column again and align the icons */
3280   for (items = first_item; items != last_item; items = items->next)
3281     {
3282       item = EXO_ICON_VIEW_ITEM (items->data);
3283 
3284       exo_icon_view_calculate_item_size2 (icon_view, item, max_width, max_height);
3285 
3286       /* We may want to readjust the new x coordinate. */
3287       if (item->area.x + item->area.width + focus_width + priv->column_spacing > *x)
3288         *x = item->area.x + item->area.width + focus_width + priv->column_spacing;
3289     }
3290 
3291   return last_item;
3292 }
3293 
3294 
3295 
3296 static void
exo_icon_view_set_adjustment_upper(GtkAdjustment * adj,gdouble upper)3297 exo_icon_view_set_adjustment_upper (GtkAdjustment *adj,
3298                                     gdouble        upper)
3299 {
3300   if (upper != gtk_adjustment_get_upper (adj))
3301     {
3302       gdouble min = MAX (0.0, upper - gtk_adjustment_get_page_size (adj));
3303 
3304       gtk_adjustment_set_upper (adj, upper);
3305 
3306       if (gtk_adjustment_get_value (adj) > min)
3307         {
3308           gtk_adjustment_set_value (adj, min);
3309         }
3310     }
3311 }
3312 
3313 
3314 
3315 static gint
exo_icon_view_layout_cols(ExoIconView * icon_view,gint item_height,gint * x,gint * maximum_height,gint max_rows)3316 exo_icon_view_layout_cols (ExoIconView *icon_view,
3317                            gint         item_height,
3318                            gint        *x,
3319                            gint        *maximum_height,
3320                            gint         max_rows)
3321 {
3322   GList *icons = icon_view->priv->items;
3323   GList *items;
3324   gint   col = 0;
3325   gint   rows = 0;
3326 
3327   *x = icon_view->priv->margin;
3328 
3329   do
3330     {
3331       icons = exo_icon_view_layout_single_col (icon_view, icons,
3332                                                item_height, col,
3333                                                x, maximum_height, max_rows);
3334 
3335       /* count the number of rows in the first column */
3336       if (G_UNLIKELY (col == 0))
3337         {
3338           for (items = icon_view->priv->items, rows = 0; items != icons; items = items->next, ++rows)
3339             ;
3340         }
3341 
3342       col++;
3343     }
3344   while (icons != NULL);
3345 
3346   *x += icon_view->priv->margin;
3347   icon_view->priv->cols = col;
3348 
3349   return rows;
3350 }
3351 
3352 
3353 
3354 static gint
exo_icon_view_layout_rows(ExoIconView * icon_view,gint item_width,gint * y,gint * maximum_width,gint max_cols)3355 exo_icon_view_layout_rows (ExoIconView *icon_view,
3356                            gint         item_width,
3357                            gint        *y,
3358                            gint        *maximum_width,
3359                            gint         max_cols)
3360 {
3361   GList *icons = icon_view->priv->items;
3362   GList *items;
3363   gint   row = 0;
3364   gint   cols = 0;
3365 
3366   *y = icon_view->priv->margin;
3367 
3368   do
3369     {
3370       icons = exo_icon_view_layout_single_row (icon_view, icons,
3371                                                item_width, row,
3372                                                y, maximum_width, max_cols);
3373 
3374       /* count the number of columns in the first row */
3375       if (G_UNLIKELY (row == 0))
3376         {
3377           for (items = icon_view->priv->items, cols = 0; items != icons; items = items->next, ++cols)
3378             ;
3379         }
3380 
3381       row++;
3382     }
3383   while (icons != NULL);
3384 
3385   *y += icon_view->priv->margin;
3386   icon_view->priv->rows = row;
3387 
3388   return cols;
3389 }
3390 
3391 
3392 
3393 static void
exo_icon_view_layout(ExoIconView * icon_view)3394 exo_icon_view_layout (ExoIconView *icon_view)
3395 {
3396   ExoIconViewPrivate *priv = icon_view->priv;
3397   ExoIconViewItem    *item;
3398   GList              *icons;
3399   gint                maximum_height = 0;
3400   gint                maximum_width = 0;
3401   gint                item_height;
3402   gint                item_width;
3403   gint                rows, cols;
3404   gint                x, y;
3405   GtkAllocation       allocation;
3406   GtkRequisition      requisition;
3407 
3408   /* verify that we still have a valid model */
3409   if (G_UNLIKELY (priv->model == NULL))
3410     return;
3411 
3412   gtk_widget_get_allocation (GTK_WIDGET (icon_view), &allocation);
3413 
3414   gtk_widget_get_preferred_width (GTK_WIDGET (icon_view), NULL, &requisition.width);
3415   gtk_widget_get_preferred_height (GTK_WIDGET (icon_view), NULL, &requisition.height);
3416 
3417   /* determine the layout mode */
3418   if (G_LIKELY (priv->layout_mode == EXO_ICON_VIEW_LAYOUT_ROWS))
3419     {
3420       /* calculate item sizes on-demand */
3421       item_width = priv->item_width;
3422       if (item_width < 0)
3423         {
3424           for (icons = priv->items; icons != NULL; icons = icons->next)
3425             {
3426               item = icons->data;
3427               exo_icon_view_calculate_item_size (icon_view, item);
3428               item_width = MAX (item_width, item->area.width);
3429             }
3430         }
3431 
3432       cols = exo_icon_view_layout_rows (icon_view, item_width, &y, &maximum_width, 0);
3433 
3434       /* If, by adding another column, we increase the height of the icon view, thus forcing a
3435        * vertical scrollbar to appear that would prevent the last column from being able to fit,
3436        * we need to relayout the icons with one less column.
3437        */
3438       if (cols == priv->cols + 1 && y > allocation.height &&
3439           priv->height <= allocation.height)
3440         {
3441           cols = exo_icon_view_layout_rows (icon_view, item_width, &y, &maximum_width, priv->cols);
3442         }
3443 
3444       priv->width = maximum_width;
3445       priv->height = y;
3446       priv->cols = cols;
3447     }
3448   else
3449     {
3450       /* calculate item sizes on-demand */
3451       for (icons = priv->items, item_height = 0; icons != NULL; icons = icons->next)
3452         {
3453           item = icons->data;
3454           exo_icon_view_calculate_item_size (icon_view, item);
3455           item_height = MAX (item_height, item->area.height);
3456         }
3457 
3458       rows = exo_icon_view_layout_cols (icon_view, item_height, &x, &maximum_height, 0);
3459 
3460       /* If, by adding another row, we increase the width of the icon view, thus forcing a
3461        * horizontal scrollbar to appear that would prevent the last row from being able to fit,
3462        * we need to relayout the icons with one less row.
3463        */
3464       if (rows == priv->rows + 1 && x > allocation.width &&
3465           priv->width <= allocation.width)
3466         {
3467           rows = exo_icon_view_layout_cols (icon_view, item_height, &x, &maximum_height, priv->rows);
3468         }
3469 
3470       priv->height = maximum_height;
3471       priv->width = x;
3472       priv->rows = rows;
3473     }
3474 
3475   exo_icon_view_set_adjustment_upper (priv->hadjustment, priv->width);
3476   exo_icon_view_set_adjustment_upper (priv->vadjustment, priv->height);
3477 
3478   if (priv->width != requisition.width
3479       || priv->height != requisition.height)
3480     gtk_widget_queue_resize_no_redraw (GTK_WIDGET (icon_view));
3481 
3482   if (gtk_widget_get_realized (GTK_WIDGET (icon_view)))
3483     {
3484       gdk_window_resize (priv->bin_window,
3485                          MAX (priv->width, allocation.width),
3486                          MAX (priv->height, allocation.height));
3487     }
3488 
3489   /* drop any pending layout idle source */
3490   if (priv->layout_idle_id != 0)
3491     g_source_remove (priv->layout_idle_id);
3492 
3493   gtk_widget_queue_draw (GTK_WIDGET (icon_view));
3494 }
3495 
3496 
3497 
3498 static void
exo_icon_view_get_cell_area(ExoIconView * icon_view,ExoIconViewItem * item,ExoIconViewCellInfo * info,GdkRectangle * cell_area)3499 exo_icon_view_get_cell_area (ExoIconView         *icon_view,
3500                              ExoIconViewItem     *item,
3501                              ExoIconViewCellInfo *info,
3502                              GdkRectangle        *cell_area)
3503 {
3504   if (icon_view->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
3505     {
3506       cell_area->x = item->box[info->position].x - item->before[info->position];
3507       cell_area->y = item->area.y;
3508       cell_area->width = item->box[info->position].width + item->before[info->position] + item->after[info->position];
3509       cell_area->height = item->area.height;
3510     }
3511   else
3512     {
3513       cell_area->x = item->area.x;
3514       cell_area->y = item->box[info->position].y - item->before[info->position];
3515       cell_area->width = item->area.width;
3516       cell_area->height = item->box[info->position].height + item->before[info->position] + item->after[info->position];
3517     }
3518 }
3519 
3520 
3521 
3522 static void
exo_icon_view_calculate_item_size(ExoIconView * icon_view,ExoIconViewItem * item)3523 exo_icon_view_calculate_item_size (ExoIconView     *icon_view,
3524                                    ExoIconViewItem *item)
3525 {
3526   ExoIconViewCellInfo *info;
3527   GList               *lp;
3528   gchar               *buffer;
3529 
3530   if (G_LIKELY (item->area.width != -1))
3531     return;
3532 
3533   if (G_UNLIKELY (item->n_cells != icon_view->priv->n_cells))
3534     {
3535       /* apply the new cell size */
3536       item->n_cells = icon_view->priv->n_cells;
3537 
3538       /* release the memory chunk (if any) */
3539       g_free (item->box);
3540 
3541       /* allocate a single memory chunk for box, after and before */
3542       buffer = g_malloc0 (item->n_cells * (sizeof (GdkRectangle) + 2 * sizeof (gint)));
3543 
3544       /* assign the memory */
3545       item->box = (GdkRectangle *) buffer;
3546       item->after = (gint *) (buffer + item->n_cells * sizeof (GdkRectangle));
3547       item->before = item->after + item->n_cells;
3548     }
3549 
3550   exo_icon_view_set_cell_data (icon_view, item);
3551 
3552   item->area.width = 0;
3553   item->area.height = 0;
3554   for (lp = icon_view->priv->cell_list; lp != NULL; lp = lp->next)
3555     {
3556       info = EXO_ICON_VIEW_CELL_INFO (lp->data);
3557       if (G_UNLIKELY (!gtk_cell_renderer_get_visible (info->cell)))
3558         continue;
3559 
3560       {
3561         GtkRequisition req;
3562 
3563         gtk_cell_renderer_get_preferred_size (info->cell, GTK_WIDGET (icon_view),
3564                                               &req, NULL);
3565 
3566         if (info->is_text && icon_view->priv->orientation == GTK_ORIENTATION_VERTICAL)
3567           {
3568             GdkRectangle cell_area, aligned_area;
3569 
3570             cell_area.x = cell_area.y = 0;
3571             cell_area.width = req.width;
3572             cell_area.height = req.height;
3573 
3574             gtk_cell_renderer_get_aligned_area (info->cell, GTK_WIDGET (icon_view),
3575                                                 0, &cell_area, &aligned_area);
3576 
3577             /* 4px padding for width to avoid truncated text in small zoom levels */
3578             item->box[info->position].width = aligned_area.width + 4;
3579             item->box[info->position].height = aligned_area.height;
3580           }
3581         else
3582           {
3583             item->box[info->position].width = req.width;
3584             item->box[info->position].height = req.height;
3585           }
3586       }
3587 
3588       if (icon_view->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
3589         {
3590           item->area.width += item->box[info->position].width + (info->position > 0 ? icon_view->priv->spacing : 0);
3591           item->area.height = MAX (item->area.height, item->box[info->position].height);
3592         }
3593       else
3594         {
3595           item->area.width = MAX (item->area.width, item->box[info->position].width);
3596           item->area.height += item->box[info->position].height + (info->position > 0 ? icon_view->priv->spacing : 0);
3597         }
3598     }
3599 }
3600 
3601 
3602 
3603 static void
exo_icon_view_calculate_item_size2(ExoIconView * icon_view,ExoIconViewItem * item,gint * max_width,gint * max_height)3604 exo_icon_view_calculate_item_size2 (ExoIconView     *icon_view,
3605                                     ExoIconViewItem *item,
3606                                     gint            *max_width,
3607                                     gint            *max_height)
3608 {
3609   ExoIconViewCellInfo *info;
3610   GdkRectangle        *box;
3611   GdkRectangle         cell_area;
3612   gboolean             rtl;
3613   GList               *lp;
3614   gint                 spacing;
3615   gint                 i, k;
3616   gfloat               cell_xalign, cell_yalign;
3617   gint                 cell_xpad, cell_ypad;
3618 
3619   rtl = (gtk_widget_get_direction (GTK_WIDGET (icon_view)) == GTK_TEXT_DIR_RTL);
3620 
3621   spacing = icon_view->priv->spacing;
3622 
3623   if (G_LIKELY (icon_view->priv->layout_mode == EXO_ICON_VIEW_LAYOUT_ROWS))
3624     {
3625       item->area.height = 0;
3626       for (i = 0; i < icon_view->priv->n_cells; ++i)
3627         {
3628           if (icon_view->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
3629             item->area.height = MAX (item->area.height, max_height[i]);
3630           else
3631             item->area.height += max_height[i] + (i > 0 ? spacing : 0);
3632         }
3633     }
3634   else
3635     {
3636       item->area.width = 0;
3637       for (i = 0; i < icon_view->priv->n_cells; ++i)
3638         {
3639           if (icon_view->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
3640             item->area.width += max_width[i] + (i > 0 ? spacing : 0);
3641           else
3642             item->area.width = MAX (item->area.width, max_width[i]);
3643         }
3644     }
3645 
3646   cell_area.x = item->area.x;
3647   cell_area.y = item->area.y;
3648 
3649   for (k = 0; k < 2; ++k)
3650     {
3651       for (lp = icon_view->priv->cell_list, i = 0; lp != NULL; lp = lp->next, ++i)
3652         {
3653           info = EXO_ICON_VIEW_CELL_INFO (lp->data);
3654           if (G_UNLIKELY (!gtk_cell_renderer_get_visible (info->cell) || info->pack == (k ? GTK_PACK_START : GTK_PACK_END)))
3655             continue;
3656 
3657           gtk_cell_renderer_get_alignment (info->cell, &cell_xalign, &cell_yalign);
3658           gtk_cell_renderer_get_padding (info->cell, &cell_xpad, &cell_ypad);
3659 
3660           if (icon_view->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
3661             {
3662               cell_area.width = item->box[info->position].width;
3663               cell_area.height = item->area.height;
3664             }
3665           else
3666             {
3667               cell_area.width = item->area.width;
3668               cell_area.height = max_height[i];
3669             }
3670 
3671           box = item->box + info->position;
3672           box->x = cell_area.x + cell_xalign * (cell_area.width - box->width - (2 * cell_xpad));
3673           box->x = MAX (box->x, 0);
3674           box->y = cell_area.y + cell_yalign * (cell_area.height - box->height - (2 * cell_ypad));
3675           box->y = MAX (box->y, 0);
3676 
3677           if (icon_view->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
3678             {
3679               item->before[info->position] = item->box[info->position].x - cell_area.x;
3680               item->after[info->position] = cell_area.width - item->box[info->position].width - item->before[info->position];
3681               cell_area.x += cell_area.width + spacing;
3682             }
3683           else
3684             {
3685               if (item->box[info->position].width > item->area.width)
3686                 {
3687                   item->area.width = item->box[info->position].width;
3688                   cell_area.width = item->area.width;
3689                 }
3690               item->before[info->position] = item->box[info->position].y - cell_area.y;
3691               item->after[info->position] = cell_area.height - item->box[info->position].height - item->before[info->position];
3692               cell_area.y += cell_area.height + spacing;
3693             }
3694         }
3695     }
3696 
3697   if (G_UNLIKELY (rtl && icon_view->priv->orientation == GTK_ORIENTATION_HORIZONTAL))
3698     {
3699       for (i = 0; i < icon_view->priv->n_cells; i++)
3700         item->box[i].x = item->area.x + item->area.width - (item->box[i].x + item->box[i].width - item->area.x);
3701     }
3702 }
3703 
3704 
3705 
3706 static void
exo_icon_view_invalidate_sizes(ExoIconView * icon_view)3707 exo_icon_view_invalidate_sizes (ExoIconView *icon_view)
3708 {
3709   GList *lp;
3710 
3711   for (lp = icon_view->priv->items; lp != NULL; lp = lp->next)
3712     EXO_ICON_VIEW_ITEM (lp->data)->area.width = -1;
3713   exo_icon_view_queue_layout (icon_view);
3714 }
3715 
3716 
3717 
3718 static void
exo_icon_view_paint_item(ExoIconView * icon_view,ExoIconViewItem * item,cairo_t * cr,gint x,gint y,gboolean draw_focus)3719 exo_icon_view_paint_item (ExoIconView     *icon_view,
3720                           ExoIconViewItem *item,
3721                           cairo_t         *cr,
3722                           gint             x,
3723                           gint             y,
3724                           gboolean         draw_focus)
3725 {
3726   GtkCellRendererState flags = 0;
3727   ExoIconViewCellInfo *info;
3728   GtkStateFlags        state;
3729   GdkRectangle         cell_area;
3730   GdkRectangle         aligned_area;
3731   GtkStyleContext     *style_context;
3732   GList               *lp;
3733 
3734   if (G_UNLIKELY (icon_view->priv->model == NULL))
3735     return;
3736 
3737   exo_icon_view_set_cell_data (icon_view, item);
3738 
3739   style_context = gtk_widget_get_style_context (GTK_WIDGET (icon_view));
3740   state = gtk_widget_get_state_flags (GTK_WIDGET (icon_view));
3741 
3742   gtk_style_context_save (style_context);
3743   gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_CELL);
3744 
3745   state &= ~(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_PRELIGHT);
3746 
3747   if (G_UNLIKELY (EXO_ICON_VIEW_FLAG_SET (icon_view, EXO_ICON_VIEW_DRAW_KEYFOCUS)
3748     && (state & GTK_STATE_FLAG_FOCUSED) && item == icon_view->priv->cursor_item))
3749     {
3750       flags |= GTK_CELL_RENDERER_FOCUSED;
3751     }
3752 
3753   if (G_UNLIKELY (item->selected))
3754     {
3755       state |= GTK_STATE_FLAG_SELECTED;
3756       flags |= GTK_CELL_RENDERER_SELECTED;
3757     }
3758 
3759   if (G_UNLIKELY (icon_view->priv->prelit_item == item))
3760     {
3761       state |= GTK_STATE_FLAG_PRELIGHT;
3762       flags |= GTK_CELL_RENDERER_PRELIT;
3763     }
3764 
3765   gtk_style_context_set_state (style_context, state);
3766 
3767   for (lp = icon_view->priv->cell_list; lp != NULL; lp = lp->next)
3768     {
3769       info = EXO_ICON_VIEW_CELL_INFO (lp->data);
3770 
3771       cairo_save (cr);
3772 
3773       if (G_UNLIKELY (!gtk_cell_renderer_get_visible (info->cell)))
3774         continue;
3775 
3776       exo_icon_view_get_cell_area (icon_view, item, info, &cell_area);
3777 
3778       cell_area.x = x - item->area.x + cell_area.x;
3779       cell_area.y = y - item->area.y + cell_area.y;
3780 
3781       /* FIXME: this is bad CSS usage */
3782       if (info->is_text)
3783         {
3784           gtk_cell_renderer_get_aligned_area (info->cell,
3785                                               GTK_WIDGET (icon_view),
3786                                               flags,
3787                                               &cell_area,
3788                                               &aligned_area);
3789 
3790           gtk_render_background (style_context, cr,
3791                                  aligned_area.x, aligned_area.y,
3792                                  aligned_area.width, aligned_area.height);
3793 
3794           gtk_render_frame (style_context, cr,
3795                             aligned_area.x, aligned_area.y,
3796                             aligned_area.width, aligned_area.height);
3797 
3798           /* draw outline if focused */
3799           if (flags & GTK_CELL_RENDERER_FOCUSED)
3800             {
3801               gtk_render_focus (style_context, cr,
3802                                 aligned_area.x, aligned_area.y,
3803                                 aligned_area.width, aligned_area.height);
3804             }
3805         }
3806 
3807       gtk_cell_renderer_render (info->cell,
3808                                 cr,
3809                                 GTK_WIDGET (icon_view),
3810                                 &cell_area, &cell_area, flags);
3811 
3812       cairo_restore (cr);
3813     }
3814 
3815   gtk_style_context_restore (style_context);
3816 }
3817 
3818 
3819 
3820 static void
exo_icon_view_queue_draw_item(ExoIconView * icon_view,ExoIconViewItem * item)3821 exo_icon_view_queue_draw_item (ExoIconView     *icon_view,
3822                                ExoIconViewItem *item)
3823 {
3824   GdkRectangle rect;
3825   gint         focus_width;
3826 
3827   gtk_widget_style_get (GTK_WIDGET (icon_view),
3828                         "focus-line-width", &focus_width,
3829                         NULL);
3830 
3831   rect.x = item->area.x - focus_width;
3832   rect.y = item->area.y - focus_width;
3833   rect.width = item->area.width + 2 * focus_width;
3834   rect.height = item->area.height + 2 * focus_width;
3835 
3836   if (icon_view->priv->bin_window)
3837     gdk_window_invalidate_rect (icon_view->priv->bin_window, &rect, TRUE);
3838 }
3839 
3840 
3841 
3842 static gboolean
layout_callback(gpointer user_data)3843 layout_callback (gpointer user_data)
3844 {
3845   ExoIconView *icon_view = EXO_ICON_VIEW (user_data);
3846 
3847   exo_icon_view_layout (icon_view);
3848 
3849   return FALSE;
3850 }
3851 
3852 
3853 
3854 static void
layout_destroy(gpointer user_data)3855 layout_destroy (gpointer user_data)
3856 {
3857   EXO_ICON_VIEW (user_data)->priv->layout_idle_id = 0;
3858 }
3859 
3860 
3861 
3862 static void
exo_icon_view_queue_layout(ExoIconView * icon_view)3863 exo_icon_view_queue_layout (ExoIconView *icon_view)
3864 {
3865   if (G_UNLIKELY (icon_view->priv->layout_idle_id == 0))
3866     icon_view->priv->layout_idle_id = gdk_threads_add_idle_full (G_PRIORITY_DEFAULT_IDLE, layout_callback, icon_view, layout_destroy);
3867 }
3868 
3869 
3870 
3871 static void
exo_icon_view_set_cursor_item(ExoIconView * icon_view,ExoIconViewItem * item,gint cursor_cell)3872 exo_icon_view_set_cursor_item (ExoIconView     *icon_view,
3873                                ExoIconViewItem *item,
3874                                gint             cursor_cell)
3875 {
3876   if (icon_view->priv->cursor_item == item &&
3877       (cursor_cell < 0 || cursor_cell == icon_view->priv->cursor_cell))
3878     return;
3879 
3880   if (icon_view->priv->cursor_item != NULL)
3881     exo_icon_view_queue_draw_item (icon_view, icon_view->priv->cursor_item);
3882 
3883   icon_view->priv->cursor_item = item;
3884   if (cursor_cell >= 0)
3885     icon_view->priv->cursor_cell = cursor_cell;
3886 
3887   exo_icon_view_queue_draw_item (icon_view, item);
3888 }
3889 
3890 
3891 
3892 static ExoIconViewItem*
exo_icon_view_get_item_at_coords(const ExoIconView * icon_view,gint x,gint y,gboolean only_in_cell,ExoIconViewCellInfo ** cell_at_pos)3893 exo_icon_view_get_item_at_coords (const ExoIconView    *icon_view,
3894                                   gint                  x,
3895                                   gint                  y,
3896                                   gboolean              only_in_cell,
3897                                   ExoIconViewCellInfo **cell_at_pos)
3898 {
3899   const ExoIconViewPrivate *priv = icon_view->priv;
3900   ExoIconViewCellInfo      *info;
3901   ExoIconViewItem          *item;
3902   GdkRectangle              box;
3903   const GList              *items;
3904   const GList              *lp;
3905 
3906   for (items = priv->items; items != NULL; items = items->next)
3907     {
3908       item = items->data;
3909       if (x >= item->area.x - priv->row_spacing / 2 && x <= item->area.x + item->area.width + priv->row_spacing / 2 &&
3910           y >= item->area.y - priv->column_spacing / 2 && y <= item->area.y + item->area.height + priv->column_spacing / 2)
3911         {
3912           if (only_in_cell || cell_at_pos)
3913             {
3914               exo_icon_view_set_cell_data (icon_view, item);
3915               for (lp = priv->cell_list; lp != NULL; lp = lp->next)
3916                 {
3917                   /* check if the cell is visible */
3918                   info = (ExoIconViewCellInfo *) lp->data;
3919                   if (!gtk_cell_renderer_get_visible (info->cell))
3920                     continue;
3921 
3922                   box = item->box[info->position];
3923                   if ((x >= box.x && x <= box.x + box.width &&
3924                        y >= box.y && y <= box.y + box.height))
3925                     {
3926                       if (cell_at_pos != NULL)
3927                         *cell_at_pos = info;
3928 
3929                       return item;
3930                     }
3931                 }
3932 
3933               if (only_in_cell)
3934                 return NULL;
3935 
3936               if (cell_at_pos != NULL)
3937                 *cell_at_pos = NULL;
3938             }
3939 
3940           return item;
3941         }
3942     }
3943 
3944   return NULL;
3945 }
3946 
3947 
3948 
3949 static void
exo_icon_view_select_item(ExoIconView * icon_view,ExoIconViewItem * item)3950 exo_icon_view_select_item (ExoIconView      *icon_view,
3951                            ExoIconViewItem  *item)
3952 {
3953   if (item->selected || icon_view->priv->selection_mode == GTK_SELECTION_NONE)
3954     return;
3955   else if (icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
3956     exo_icon_view_unselect_all_internal (icon_view);
3957 
3958   item->selected = TRUE;
3959 
3960   exo_icon_view_queue_draw_item (icon_view, item);
3961 
3962   g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
3963 }
3964 
3965 
3966 
3967 static void
exo_icon_view_unselect_item(ExoIconView * icon_view,ExoIconViewItem * item)3968 exo_icon_view_unselect_item (ExoIconView      *icon_view,
3969                              ExoIconViewItem  *item)
3970 {
3971   if (!item->selected)
3972     return;
3973 
3974   if (icon_view->priv->selection_mode == GTK_SELECTION_NONE ||
3975       icon_view->priv->selection_mode == GTK_SELECTION_BROWSE)
3976     return;
3977 
3978   item->selected = FALSE;
3979 
3980   g_signal_emit (G_OBJECT (icon_view), icon_view_signals[SELECTION_CHANGED], 0);
3981 
3982   exo_icon_view_queue_draw_item (icon_view, item);
3983 }
3984 
3985 
3986 
3987 static void
exo_icon_view_row_changed(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,ExoIconView * icon_view)3988 exo_icon_view_row_changed (GtkTreeModel *model,
3989                            GtkTreePath  *path,
3990                            GtkTreeIter  *iter,
3991                            ExoIconView  *icon_view)
3992 {
3993   ExoIconViewItem *item;
3994 
3995   item = g_list_nth_data (icon_view->priv->items, gtk_tree_path_get_indices(path)[0]);
3996 
3997   /* stop editing this item */
3998   if (G_UNLIKELY (item == icon_view->priv->edited_item))
3999     exo_icon_view_stop_editing (icon_view, TRUE);
4000 
4001   /* emit "selection-changed" if the item is selected */
4002   if (G_UNLIKELY (item->selected))
4003     g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
4004 
4005   /* recalculate layout (a value of -1 for width
4006    * indicates that the item needs to be layouted).
4007    */
4008   item->area.width = -1;
4009   exo_icon_view_queue_layout (icon_view);
4010 }
4011 
4012 
4013 
4014 static void
exo_icon_view_row_inserted(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,ExoIconView * icon_view)4015 exo_icon_view_row_inserted (GtkTreeModel *model,
4016                             GtkTreePath  *path,
4017                             GtkTreeIter  *iter,
4018                             ExoIconView  *icon_view)
4019 {
4020   ExoIconViewItem *item;
4021   gint             idx;
4022 
4023   idx = gtk_tree_path_get_indices (path)[0];
4024 
4025   /* allocate the new item */
4026   item = g_slice_new0 (ExoIconViewItem);
4027   item->iter = *iter;
4028   item->area.width = -1;
4029   icon_view->priv->items = g_list_insert (icon_view->priv->items, item, idx);
4030 
4031   /* recalculate the layout */
4032   exo_icon_view_queue_layout (icon_view);
4033 }
4034 
4035 
4036 
4037 static void
exo_icon_view_row_deleted(GtkTreeModel * model,GtkTreePath * path,ExoIconView * icon_view)4038 exo_icon_view_row_deleted (GtkTreeModel *model,
4039                            GtkTreePath  *path,
4040                            ExoIconView  *icon_view)
4041 {
4042   ExoIconViewItem *item;
4043   gboolean         changed = FALSE;
4044   GList           *list;
4045 
4046   /* determine the position and the item for the path */
4047   list = g_list_nth (icon_view->priv->items, gtk_tree_path_get_indices (path)[0]);
4048   item = list->data;
4049 
4050   if (G_UNLIKELY (item == icon_view->priv->edited_item))
4051     exo_icon_view_stop_editing (icon_view, TRUE);
4052 
4053   /* use the next item (if any) as anchor, else use prev, otherwise reset anchor */
4054   if (G_UNLIKELY (item == icon_view->priv->anchor_item))
4055     icon_view->priv->anchor_item = (list->next != NULL) ? list->next->data : ((list->prev != NULL) ? list->prev->data : NULL);
4056 
4057   /* use the next item (if any) as cursor, else use prev, otherwise reset cursor */
4058   if (G_UNLIKELY (item == icon_view->priv->cursor_item))
4059     icon_view->priv->cursor_item = (list->next != NULL) ? list->next->data : ((list->prev != NULL) ? list->prev->data : NULL);
4060 
4061   if (G_UNLIKELY (item == icon_view->priv->prelit_item))
4062     {
4063       /* reset the prelit item */
4064       icon_view->priv->prelit_item = NULL;
4065 
4066       /* cancel any pending single click timer */
4067       if (G_UNLIKELY (icon_view->priv->single_click_timeout_id != 0))
4068         g_source_remove (icon_view->priv->single_click_timeout_id);
4069 
4070       /* in single click mode, we also reset the cursor when realized */
4071       if (G_UNLIKELY (icon_view->priv->single_click && gtk_widget_get_realized (GTK_WIDGET (icon_view))))
4072         gdk_window_set_cursor (icon_view->priv->bin_window, NULL);
4073     }
4074 
4075   /* check if the selection changed */
4076   if (G_UNLIKELY (item->selected))
4077     changed = TRUE;
4078 
4079   /* release the item resources */
4080   g_free (item->box);
4081 
4082   /* drop the item from the list */
4083   icon_view->priv->items = g_list_delete_link (icon_view->priv->items, list);
4084 
4085   /* release the item */
4086   g_slice_free (ExoIconViewItem, item);
4087 
4088   /* recalculate the layout */
4089   exo_icon_view_queue_layout (icon_view);
4090 
4091   /* if we removed a previous selected item, we need
4092    * to tell others that we have a new selection.
4093    */
4094   if (G_UNLIKELY (changed))
4095     g_signal_emit (G_OBJECT (icon_view), icon_view_signals[SELECTION_CHANGED], 0);
4096 }
4097 
4098 
4099 
4100 static void
exo_icon_view_rows_reordered(GtkTreeModel * model,GtkTreePath * parent,GtkTreeIter * iter,gint * new_order,ExoIconView * icon_view)4101 exo_icon_view_rows_reordered (GtkTreeModel *model,
4102                               GtkTreePath  *parent,
4103                               GtkTreeIter  *iter,
4104                               gint         *new_order,
4105                               ExoIconView  *icon_view)
4106 {
4107   GList          **list_array;
4108   GList           *list;
4109   gint            *order;
4110   gint              length;
4111   gint              i;
4112 
4113   /* cancel any editing attempt */
4114   exo_icon_view_stop_editing (icon_view, TRUE);
4115 
4116   /* determine the number of items to reorder */
4117   length = gtk_tree_model_iter_n_children (model, NULL);
4118   if (G_UNLIKELY (length == 0))
4119     return;
4120 
4121   list_array = g_newa (GList *, length);
4122   order = g_newa (gint, length);
4123 
4124   for (i = 0; i < length; i++)
4125     order[new_order[i]] = i;
4126 
4127   for (i = 0, list = icon_view->priv->items; list != NULL; list = list->next, i++)
4128     list_array[order[i]] = list;
4129 
4130   /* hook up the first item */
4131   icon_view->priv->items = list_array[0];
4132   list_array[0]->prev = NULL;
4133 
4134   /* hook up the remaining items */
4135   for (i = 1; i < length; ++i)
4136     {
4137       list_array[i - 1]->next = list_array[i];
4138       list_array[i]->prev = list_array[i - 1];
4139     }
4140 
4141   /* hook up the last item */
4142   list_array[length - 1]->next = NULL;
4143 
4144   exo_icon_view_queue_layout (icon_view);
4145 }
4146 
4147 
4148 
4149 static void
exo_icon_view_add_move_binding(GtkBindingSet * binding_set,guint keyval,guint modmask,GtkMovementStep step,gint count)4150 exo_icon_view_add_move_binding (GtkBindingSet  *binding_set,
4151                                 guint           keyval,
4152                                 guint           modmask,
4153                                 GtkMovementStep step,
4154                                 gint            count)
4155 {
4156   gtk_binding_entry_add_signal (binding_set, keyval, modmask, "move-cursor", 2, G_TYPE_ENUM, step, G_TYPE_INT, count);
4157 
4158   /* skip shift+n and shift+p because this blocks type-ahead search.
4159    * see https://bugzilla.xfce.org/show_bug.cgi?id=4633
4160    */
4161   if (G_LIKELY (keyval != GDK_KEY_p && keyval != GDK_KEY_n))
4162     gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK, "move-cursor", 2, G_TYPE_ENUM, step, G_TYPE_INT, count);
4163 
4164   if ((modmask & GDK_CONTROL_MASK) != GDK_CONTROL_MASK)
4165     {
4166       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);
4167       gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK, "move-cursor", 2, G_TYPE_ENUM, step, G_TYPE_INT, count);
4168     }
4169 }
4170 
4171 
4172 
4173 static gboolean
exo_icon_view_real_move_cursor(ExoIconView * icon_view,GtkMovementStep step,gint count)4174 exo_icon_view_real_move_cursor (ExoIconView     *icon_view,
4175                                 GtkMovementStep  step,
4176                                 gint             count)
4177 {
4178   GdkModifierType state;
4179 
4180   _exo_return_val_if_fail (EXO_ICON_VIEW (icon_view), FALSE);
4181   _exo_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS ||
4182                            step == GTK_MOVEMENT_VISUAL_POSITIONS ||
4183                            step == GTK_MOVEMENT_DISPLAY_LINES ||
4184                            step == GTK_MOVEMENT_PAGES ||
4185                            step == GTK_MOVEMENT_BUFFER_ENDS, FALSE);
4186 
4187   if (!gtk_widget_has_focus (GTK_WIDGET (icon_view)))
4188     return FALSE;
4189 
4190   exo_icon_view_stop_editing (icon_view, FALSE);
4191   EXO_ICON_VIEW_SET_FLAG (icon_view, EXO_ICON_VIEW_DRAW_KEYFOCUS);
4192   gtk_widget_grab_focus (GTK_WIDGET (icon_view));
4193 
4194   if (gtk_get_current_event_state (&state))
4195     {
4196       if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
4197         icon_view->priv->ctrl_pressed = TRUE;
4198       if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
4199         icon_view->priv->shift_pressed = TRUE;
4200     }
4201   /* else we assume not pressed */
4202 
4203   switch (step)
4204     {
4205     case GTK_MOVEMENT_LOGICAL_POSITIONS:
4206     case GTK_MOVEMENT_VISUAL_POSITIONS:
4207       exo_icon_view_move_cursor_left_right (icon_view, count);
4208       break;
4209     case GTK_MOVEMENT_DISPLAY_LINES:
4210       exo_icon_view_move_cursor_up_down (icon_view, count);
4211       break;
4212     case GTK_MOVEMENT_PAGES:
4213       exo_icon_view_move_cursor_page_up_down (icon_view, count);
4214       break;
4215     case GTK_MOVEMENT_BUFFER_ENDS:
4216       exo_icon_view_move_cursor_start_end (icon_view, count);
4217       break;
4218     default:
4219       g_assert_not_reached ();
4220     }
4221 
4222   icon_view->priv->ctrl_pressed = FALSE;
4223   icon_view->priv->shift_pressed = FALSE;
4224 
4225   return TRUE;
4226 }
4227 
4228 
4229 
4230 static gint
find_cell(ExoIconView * icon_view,ExoIconViewItem * item,gint cell,GtkOrientation orientation,gint step,gint * count)4231 find_cell (ExoIconView     *icon_view,
4232            ExoIconViewItem *item,
4233            gint             cell,
4234            GtkOrientation   orientation,
4235            gint             step,
4236            gint            *count)
4237 {
4238   gint n_focusable;
4239   gint *focusable;
4240   gint first_text;
4241   gint current;
4242   gint i, k;
4243   GList *l;
4244   GtkCellRendererMode mode;
4245 
4246   if (icon_view->priv->orientation != orientation)
4247     return cell;
4248 
4249   exo_icon_view_set_cell_data (icon_view, item);
4250 
4251   focusable = g_new0 (gint, icon_view->priv->n_cells);
4252   n_focusable = 0;
4253 
4254   first_text = 0;
4255   current = 0;
4256   for (k = 0; k < 2; k++)
4257     for (l = icon_view->priv->cell_list, i = 0; l; l = l->next, i++)
4258       {
4259         ExoIconViewCellInfo *info = (ExoIconViewCellInfo *)l->data;
4260 
4261         if (info->pack == (k ? GTK_PACK_START : GTK_PACK_END))
4262           continue;
4263 
4264         if (!gtk_cell_renderer_get_visible (info->cell))
4265           continue;
4266 
4267         if (GTK_IS_CELL_RENDERER_TEXT (info->cell))
4268           first_text = i;
4269 
4270         g_object_get (info->cell, "mode", &mode, NULL);
4271         if (mode != GTK_CELL_RENDERER_MODE_INERT)
4272           {
4273             if (cell == i)
4274               current = n_focusable;
4275 
4276             focusable[n_focusable] = i;
4277 
4278             n_focusable++;
4279           }
4280       }
4281 
4282   if (n_focusable == 0)
4283     focusable[n_focusable++] = first_text;
4284 
4285   if (cell < 0)
4286     {
4287       current = step > 0 ? 0 : n_focusable - 1;
4288     }
4289 
4290   if (current + *count < 0)
4291     {
4292       cell = -1;
4293       *count = current + *count;
4294     }
4295   else if (current + *count > n_focusable - 1)
4296     {
4297       cell = -1;
4298       *count = current + *count - (n_focusable - 1);
4299     }
4300   else
4301     {
4302       cell = focusable[current + *count];
4303       *count = 0;
4304     }
4305 
4306   g_free (focusable);
4307 
4308   return cell;
4309 }
4310 
4311 
4312 
4313 static ExoIconViewItem *
find_item_page_up_down(ExoIconView * icon_view,ExoIconViewItem * current,gint count)4314 find_item_page_up_down (ExoIconView     *icon_view,
4315                         ExoIconViewItem *current,
4316                         gint             count)
4317 {
4318   GList *item = g_list_find (icon_view->priv->items, current);
4319   GList *next;
4320   gint   col = current->col;
4321   gint   y = current->area.y + count * gtk_adjustment_get_page_size (icon_view->priv->vadjustment);
4322 
4323   if (count > 0)
4324     {
4325       for (; item != NULL; item = item->next)
4326         {
4327           for (next = item->next; next; next = next->next)
4328             if (EXO_ICON_VIEW_ITEM (next->data)->col == col)
4329               break;
4330 
4331           if (next == NULL || EXO_ICON_VIEW_ITEM (next->data)->area.y > y)
4332             break;
4333         }
4334     }
4335   else
4336     {
4337       for (; item != NULL; item = item->prev)
4338         {
4339           for (next = item->prev; next; next = next->prev)
4340             if (EXO_ICON_VIEW_ITEM (next->data)->col == col)
4341               break;
4342 
4343           if (next == NULL || EXO_ICON_VIEW_ITEM (next->data)->area.y < y)
4344             break;
4345         }
4346     }
4347 
4348   return (item != NULL) ? item->data : NULL;
4349 }
4350 
4351 
4352 
4353 static gboolean
exo_icon_view_select_all_between(ExoIconView * icon_view,ExoIconViewItem * anchor,ExoIconViewItem * cursor)4354 exo_icon_view_select_all_between (ExoIconView     *icon_view,
4355                                   ExoIconViewItem *anchor,
4356                                   ExoIconViewItem *cursor)
4357 {
4358   GList *items;
4359   ExoIconViewItem *item, *last = NULL;
4360   gboolean dirty = FALSE;
4361 
4362   for (items = icon_view->priv->items; items; items = items->next)
4363     {
4364       item = items->data;
4365 
4366       if (item == anchor)
4367         {
4368           last = cursor;
4369           break;
4370         }
4371       else if (item == cursor)
4372         {
4373           last = anchor;
4374           break;
4375         }
4376     }
4377 
4378   for (; items; items = items->next)
4379     {
4380       item = items->data;
4381 
4382       if (!item->selected)
4383         dirty = TRUE;
4384 
4385       item->selected = TRUE;
4386 
4387       exo_icon_view_queue_draw_item (icon_view, item);
4388 
4389       if (item == last)
4390         break;
4391     }
4392 
4393   return dirty;
4394 }
4395 
4396 
4397 
4398 static void
exo_icon_view_move_cursor_up_down(ExoIconView * icon_view,gint count)4399 exo_icon_view_move_cursor_up_down (ExoIconView *icon_view,
4400                                    gint         count)
4401 {
4402   ExoIconViewItem  *item;
4403   gboolean          dirty = FALSE;
4404   GList            *list;
4405   gint              cell = -1;
4406   gint              step;
4407   GtkDirectionType  direction;
4408   GtkWidget        *toplevel;
4409 
4410   if (!gtk_widget_has_focus (GTK_WIDGET (icon_view)))
4411     return;
4412 
4413   direction = count < 0 ? GTK_DIR_UP : GTK_DIR_DOWN;
4414 
4415   if (!icon_view->priv->cursor_item)
4416     {
4417       if (count > 0)
4418         list = icon_view->priv->items;
4419       else
4420         list = g_list_last (icon_view->priv->items);
4421 
4422       item = list ? list->data : NULL;
4423     }
4424   else
4425     {
4426       item = icon_view->priv->cursor_item;
4427       cell = icon_view->priv->cursor_cell;
4428       step = count > 0 ? 1 : -1;
4429       while (item)
4430         {
4431           cell = find_cell (icon_view, item, cell,
4432                             GTK_ORIENTATION_VERTICAL,
4433                             step, &count);
4434           if (count == 0)
4435             break;
4436 
4437           /* determine the list position for the item */
4438           list = g_list_find (icon_view->priv->items, item);
4439 
4440           if (G_LIKELY (icon_view->priv->layout_mode == EXO_ICON_VIEW_LAYOUT_ROWS))
4441             {
4442               /* determine the item in the next/prev row */
4443               if (step > 0)
4444                 {
4445                   for (list = list->next; list != NULL; list = list->next)
4446                     if (EXO_ICON_VIEW_ITEM (list->data)->row == item->row + step
4447                         && EXO_ICON_VIEW_ITEM (list->data)->col == item->col)
4448                       break;
4449                  }
4450               else
4451                 {
4452                   for (list = list->prev; list != NULL; list = list->prev)
4453                     if (EXO_ICON_VIEW_ITEM (list->data)->row == item->row + step
4454                         && EXO_ICON_VIEW_ITEM (list->data)->col == item->col)
4455                       break;
4456                 }
4457             }
4458           else
4459             {
4460               list = (step > 0) ? list->next : list->prev;
4461             }
4462 
4463           /* check if we found a matching item */
4464           item = (list != NULL) ? list->data : NULL;
4465 
4466           count = count - step;
4467         }
4468     }
4469 
4470   if (!item)
4471     {
4472       if (!gtk_widget_keynav_failed (GTK_WIDGET (icon_view), direction))
4473         {
4474           toplevel = gtk_widget_get_toplevel (GTK_WIDGET (icon_view));
4475           if (toplevel != NULL)
4476             {
4477               gtk_widget_child_focus (toplevel,
4478                                       direction == GTK_DIR_UP ?
4479                                           GTK_DIR_TAB_BACKWARD :
4480                                           GTK_DIR_TAB_FORWARD);
4481             }
4482 
4483           return;
4484         }
4485 
4486       /* check if we should select the cursor item */
4487       item = icon_view->priv->cursor_item;
4488       if (!item || item->selected)
4489         return;
4490     }
4491 
4492   if (icon_view->priv->ctrl_pressed ||
4493       !icon_view->priv->shift_pressed ||
4494       !icon_view->priv->anchor_item ||
4495       icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
4496     icon_view->priv->anchor_item = item;
4497 
4498   exo_icon_view_set_cursor_item (icon_view, item, cell);
4499 
4500   if (!icon_view->priv->ctrl_pressed &&
4501       icon_view->priv->selection_mode != GTK_SELECTION_NONE)
4502     {
4503       dirty = exo_icon_view_unselect_all_internal (icon_view);
4504       dirty = exo_icon_view_select_all_between (icon_view,
4505                                                 icon_view->priv->anchor_item,
4506                                                 item) || dirty;
4507     }
4508 
4509   exo_icon_view_scroll_to_item (icon_view, item);
4510 
4511   if (dirty)
4512     g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
4513 }
4514 
4515 
4516 
4517 static void
exo_icon_view_move_cursor_page_up_down(ExoIconView * icon_view,gint count)4518 exo_icon_view_move_cursor_page_up_down (ExoIconView *icon_view,
4519                                         gint         count)
4520 {
4521   ExoIconViewItem *item;
4522   gboolean dirty = FALSE;
4523 
4524   if (!gtk_widget_has_focus (GTK_WIDGET (icon_view)))
4525     return;
4526 
4527   if (!icon_view->priv->cursor_item)
4528     {
4529       GList *list;
4530 
4531       if (count > 0)
4532         list = icon_view->priv->items;
4533       else
4534         list = g_list_last (icon_view->priv->items);
4535 
4536       item = list ? list->data : NULL;
4537     }
4538   else
4539     item = find_item_page_up_down (icon_view,
4540                                    icon_view->priv->cursor_item,
4541                                    count);
4542 
4543   if (!item)
4544     return;
4545 
4546   if (icon_view->priv->ctrl_pressed ||
4547       !icon_view->priv->shift_pressed ||
4548       !icon_view->priv->anchor_item ||
4549       icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
4550     icon_view->priv->anchor_item = item;
4551 
4552   exo_icon_view_set_cursor_item (icon_view, item, -1);
4553 
4554   if (!icon_view->priv->ctrl_pressed &&
4555       icon_view->priv->selection_mode != GTK_SELECTION_NONE)
4556     {
4557       dirty = exo_icon_view_unselect_all_internal (icon_view);
4558       dirty = exo_icon_view_select_all_between (icon_view,
4559                                                 icon_view->priv->anchor_item,
4560                                                 item) || dirty;
4561     }
4562 
4563   exo_icon_view_scroll_to_item (icon_view, item);
4564 
4565   if (dirty)
4566     g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
4567 }
4568 
4569 
4570 
4571 static void
exo_icon_view_move_cursor_left_right(ExoIconView * icon_view,gint count)4572 exo_icon_view_move_cursor_left_right (ExoIconView *icon_view,
4573                                       gint         count)
4574 {
4575   ExoIconViewItem  *item;
4576   gboolean          dirty = FALSE;
4577   GList            *list;
4578   gint              cell = -1;
4579   gint              step;
4580   GtkDirectionType  direction;
4581   GtkWidget        *toplevel;
4582 
4583   if (!gtk_widget_has_focus (GTK_WIDGET (icon_view)))
4584     return;
4585 
4586   if (gtk_widget_get_direction (GTK_WIDGET (icon_view)) == GTK_TEXT_DIR_RTL)
4587     count *= -1;
4588 
4589   direction = count < 0 ? GTK_DIR_LEFT : GTK_DIR_RIGHT;
4590 
4591   if (!icon_view->priv->cursor_item)
4592     {
4593       if (count > 0)
4594         list = icon_view->priv->items;
4595       else
4596         list = g_list_last (icon_view->priv->items);
4597 
4598       item = list ? list->data : NULL;
4599     }
4600   else
4601     {
4602       item = icon_view->priv->cursor_item;
4603       cell = icon_view->priv->cursor_cell;
4604       step = count > 0 ? 1 : -1;
4605       while (item)
4606         {
4607           cell = find_cell (icon_view, item, cell,
4608                             GTK_ORIENTATION_HORIZONTAL,
4609                             step, &count);
4610           if (count == 0)
4611             break;
4612 
4613           /* lookup the item in the list */
4614           list = g_list_find (icon_view->priv->items, item);
4615 
4616           if (G_LIKELY (icon_view->priv->layout_mode == EXO_ICON_VIEW_LAYOUT_ROWS))
4617             {
4618               /* determine the next/prev list item depending on step,
4619                * support wrapping around on the edges, as requested
4620                * in https://bugzilla.xfce.org/show_bug.cgi?id=1623.
4621                */
4622               list = (step > 0) ? list->next : list->prev;
4623             }
4624           else
4625             {
4626               /* determine the item in the next/prev row */
4627               if (step > 0)
4628                 {
4629                   for (list = list->next; list != NULL; list = list->next)
4630                     if (EXO_ICON_VIEW_ITEM (list->data)->col == item->col + step
4631                         && EXO_ICON_VIEW_ITEM (list->data)->row == item->row)
4632                       break;
4633                  }
4634               else
4635                 {
4636                   for (list = list->prev; list != NULL; list = list->prev)
4637                     if (EXO_ICON_VIEW_ITEM (list->data)->col == item->col + step
4638                         && EXO_ICON_VIEW_ITEM (list->data)->row == item->row)
4639                       break;
4640                 }
4641             }
4642 
4643           /* determine the item for the list position (if any) */
4644           item = (list != NULL) ? list->data : NULL;
4645 
4646           count = count - step;
4647         }
4648     }
4649 
4650   if (!item)
4651     {
4652       if (!gtk_widget_keynav_failed (GTK_WIDGET (icon_view), direction))
4653         {
4654           toplevel = gtk_widget_get_toplevel (GTK_WIDGET (icon_view));
4655           if (toplevel != NULL)
4656             {
4657               gtk_widget_child_focus (toplevel,
4658                                       direction == GTK_DIR_LEFT ?
4659                                           GTK_DIR_TAB_BACKWARD :
4660                                           GTK_DIR_TAB_FORWARD);
4661             }
4662 
4663           return;
4664         }
4665 
4666       /* check if we should select the cursor item */
4667       item = icon_view->priv->cursor_item;
4668       if (!item || item->selected)
4669         return;
4670     }
4671 
4672   if (icon_view->priv->ctrl_pressed ||
4673       !icon_view->priv->shift_pressed ||
4674       !icon_view->priv->anchor_item ||
4675       icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
4676     icon_view->priv->anchor_item = item;
4677 
4678   exo_icon_view_set_cursor_item (icon_view, item, cell);
4679 
4680   if (!icon_view->priv->ctrl_pressed &&
4681       icon_view->priv->selection_mode != GTK_SELECTION_NONE)
4682     {
4683       dirty = exo_icon_view_unselect_all_internal (icon_view);
4684       dirty = exo_icon_view_select_all_between (icon_view,
4685                                                 icon_view->priv->anchor_item,
4686                                                 item) || dirty;
4687     }
4688 
4689   exo_icon_view_scroll_to_item (icon_view, item);
4690 
4691   if (dirty)
4692     g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
4693 }
4694 
4695 
4696 
4697 static void
exo_icon_view_move_cursor_start_end(ExoIconView * icon_view,gint count)4698 exo_icon_view_move_cursor_start_end (ExoIconView *icon_view,
4699                                      gint         count)
4700 {
4701   ExoIconViewItem *item;
4702   gboolean         dirty = FALSE;
4703   GList           *lp;
4704 
4705   if (!gtk_widget_has_focus (GTK_WIDGET (icon_view)))
4706     return;
4707 
4708   lp = (count < 0) ? icon_view->priv->items : g_list_last (icon_view->priv->items);
4709   if (G_UNLIKELY (lp == NULL))
4710     return;
4711 
4712   item = EXO_ICON_VIEW_ITEM (lp->data);
4713   if (icon_view->priv->ctrl_pressed ||
4714       !icon_view->priv->shift_pressed ||
4715       !icon_view->priv->anchor_item ||
4716       icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
4717     icon_view->priv->anchor_item = item;
4718 
4719   exo_icon_view_set_cursor_item (icon_view, item, -1);
4720 
4721   if (!icon_view->priv->ctrl_pressed &&
4722       icon_view->priv->selection_mode != GTK_SELECTION_NONE)
4723     {
4724       dirty = exo_icon_view_unselect_all_internal (icon_view);
4725       dirty = exo_icon_view_select_all_between (icon_view,
4726                                                 icon_view->priv->anchor_item,
4727                                                 item) || dirty;
4728     }
4729 
4730   exo_icon_view_scroll_to_item (icon_view, item);
4731 
4732   if (G_UNLIKELY (dirty))
4733     g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
4734 }
4735 
4736 
4737 
4738 /* Get the actual size needed by an item (as opposed to the size
4739  * allocated based on the largest item in the same row/column).
4740  */
4741 static void
exo_icon_view_get_item_needed_size(ExoIconView * icon_view,ExoIconViewItem * item,gint * width,gint * height)4742 exo_icon_view_get_item_needed_size (ExoIconView     *icon_view,
4743                                     ExoIconViewItem *item,
4744                                     gint            *width,
4745                                     gint            *height)
4746 {
4747   GList               *lp;
4748   ExoIconViewCellInfo *info;
4749 
4750   *width = 0;
4751   *height = 0;
4752 
4753   for (lp = icon_view->priv->cell_list; lp != NULL; lp = lp->next)
4754     {
4755       info = EXO_ICON_VIEW_CELL_INFO (lp->data);
4756       if (G_UNLIKELY (!gtk_cell_renderer_get_visible (info->cell)))
4757         continue;
4758 
4759       if (icon_view->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
4760         {
4761           *width += item->box[info->position].width
4762                   + (info->position > 0 ? icon_view->priv->spacing : 0);
4763           *height = MAX (*height, item->box[info->position].height);
4764         }
4765       else
4766         {
4767           *width = MAX (*width, item->box[info->position].width);
4768           *height += item->box[info->position].height
4769                    + (info->position > 0 ? icon_view->priv->spacing : 0);
4770         }
4771     }
4772 }
4773 
4774 
4775 
4776 static void
exo_icon_view_scroll_to_item(ExoIconView * icon_view,ExoIconViewItem * item)4777 exo_icon_view_scroll_to_item (ExoIconView     *icon_view,
4778                               ExoIconViewItem *item)
4779 {
4780   gint x, y;
4781   gint focus_width;
4782   gint item_width, item_height;
4783   GtkAllocation allocation;
4784   GtkTreePath *path;
4785 
4786   /* Delay scrolling if either not realized or pending layout() */
4787   if (!gtk_widget_get_realized (GTK_WIDGET(icon_view)) || icon_view->priv->layout_idle_id != 0)
4788     {
4789       /* release the previous scroll_to_path reference */
4790       if (G_UNLIKELY (icon_view->priv->scroll_to_path != NULL))
4791         gtk_tree_row_reference_free (icon_view->priv->scroll_to_path);
4792 
4793       /* remember a reference for the new path and settings */
4794 
4795       path = gtk_tree_path_new_from_indices (g_list_index (icon_view->priv->items, item), -1);
4796       icon_view->priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (icon_view), icon_view->priv->model, path);
4797       gtk_tree_path_free (path);
4798 
4799       icon_view->priv->scroll_to_use_align = FALSE;
4800 
4801       return;
4802     }
4803 
4804   gtk_widget_style_get (GTK_WIDGET (icon_view),
4805                         "focus-line-width", &focus_width,
4806                         NULL);
4807 
4808   gdk_window_get_position (icon_view->priv->bin_window, &x, &y);
4809   exo_icon_view_get_item_needed_size (icon_view, item, &item_width, &item_height);
4810   gtk_widget_get_allocation (GTK_WIDGET (icon_view), &allocation);
4811 
4812   /*
4813    * If an item reaches beyond the edges of the view, we scroll just enough
4814    * to make as much of it visible as possible.  This avoids interfering
4815    * with double-click (since the second click will not scroll differently),
4816    * prevents narrow items in wide columns from being scrolled out of view
4817    * when selected, and ensures that items will be brought into view when
4818    * selected even if it was done by a keystroke instead of a mouse click.
4819    * See bugs 1683 and 6014 for some problems seen in the past.
4820    */
4821 
4822   if (y + item->area.y - focus_width < 0)
4823     {
4824       gtk_adjustment_set_value (icon_view->priv->vadjustment,
4825                                 gtk_adjustment_get_value (icon_view->priv->vadjustment) + y + item->area.y - focus_width);
4826     }
4827   else if (y + item->area.y + item_height + focus_width > allocation.height
4828         && y + item->area.y - focus_width > 0)
4829     {
4830       gtk_adjustment_set_value (icon_view->priv->vadjustment,
4831                                 gtk_adjustment_get_value (icon_view->priv->vadjustment)
4832                                 + MIN (y + item->area.y - focus_width,
4833                                        y + item->area.y + item_height + focus_width - allocation.height));
4834     }
4835 
4836   if (x + item->area.x - focus_width < 0)
4837     {
4838       gtk_adjustment_set_value (icon_view->priv->hadjustment,
4839                                 gtk_adjustment_get_value (icon_view->priv->hadjustment) + x + item->area.x - focus_width);
4840     }
4841   else if (x + item->area.x + item_width + focus_width > allocation.width
4842         && x + item->area.x - focus_width > 0)
4843     {
4844       gtk_adjustment_set_value (icon_view->priv->hadjustment,
4845                                 gtk_adjustment_get_value (icon_view->priv->hadjustment)
4846                                 + MIN (x + item->area.x - focus_width,
4847                                        x + item->area.x + item_width + focus_width - allocation.width));
4848     }
4849 }
4850 
4851 
4852 
4853 static ExoIconViewCellInfo *
exo_icon_view_get_cell_info(ExoIconView * icon_view,GtkCellRenderer * renderer)4854 exo_icon_view_get_cell_info (ExoIconView     *icon_view,
4855                              GtkCellRenderer *renderer)
4856 {
4857   GList *lp;
4858 
4859   for (lp = icon_view->priv->cell_list; lp != NULL; lp = lp->next)
4860     if (EXO_ICON_VIEW_CELL_INFO (lp->data)->cell == renderer)
4861       return lp->data;
4862 
4863   return NULL;
4864 }
4865 
4866 
4867 
4868 static void
exo_icon_view_set_cell_data(const ExoIconView * icon_view,ExoIconViewItem * item)4869 exo_icon_view_set_cell_data (const ExoIconView *icon_view,
4870                              ExoIconViewItem   *item)
4871 {
4872   ExoIconViewCellInfo *info;
4873   GtkTreePath         *path;
4874   GtkTreeIter          iter;
4875   GValue               value = {0, };
4876   GSList              *slp;
4877   GList               *lp;
4878 
4879   if (G_UNLIKELY (!EXO_ICON_VIEW_FLAG_SET (icon_view, EXO_ICON_VIEW_ITERS_PERSIST)))
4880     {
4881       path = gtk_tree_path_new_from_indices (g_list_index (icon_view->priv->items, item), -1);
4882       gtk_tree_model_get_iter (icon_view->priv->model, &iter, path);
4883       gtk_tree_path_free (path);
4884     }
4885   else
4886     {
4887       iter = item->iter;
4888     }
4889 
4890   for (lp = icon_view->priv->cell_list; lp != NULL; lp = lp->next)
4891     {
4892       info = EXO_ICON_VIEW_CELL_INFO (lp->data);
4893 
4894       for (slp = info->attributes; slp != NULL && slp->next != NULL; slp = slp->next->next)
4895         {
4896           gtk_tree_model_get_value (icon_view->priv->model, &iter, GPOINTER_TO_INT (slp->next->data), &value);
4897           g_object_set_property (G_OBJECT (info->cell), slp->data, &value);
4898           g_value_unset (&value);
4899         }
4900 
4901       if (G_UNLIKELY (info->func != NULL))
4902         (*info->func) (GTK_CELL_LAYOUT (icon_view), info->cell, icon_view->priv->model, &iter, info->func_data);
4903     }
4904 }
4905 
4906 
4907 
4908 static void
free_cell_attributes(ExoIconViewCellInfo * info)4909 free_cell_attributes (ExoIconViewCellInfo *info)
4910 {
4911   GSList *lp;
4912 
4913   for (lp = info->attributes; lp != NULL && lp->next != NULL; lp = lp->next->next)
4914     g_free (lp->data);
4915   g_slist_free (info->attributes);
4916   info->attributes = NULL;
4917 }
4918 
4919 
4920 
4921 static void
free_cell_info(ExoIconViewCellInfo * info)4922 free_cell_info (ExoIconViewCellInfo *info)
4923 {
4924   if (G_UNLIKELY (info->destroy != NULL))
4925     (*info->destroy) (info->func_data);
4926 
4927   free_cell_attributes (info);
4928   g_object_unref (G_OBJECT (info->cell));
4929   g_slice_free (ExoIconViewCellInfo, info);
4930 }
4931 
4932 
4933 
4934 static void
exo_icon_view_cell_layout_pack_start(GtkCellLayout * layout,GtkCellRenderer * renderer,gboolean expand)4935 exo_icon_view_cell_layout_pack_start (GtkCellLayout   *layout,
4936                                       GtkCellRenderer *renderer,
4937                                       gboolean         expand)
4938 {
4939   ExoIconViewCellInfo *info;
4940   ExoIconView         *icon_view = EXO_ICON_VIEW (layout);
4941 
4942   _exo_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
4943   _exo_return_if_fail (exo_icon_view_get_cell_info (icon_view, renderer) == NULL);
4944 
4945   g_object_ref_sink (renderer);
4946 
4947   info = g_slice_new0 (ExoIconViewCellInfo);
4948   info->cell = renderer;
4949   info->expand = expand ? TRUE : FALSE;
4950   info->pack = GTK_PACK_START;
4951   info->position = icon_view->priv->n_cells;
4952   info->is_text = GTK_IS_CELL_RENDERER_TEXT (renderer);
4953 
4954   icon_view->priv->cell_list = g_list_append (icon_view->priv->cell_list, info);
4955   icon_view->priv->n_cells++;
4956 
4957   exo_icon_view_invalidate_sizes (icon_view);
4958 }
4959 
4960 
4961 
4962 static void
exo_icon_view_cell_layout_pack_end(GtkCellLayout * layout,GtkCellRenderer * renderer,gboolean expand)4963 exo_icon_view_cell_layout_pack_end (GtkCellLayout   *layout,
4964                                     GtkCellRenderer *renderer,
4965                                     gboolean         expand)
4966 {
4967   ExoIconViewCellInfo *info;
4968   ExoIconView         *icon_view = EXO_ICON_VIEW (layout);
4969 
4970   _exo_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
4971   _exo_return_if_fail (exo_icon_view_get_cell_info (icon_view, renderer) == NULL);
4972 
4973   g_object_ref_sink (renderer);
4974 
4975   info = g_slice_new0 (ExoIconViewCellInfo);
4976   info->cell = renderer;
4977   info->expand = expand ? TRUE : FALSE;
4978   info->pack = GTK_PACK_END;
4979   info->position = icon_view->priv->n_cells;
4980   info->is_text = GTK_IS_CELL_RENDERER_TEXT (renderer);
4981 
4982   icon_view->priv->cell_list = g_list_append (icon_view->priv->cell_list, info);
4983   icon_view->priv->n_cells++;
4984 
4985   exo_icon_view_invalidate_sizes (icon_view);
4986 }
4987 
4988 
4989 
4990 static void
exo_icon_view_cell_layout_add_attribute(GtkCellLayout * layout,GtkCellRenderer * renderer,const gchar * attribute,gint column)4991 exo_icon_view_cell_layout_add_attribute (GtkCellLayout   *layout,
4992                                          GtkCellRenderer *renderer,
4993                                          const gchar     *attribute,
4994                                          gint             column)
4995 {
4996   ExoIconViewCellInfo *info;
4997 
4998   info = exo_icon_view_get_cell_info (EXO_ICON_VIEW (layout), renderer);
4999   if (G_LIKELY (info != NULL))
5000     {
5001       info->attributes = g_slist_prepend (info->attributes, GINT_TO_POINTER (column));
5002       info->attributes = g_slist_prepend (info->attributes, g_strdup (attribute));
5003 
5004       exo_icon_view_invalidate_sizes (EXO_ICON_VIEW (layout));
5005     }
5006 }
5007 
5008 
5009 
5010 static void
exo_icon_view_cell_layout_clear(GtkCellLayout * layout)5011 exo_icon_view_cell_layout_clear (GtkCellLayout *layout)
5012 {
5013   ExoIconView *icon_view = EXO_ICON_VIEW (layout);
5014 
5015   g_list_foreach (icon_view->priv->cell_list, (GFunc) (void (*)(void)) free_cell_info, NULL);
5016   g_list_free (icon_view->priv->cell_list);
5017   icon_view->priv->cell_list = NULL;
5018   icon_view->priv->n_cells = 0;
5019 
5020   exo_icon_view_invalidate_sizes (icon_view);
5021 }
5022 
5023 
5024 
5025 static void
exo_icon_view_cell_layout_set_cell_data_func(GtkCellLayout * layout,GtkCellRenderer * cell,GtkCellLayoutDataFunc func,gpointer func_data,GDestroyNotify destroy)5026 exo_icon_view_cell_layout_set_cell_data_func (GtkCellLayout         *layout,
5027                                               GtkCellRenderer       *cell,
5028                                               GtkCellLayoutDataFunc  func,
5029                                               gpointer               func_data,
5030                                               GDestroyNotify         destroy)
5031 {
5032   ExoIconViewCellInfo *info;
5033   GDestroyNotify       notify;
5034 
5035   info = exo_icon_view_get_cell_info (EXO_ICON_VIEW (layout), cell);
5036   if (G_LIKELY (info != NULL))
5037     {
5038       if (G_UNLIKELY (info->destroy != NULL))
5039         {
5040           notify = info->destroy;
5041           info->destroy = NULL;
5042           (*notify) (info->func_data);
5043         }
5044 
5045       info->func = func;
5046       info->func_data = func_data;
5047       info->destroy = destroy;
5048 
5049       exo_icon_view_invalidate_sizes (EXO_ICON_VIEW (layout));
5050     }
5051 }
5052 
5053 
5054 
5055 static void
exo_icon_view_cell_layout_clear_attributes(GtkCellLayout * layout,GtkCellRenderer * renderer)5056 exo_icon_view_cell_layout_clear_attributes (GtkCellLayout   *layout,
5057                                             GtkCellRenderer *renderer)
5058 {
5059   ExoIconViewCellInfo *info;
5060 
5061   info = exo_icon_view_get_cell_info (EXO_ICON_VIEW (layout), renderer);
5062   if (G_LIKELY (info != NULL))
5063     {
5064       free_cell_attributes (info);
5065 
5066       exo_icon_view_invalidate_sizes (EXO_ICON_VIEW (layout));
5067     }
5068 }
5069 
5070 
5071 
5072 static void
exo_icon_view_cell_layout_reorder(GtkCellLayout * layout,GtkCellRenderer * cell,gint position)5073 exo_icon_view_cell_layout_reorder (GtkCellLayout   *layout,
5074                                    GtkCellRenderer *cell,
5075                                    gint             position)
5076 {
5077   ExoIconViewCellInfo *info;
5078   ExoIconView         *icon_view = EXO_ICON_VIEW (layout);
5079   GList               *lp;
5080   gint                 n;
5081 
5082   info = exo_icon_view_get_cell_info (icon_view, cell);
5083   if (G_LIKELY (info != NULL))
5084     {
5085       lp = g_list_find (icon_view->priv->cell_list, info);
5086 
5087       icon_view->priv->cell_list = g_list_remove_link (icon_view->priv->cell_list, lp);
5088       icon_view->priv->cell_list = g_list_insert (icon_view->priv->cell_list, info, position);
5089 
5090       for (lp = icon_view->priv->cell_list, n = 0; lp != NULL; lp = lp->next, ++n)
5091         EXO_ICON_VIEW_CELL_INFO (lp->data)->position = n;
5092 
5093       exo_icon_view_invalidate_sizes (icon_view);
5094     }
5095 }
5096 
5097 
5098 
5099 /**
5100  * exo_icon_view_new:
5101  *
5102  * Creates a new #ExoIconView widget
5103  *
5104  * Returns: A newly created #ExoIconView widget
5105  **/
5106 GtkWidget*
exo_icon_view_new(void)5107 exo_icon_view_new (void)
5108 {
5109   return g_object_new (EXO_TYPE_ICON_VIEW, NULL);
5110 }
5111 
5112 
5113 
5114 /**
5115  * exo_icon_view_new_with_model:
5116  * @model: The model.
5117  *
5118  * Creates a new #ExoIconView widget with the model @model.
5119  *
5120  * Returns: A newly created #ExoIconView widget.
5121  **/
5122 GtkWidget*
exo_icon_view_new_with_model(GtkTreeModel * model)5123 exo_icon_view_new_with_model (GtkTreeModel *model)
5124 {
5125   g_return_val_if_fail (model == NULL || GTK_IS_TREE_MODEL (model), NULL);
5126 
5127   return g_object_new (EXO_TYPE_ICON_VIEW,
5128                        "model", model,
5129                        NULL);
5130 }
5131 
5132 
5133 
5134 /**
5135  * exo_icon_view_widget_to_icon_coords:
5136  * @icon_view: a #ExoIconView.
5137  * @wx: widget x coordinate.
5138  * @wy: widget y coordinate.
5139  * @ix: return location for icon x coordinate or %NULL.
5140  * @iy: return location for icon y coordinate or %NULL.
5141  *
5142  * Converts widget coordinates to coordinates for the icon window
5143  * (the full scrollable area of the icon view).
5144  **/
5145 void
exo_icon_view_widget_to_icon_coords(const ExoIconView * icon_view,gint wx,gint wy,gint * ix,gint * iy)5146 exo_icon_view_widget_to_icon_coords (const ExoIconView *icon_view,
5147                                      gint               wx,
5148                                      gint               wy,
5149                                      gint              *ix,
5150                                      gint              *iy)
5151 {
5152   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
5153 
5154   if (G_LIKELY (ix != NULL))
5155     *ix = wx + gtk_adjustment_get_value (icon_view->priv->hadjustment);
5156   if (G_LIKELY (iy != NULL))
5157     *iy = wy + gtk_adjustment_get_value (icon_view->priv->vadjustment);
5158 }
5159 
5160 
5161 
5162 /**
5163  * exo_icon_view_icon_to_widget_coords:
5164  * @icon_view : a #ExoIconView.
5165  * @ix        : icon x coordinate.
5166  * @iy        : icon y coordinate.
5167  * @wx        : return location for widget x coordinate or %NULL.
5168  * @wy        : return location for widget y coordinate or %NULL.
5169  *
5170  * Converts icon view coordinates (coordinates in full scrollable
5171  * area of the icon view) to widget coordinates.
5172  **/
5173 void
exo_icon_view_icon_to_widget_coords(const ExoIconView * icon_view,gint ix,gint iy,gint * wx,gint * wy)5174 exo_icon_view_icon_to_widget_coords (const ExoIconView *icon_view,
5175                                      gint               ix,
5176                                      gint               iy,
5177                                      gint              *wx,
5178                                      gint              *wy)
5179 {
5180   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
5181 
5182   if (G_LIKELY (wx != NULL))
5183     *wx = ix - gtk_adjustment_get_value (icon_view->priv->hadjustment);
5184   if (G_LIKELY (wy != NULL))
5185     *wy = iy - gtk_adjustment_get_value (icon_view->priv->vadjustment);
5186 }
5187 
5188 
5189 
5190 /**
5191  * exo_icon_view_get_path_at_pos:
5192  * @icon_view : A #ExoIconView.
5193  * @x         : The x position to be identified
5194  * @y         : The y position to be identified
5195  *
5196  * Finds the path at the point (@x, @y), relative to widget coordinates.
5197  * See exo_icon_view_get_item_at_pos(), if you are also interested in
5198  * the cell at the specified position.
5199  *
5200  * Returns: The #GtkTreePath corresponding to the icon or %NULL
5201  *          if no icon exists at that position.
5202  **/
5203 GtkTreePath*
exo_icon_view_get_path_at_pos(const ExoIconView * icon_view,gint x,gint y)5204 exo_icon_view_get_path_at_pos (const ExoIconView *icon_view,
5205                                gint               x,
5206                                gint               y)
5207 {
5208   ExoIconViewItem *item;
5209 
5210   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), NULL);
5211 
5212   /* translate the widget coordinates to icon window coordinates */
5213   x += gtk_adjustment_get_value (icon_view->priv->hadjustment);
5214   y += gtk_adjustment_get_value (icon_view->priv->vadjustment);
5215 
5216   item = exo_icon_view_get_item_at_coords (icon_view, x, y, TRUE, NULL);
5217 
5218   return (item != NULL) ? gtk_tree_path_new_from_indices (g_list_index (icon_view->priv->items, item), -1) : NULL;
5219 }
5220 
5221 
5222 
5223 /**
5224  * exo_icon_view_get_item_at_pos:
5225  * @icon_view: A #ExoIconView.
5226  * @x: The x position to be identified
5227  * @y: The y position to be identified
5228  * @path: Return location for the path, or %NULL
5229  * @cell: Return location for the renderer responsible for the cell
5230  *   at (@x, @y), or %NULL
5231  *
5232  * Finds the path at the point (@x, @y), relative to widget coordinates.
5233  * In contrast to exo_icon_view_get_path_at_pos(), this function also
5234  * obtains the cell at the specified position. The returned path should
5235  * be freed with gtk_tree_path_free().
5236  *
5237  * Returns: %TRUE if an item exists at the specified position
5238  *
5239  * Since: 0.3.1
5240  **/
5241 gboolean
exo_icon_view_get_item_at_pos(const ExoIconView * icon_view,gint x,gint y,GtkTreePath ** path,GtkCellRenderer ** cell)5242 exo_icon_view_get_item_at_pos (const ExoIconView *icon_view,
5243                                gint               x,
5244                                gint               y,
5245                                GtkTreePath      **path,
5246                                GtkCellRenderer  **cell)
5247 {
5248   ExoIconViewCellInfo *info = NULL;
5249   ExoIconViewItem     *item;
5250 
5251   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
5252 
5253   item = exo_icon_view_get_item_at_coords (icon_view, x, y, TRUE, &info);
5254 
5255   if (G_LIKELY (path != NULL))
5256     *path = (item != NULL) ? gtk_tree_path_new_from_indices (g_list_index (icon_view->priv->items, item), -1) : NULL;
5257 
5258   if (G_LIKELY (cell != NULL))
5259     *cell = (info != NULL) ? info->cell : NULL;
5260 
5261   return (item != NULL);
5262 }
5263 
5264 
5265 
5266 /**
5267  * exo_icon_view_get_visible_range:
5268  * @icon_view  : A #ExoIconView
5269  * @start_path : Return location for start of region, or %NULL
5270  * @end_path   : Return location for end of region, or %NULL
5271  *
5272  * Sets @start_path and @end_path to be the first and last visible path.
5273  * Note that there may be invisible paths in between.
5274  *
5275  * Both paths should be freed with gtk_tree_path_free() after use.
5276  *
5277  * Returns: %TRUE, if valid paths were placed in @start_path and @end_path
5278  *
5279  * Since: 0.3.1
5280  **/
5281 gboolean
exo_icon_view_get_visible_range(const ExoIconView * icon_view,GtkTreePath ** start_path,GtkTreePath ** end_path)5282 exo_icon_view_get_visible_range (const ExoIconView *icon_view,
5283                                  GtkTreePath      **start_path,
5284                                  GtkTreePath      **end_path)
5285 {
5286   const ExoIconViewPrivate *priv = icon_view->priv;
5287   const ExoIconViewItem    *item;
5288   const GList              *lp;
5289   gint                      start_index = -1;
5290   gint                      end_index = -1;
5291   gint                      i;
5292 
5293   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
5294 
5295   if (priv->hadjustment == NULL || priv->vadjustment == NULL)
5296     return FALSE;
5297 
5298   if (start_path == NULL && end_path == NULL)
5299     return FALSE;
5300 
5301   for (i = 0, lp = priv->items; lp != NULL; ++i, lp = lp->next)
5302     {
5303       item = (const ExoIconViewItem *) lp->data;
5304       if ((item->area.x + item->area.width >= (gint) gtk_adjustment_get_value (priv->hadjustment)) &&
5305           (item->area.y + item->area.height >= (gint) gtk_adjustment_get_value (priv->vadjustment)) &&
5306           (item->area.x <= (gint) (gtk_adjustment_get_value (priv->hadjustment) + gtk_adjustment_get_page_size (priv->hadjustment))) &&
5307           (item->area.y <= (gint) (gtk_adjustment_get_value (priv->vadjustment) + gtk_adjustment_get_page_size (priv->vadjustment))))
5308         {
5309           if (start_index == -1)
5310             start_index = i;
5311           end_index = i;
5312         }
5313     }
5314 
5315   if (start_path != NULL && start_index != -1)
5316     *start_path = gtk_tree_path_new_from_indices (start_index, -1);
5317   if (end_path != NULL && end_index != -1)
5318     *end_path = gtk_tree_path_new_from_indices (end_index, -1);
5319 
5320   return (start_index != -1);
5321 }
5322 
5323 
5324 
5325 /**
5326  * exo_icon_view_selected_foreach:
5327  * @icon_view : A #ExoIconView.
5328  * @func      : The funcion to call for each selected icon.
5329  * @data      : User data to pass to the function.
5330  *
5331  * Calls a function for each selected icon. Note that the model or
5332  * selection cannot be modified from within this function.
5333  **/
5334 void
exo_icon_view_selected_foreach(ExoIconView * icon_view,ExoIconViewForeachFunc func,gpointer data)5335 exo_icon_view_selected_foreach (ExoIconView           *icon_view,
5336                                 ExoIconViewForeachFunc func,
5337                                 gpointer               data)
5338 {
5339   GtkTreePath *path;
5340   GList       *lp;
5341 
5342   path = gtk_tree_path_new_first ();
5343   for (lp = icon_view->priv->items; lp != NULL; lp = lp->next)
5344     {
5345       if (EXO_ICON_VIEW_ITEM (lp->data)->selected)
5346         (*func) (icon_view, path, data);
5347       gtk_tree_path_next (path);
5348     }
5349   gtk_tree_path_free (path);
5350 }
5351 
5352 
5353 
5354 /**
5355  * exo_icon_view_get_selection_mode:
5356  * @icon_view : A #ExoIconView.
5357  *
5358  * Gets the selection mode of the @icon_view.
5359  *
5360  * Returns: the current selection mode
5361  **/
5362 GtkSelectionMode
exo_icon_view_get_selection_mode(const ExoIconView * icon_view)5363 exo_icon_view_get_selection_mode (const ExoIconView *icon_view)
5364 {
5365   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), GTK_SELECTION_SINGLE);
5366   return icon_view->priv->selection_mode;
5367 }
5368 
5369 
5370 
5371 /**
5372  * exo_icon_view_set_selection_mode:
5373  * @icon_view : A #ExoIconView.
5374  * @mode      : The selection mode
5375  *
5376  * Sets the selection mode of the @icon_view.
5377  **/
5378 void
exo_icon_view_set_selection_mode(ExoIconView * icon_view,GtkSelectionMode mode)5379 exo_icon_view_set_selection_mode (ExoIconView      *icon_view,
5380                                   GtkSelectionMode  mode)
5381 {
5382   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
5383 
5384   if (G_LIKELY (mode != icon_view->priv->selection_mode))
5385     {
5386       if (mode == GTK_SELECTION_NONE || icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE)
5387         exo_icon_view_unselect_all (icon_view);
5388 
5389       icon_view->priv->selection_mode = mode;
5390 
5391       g_object_notify (G_OBJECT (icon_view), "selection-mode");
5392     }
5393 }
5394 
5395 
5396 
5397 /**
5398  * exo_icon_view_get_layout_mode:
5399  * @icon_view : A #ExoIconView.
5400  *
5401  * Returns the #ExoIconViewLayoutMode used to layout the
5402  * items in the @icon_view.
5403  *
5404  * Returns: the layout mode of @icon_view.
5405  *
5406  * Since: 0.3.1.5
5407  **/
5408 ExoIconViewLayoutMode
exo_icon_view_get_layout_mode(const ExoIconView * icon_view)5409 exo_icon_view_get_layout_mode (const ExoIconView *icon_view)
5410 {
5411   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), EXO_ICON_VIEW_LAYOUT_ROWS);
5412   return icon_view->priv->layout_mode;
5413 }
5414 
5415 
5416 
5417 /**
5418  * exo_icon_view_set_layout_mode:
5419  * @icon_view   : a #ExoIconView.
5420  * @layout_mode : the new #ExoIconViewLayoutMode for @icon_view.
5421  *
5422  * Sets the layout mode of @icon_view to @layout_mode.
5423  *
5424  * Since: 0.3.1.5
5425  **/
5426 void
exo_icon_view_set_layout_mode(ExoIconView * icon_view,ExoIconViewLayoutMode layout_mode)5427 exo_icon_view_set_layout_mode (ExoIconView          *icon_view,
5428                                ExoIconViewLayoutMode layout_mode)
5429 {
5430   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
5431 
5432   /* check if we have a new setting */
5433   if (G_LIKELY (icon_view->priv->layout_mode != layout_mode))
5434     {
5435       /* apply the new setting */
5436       icon_view->priv->layout_mode = layout_mode;
5437 
5438       /* cancel any active cell editor */
5439       exo_icon_view_stop_editing (icon_view, TRUE);
5440 
5441       /* invalidate the current item sizes */
5442       exo_icon_view_invalidate_sizes (icon_view);
5443       exo_icon_view_queue_layout (icon_view);
5444 
5445       /* notify listeners */
5446       g_object_notify (G_OBJECT (icon_view), "layout-mode");
5447     }
5448 }
5449 
5450 
5451 
5452 /**
5453  * exo_icon_view_get_model:
5454  * @icon_view : a #ExoIconView
5455  *
5456  * Returns the model the #ExoIconView is based on. Returns %NULL if the
5457  * model is unset.
5458  *
5459  * Returns: A #GtkTreeModel, or %NULL if none is currently being used.
5460  **/
5461 GtkTreeModel*
exo_icon_view_get_model(const ExoIconView * icon_view)5462 exo_icon_view_get_model (const ExoIconView *icon_view)
5463 {
5464   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), NULL);
5465   return icon_view->priv->model;
5466 }
5467 
5468 
5469 
5470 /**
5471  * exo_icon_view_set_model:
5472  * @icon_view : A #ExoIconView.
5473  * @model     : The model.
5474  *
5475  * Sets the model for a #ExoIconView.
5476  * If the @icon_view already has a model set, it will remove
5477  * it before setting the new model.  If @model is %NULL, then
5478  * it will unset the old model.
5479  **/
5480 void
exo_icon_view_set_model(ExoIconView * icon_view,GtkTreeModel * model)5481 exo_icon_view_set_model (ExoIconView  *icon_view,
5482                          GtkTreeModel *model)
5483 {
5484   ExoIconViewItem *item;
5485   GtkTreeIter      iter;
5486   GList           *items = NULL;
5487   GList           *lp;
5488   gint             n;
5489 
5490   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
5491   g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
5492 
5493   /* verify that we don't already use that model */
5494   if (G_UNLIKELY (icon_view->priv->model == model))
5495     return;
5496 
5497   /* verify the new model */
5498   if (G_LIKELY (model != NULL))
5499     {
5500       g_return_if_fail (gtk_tree_model_get_flags (model) & GTK_TREE_MODEL_LIST_ONLY);
5501 
5502       if (G_UNLIKELY (icon_view->priv->pixbuf_column != -1))
5503         g_return_if_fail (gtk_tree_model_get_column_type (model, icon_view->priv->pixbuf_column) == GDK_TYPE_PIXBUF);
5504 
5505       if (G_UNLIKELY (icon_view->priv->icon_column != -1))
5506         g_return_if_fail (gtk_tree_model_get_column_type (model, icon_view->priv->icon_column) == G_TYPE_STRING);
5507 
5508       if (G_UNLIKELY (icon_view->priv->text_column != -1))
5509         g_return_if_fail (gtk_tree_model_get_column_type (model, icon_view->priv->text_column) == G_TYPE_STRING);
5510 
5511       if (G_UNLIKELY (icon_view->priv->markup_column != -1))
5512         g_return_if_fail (gtk_tree_model_get_column_type (model, icon_view->priv->markup_column) == G_TYPE_STRING);
5513     }
5514 
5515   /* be sure to cancel any pending editor */
5516   exo_icon_view_stop_editing (icon_view, TRUE);
5517 
5518   /* disconnect from the previous model */
5519   if (G_LIKELY (icon_view->priv->model != NULL))
5520     {
5521       /* disconnect signals handlers from the previous model */
5522       g_signal_handlers_disconnect_by_func (G_OBJECT (icon_view->priv->model), exo_icon_view_row_changed, icon_view);
5523       g_signal_handlers_disconnect_by_func (G_OBJECT (icon_view->priv->model), exo_icon_view_row_inserted, icon_view);
5524       g_signal_handlers_disconnect_by_func (G_OBJECT (icon_view->priv->model), exo_icon_view_row_deleted, icon_view);
5525       g_signal_handlers_disconnect_by_func (G_OBJECT (icon_view->priv->model), exo_icon_view_rows_reordered, icon_view);
5526 
5527       /* release our reference on the model */
5528       g_object_unref (G_OBJECT (icon_view->priv->model));
5529 
5530       /* drop all items belonging to the previous model */
5531       for (lp = icon_view->priv->items; lp != NULL; lp = lp->next)
5532         {
5533           g_free (EXO_ICON_VIEW_ITEM (lp->data)->box);
5534           g_slice_free (ExoIconViewItem, lp->data);
5535         }
5536       g_list_free (icon_view->priv->items);
5537       icon_view->priv->items = NULL;
5538 
5539       /* reset statistics */
5540       icon_view->priv->search_column = -1;
5541       icon_view->priv->anchor_item = NULL;
5542       icon_view->priv->cursor_item = NULL;
5543       icon_view->priv->prelit_item = NULL;
5544       icon_view->priv->last_single_clicked = NULL;
5545       icon_view->priv->width = 0;
5546       icon_view->priv->height = 0;
5547 
5548       /* cancel any pending single click timer */
5549       if (G_UNLIKELY (icon_view->priv->single_click_timeout_id != 0))
5550         g_source_remove (icon_view->priv->single_click_timeout_id);
5551 
5552       /* reset cursor when in single click mode and realized */
5553       if (G_UNLIKELY (icon_view->priv->single_click && gtk_widget_get_realized (GTK_WIDGET (icon_view))))
5554         gdk_window_set_cursor (icon_view->priv->bin_window, NULL);
5555     }
5556 
5557   /* be sure to drop any previous scroll_to_path reference,
5558    * as it points to the old (no longer valid) model.
5559    */
5560   if (G_UNLIKELY (icon_view->priv->scroll_to_path != NULL))
5561     {
5562       gtk_tree_row_reference_free (icon_view->priv->scroll_to_path);
5563       icon_view->priv->scroll_to_path = NULL;
5564     }
5565 
5566   /* activate the new model */
5567   icon_view->priv->model = model;
5568 
5569   /* connect to the new model */
5570   if (G_LIKELY (model != NULL))
5571     {
5572       /* take a reference on the model */
5573       g_object_ref (G_OBJECT (model));
5574 
5575       /* connect signals */
5576       g_signal_connect (G_OBJECT (model), "row-changed", G_CALLBACK (exo_icon_view_row_changed), icon_view);
5577       g_signal_connect (G_OBJECT (model), "row-inserted", G_CALLBACK (exo_icon_view_row_inserted), icon_view);
5578       g_signal_connect (G_OBJECT (model), "row-deleted", G_CALLBACK (exo_icon_view_row_deleted), icon_view);
5579       g_signal_connect (G_OBJECT (model), "rows-reordered", G_CALLBACK (exo_icon_view_rows_reordered), icon_view);
5580 
5581       /* check if the new model supports persistent iterators */
5582       if (gtk_tree_model_get_flags (model) & GTK_TREE_MODEL_ITERS_PERSIST)
5583         EXO_ICON_VIEW_SET_FLAG (icon_view, EXO_ICON_VIEW_ITERS_PERSIST);
5584       else
5585         EXO_ICON_VIEW_UNSET_FLAG (icon_view, EXO_ICON_VIEW_ITERS_PERSIST);
5586 
5587       /* determine an appropriate search column */
5588       if (icon_view->priv->search_column <= 0)
5589         {
5590           /* we simply use the first string column */
5591           for (n = 0; n < gtk_tree_model_get_n_columns (model); ++n)
5592             if (g_value_type_transformable (gtk_tree_model_get_column_type (model, n), G_TYPE_STRING))
5593               {
5594                 icon_view->priv->search_column = n;
5595                 break;
5596               }
5597         }
5598 
5599       /* build up the initial items list */
5600       if (gtk_tree_model_get_iter_first (model, &iter))
5601         {
5602           n = 0;
5603           do
5604             {
5605               item = g_slice_new0 (ExoIconViewItem);
5606               item->iter = iter;
5607               item->area.width = -1;
5608               items = g_list_prepend (items, item);
5609             }
5610           while (gtk_tree_model_iter_next (model, &iter));
5611         }
5612       icon_view->priv->items = g_list_reverse (items);
5613 
5614       /* layout the new items */
5615       exo_icon_view_queue_layout (icon_view);
5616     }
5617 
5618   /* hide the interactive search dialog (if any) */
5619   if (G_LIKELY (icon_view->priv->search_window != NULL))
5620     exo_icon_view_search_dialog_hide (icon_view->priv->search_window, icon_view);
5621 
5622   /* notify listeners */
5623   g_object_notify (G_OBJECT (icon_view), "model");
5624 
5625   if (gtk_widget_get_realized (GTK_WIDGET (icon_view)))
5626     gtk_widget_queue_resize (GTK_WIDGET (icon_view));
5627 }
5628 
5629 
5630 
5631 static void
update_text_cell(ExoIconView * icon_view)5632 update_text_cell (ExoIconView *icon_view)
5633 {
5634   ExoIconViewCellInfo *info;
5635   GList *l;
5636   gint i;
5637 
5638   if (icon_view->priv->text_column == -1 &&
5639       icon_view->priv->markup_column == -1)
5640     {
5641       if (icon_view->priv->text_cell != -1)
5642         {
5643           info = g_list_nth_data (icon_view->priv->cell_list,
5644                                   icon_view->priv->text_cell);
5645 
5646           icon_view->priv->cell_list = g_list_remove (icon_view->priv->cell_list, info);
5647 
5648           free_cell_info (info);
5649 
5650           icon_view->priv->n_cells--;
5651           icon_view->priv->text_cell = -1;
5652         }
5653     }
5654   else
5655     {
5656       if (icon_view->priv->text_cell == -1)
5657         {
5658           GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
5659           gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (icon_view), cell, FALSE);
5660           for (l = icon_view->priv->cell_list, i = 0; l; l = l->next, i++)
5661             {
5662               info = l->data;
5663               if (info->cell == cell)
5664                 {
5665                   icon_view->priv->text_cell = i;
5666                   break;
5667                 }
5668             }
5669         }
5670 
5671       info = g_list_nth_data (icon_view->priv->cell_list,
5672                               icon_view->priv->text_cell);
5673 
5674       if (icon_view->priv->markup_column != -1)
5675         gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (icon_view),
5676                                         info->cell,
5677                                         "markup", icon_view->priv->markup_column,
5678                                         NULL);
5679       else
5680         gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (icon_view),
5681                                         info->cell,
5682                                         "text", icon_view->priv->text_column,
5683                                         NULL);
5684     }
5685 }
5686 
5687 static void
update_pixbuf_cell(ExoIconView * icon_view)5688 update_pixbuf_cell (ExoIconView *icon_view)
5689 {
5690   ExoIconViewCellInfo *info;
5691   GList *l;
5692   gint i;
5693 
5694   if (icon_view->priv->pixbuf_column == -1 &&
5695       icon_view->priv->icon_column == -1)
5696     {
5697       if (icon_view->priv->pixbuf_cell != -1)
5698         {
5699           info = g_list_nth_data (icon_view->priv->cell_list,
5700                                   icon_view->priv->pixbuf_cell);
5701 
5702           icon_view->priv->cell_list = g_list_remove (icon_view->priv->cell_list, info);
5703 
5704           free_cell_info (info);
5705 
5706           icon_view->priv->n_cells--;
5707           icon_view->priv->pixbuf_cell = -1;
5708         }
5709     }
5710   else
5711     {
5712       if (icon_view->priv->pixbuf_cell == -1)
5713         {
5714           GtkCellRenderer *cell;
5715 
5716           if (icon_view->priv->pixbuf_column != -1)
5717             {
5718               cell = gtk_cell_renderer_pixbuf_new ();
5719             }
5720           else
5721             {
5722               cell = exo_cell_renderer_icon_new ();
5723             }
5724 
5725           gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (icon_view), cell, FALSE);
5726           for (l = icon_view->priv->cell_list, i = 0; l; l = l->next, i++)
5727             {
5728               info = l->data;
5729               if (info->cell == cell)
5730                 {
5731                   icon_view->priv->pixbuf_cell = i;
5732                   break;
5733                 }
5734             }
5735         }
5736 
5737       info = g_list_nth_data (icon_view->priv->cell_list,
5738                               icon_view->priv->pixbuf_cell);
5739 
5740       if (icon_view->priv->pixbuf_column != -1)
5741         {
5742           gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (icon_view),
5743                                           info->cell,
5744                                           "pixbuf", icon_view->priv->pixbuf_column,
5745                                           NULL);
5746         }
5747       else
5748         {
5749           gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (icon_view),
5750                                           info->cell,
5751                                           "icon", icon_view->priv->icon_column,
5752                                           NULL);
5753         }
5754     }
5755 }
5756 
5757 
5758 
5759 /**
5760  * exo_icon_view_select_path:
5761  * @icon_view : A #ExoIconView.
5762  * @path      : The #GtkTreePath to be selected.
5763  *
5764  * Selects the row at @path.
5765  **/
5766 void
exo_icon_view_select_path(ExoIconView * icon_view,GtkTreePath * path)5767 exo_icon_view_select_path (ExoIconView *icon_view,
5768                            GtkTreePath *path)
5769 {
5770   ExoIconViewItem *item;
5771 
5772   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
5773   g_return_if_fail (icon_view->priv->model != NULL);
5774   g_return_if_fail (gtk_tree_path_get_depth (path) > 0);
5775 
5776   item = g_list_nth_data (icon_view->priv->items, gtk_tree_path_get_indices(path)[0]);
5777   if (G_LIKELY (item != NULL))
5778     exo_icon_view_select_item (icon_view, item);
5779 }
5780 
5781 
5782 
5783 /**
5784  * exo_icon_view_unselect_path:
5785  * @icon_view : A #ExoIconView.
5786  * @path      : The #GtkTreePath to be unselected.
5787  *
5788  * Unselects the row at @path.
5789  **/
5790 void
exo_icon_view_unselect_path(ExoIconView * icon_view,GtkTreePath * path)5791 exo_icon_view_unselect_path (ExoIconView *icon_view,
5792                              GtkTreePath *path)
5793 {
5794   ExoIconViewItem *item;
5795 
5796   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
5797   g_return_if_fail (icon_view->priv->model != NULL);
5798   g_return_if_fail (gtk_tree_path_get_depth (path) > 0);
5799 
5800   item = g_list_nth_data (icon_view->priv->items, gtk_tree_path_get_indices(path)[0]);
5801   if (G_LIKELY (item != NULL))
5802     exo_icon_view_unselect_item (icon_view, item);
5803 }
5804 
5805 
5806 
5807 /**
5808  * exo_icon_view_get_selected_items:
5809  * @icon_view: A #ExoIconView.
5810  *
5811  * Creates a list of paths of all selected items. Additionally, if you are
5812  * planning on modifying the model after calling this function, you may
5813  * want to convert the returned list into a list of #GtkTreeRowReference<!-- -->s.
5814  * To do this, you can use gtk_tree_row_reference_new().
5815  *
5816  * To free the return value, use:
5817  * <informalexample><programlisting>
5818  * g_list_foreach (list, gtk_tree_path_free, NULL);
5819  * g_list_free (list);
5820  * </programlisting></informalexample>
5821  *
5822  * Returns: A #GList containing a #GtkTreePath for each selected row.
5823  **/
5824 GList*
exo_icon_view_get_selected_items(const ExoIconView * icon_view)5825 exo_icon_view_get_selected_items (const ExoIconView *icon_view)
5826 {
5827   GList *selected = NULL;
5828   GList *lp;
5829   gint   i;
5830 
5831   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), NULL);
5832 
5833   for (i = 0, lp = icon_view->priv->items; lp != NULL; ++i, lp = lp->next)
5834     {
5835       if (EXO_ICON_VIEW_ITEM (lp->data)->selected)
5836         selected = g_list_prepend (selected, gtk_tree_path_new_from_indices (i, -1));
5837     }
5838 
5839   return g_list_reverse (selected);
5840 }
5841 
5842 
5843 
5844 /**
5845  * exo_icon_view_select_all:
5846  * @icon_view : A #ExoIconView.
5847  *
5848  * Selects all the icons. @icon_view must has its selection mode set
5849  * to #GTK_SELECTION_MULTIPLE.
5850  **/
5851 void
exo_icon_view_select_all(ExoIconView * icon_view)5852 exo_icon_view_select_all (ExoIconView *icon_view)
5853 {
5854   GList *items;
5855   gboolean dirty = FALSE;
5856 
5857   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
5858 
5859   if (icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
5860     return;
5861 
5862   for (items = icon_view->priv->items; items; items = items->next)
5863     {
5864       ExoIconViewItem *item = items->data;
5865 
5866       if (!item->selected)
5867         {
5868           dirty = TRUE;
5869           item->selected = TRUE;
5870           exo_icon_view_queue_draw_item (icon_view, item);
5871         }
5872     }
5873 
5874   if (dirty)
5875     g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
5876 }
5877 
5878 
5879 
5880 /**
5881  * exo_icon_view_selection_invert:
5882  * @icon_view : A #ExoIconView.
5883  *
5884  * Selects all the icons that are currently not selected. @icon_view must
5885  * has its selection mode set to #GTK_SELECTION_MULTIPLE.
5886  **/
5887 void
exo_icon_view_selection_invert(ExoIconView * icon_view)5888 exo_icon_view_selection_invert (ExoIconView *icon_view)
5889 {
5890   GList           *items;
5891   gboolean         dirty = FALSE;
5892   ExoIconViewItem *item;
5893 
5894   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
5895 
5896   if (icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
5897     return;
5898 
5899   for (items = icon_view->priv->items; items; items = items->next)
5900     {
5901       item = items->data;
5902 
5903       item->selected = !item->selected;
5904       exo_icon_view_queue_draw_item (icon_view, item);
5905 
5906       dirty = TRUE;
5907     }
5908 
5909   if (dirty)
5910     g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
5911 }
5912 
5913 
5914 
5915 /**
5916  * exo_icon_view_unselect_all:
5917  * @icon_view : A #ExoIconView.
5918  *
5919  * Unselects all the icons.
5920  **/
5921 void
exo_icon_view_unselect_all(ExoIconView * icon_view)5922 exo_icon_view_unselect_all (ExoIconView *icon_view)
5923 {
5924   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
5925 
5926   if (G_UNLIKELY (icon_view->priv->selection_mode == GTK_SELECTION_BROWSE))
5927     return;
5928 
5929   if (exo_icon_view_unselect_all_internal (icon_view))
5930     g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
5931 }
5932 
5933 
5934 
5935 /**
5936  * exo_icon_view_path_is_selected:
5937  * @icon_view: A #ExoIconView.
5938  * @path: A #GtkTreePath to check selection on.
5939  *
5940  * Returns %TRUE if the icon pointed to by @path is currently
5941  * selected. If @icon does not point to a valid location, %FALSE is returned.
5942  *
5943  * Returns: %TRUE if @path is selected.
5944  **/
5945 gboolean
exo_icon_view_path_is_selected(const ExoIconView * icon_view,GtkTreePath * path)5946 exo_icon_view_path_is_selected (const ExoIconView *icon_view,
5947                                 GtkTreePath       *path)
5948 {
5949   ExoIconViewItem *item;
5950 
5951   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
5952   g_return_val_if_fail (icon_view->priv->model != NULL, FALSE);
5953   g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);
5954 
5955   item = g_list_nth_data (icon_view->priv->items, gtk_tree_path_get_indices(path)[0]);
5956 
5957   return (item != NULL && item->selected);
5958 }
5959 
5960 
5961 
5962 /**
5963  * exo_icon_view_item_activated:
5964  * @icon_view : a #ExoIconView
5965  * @path      : the #GtkTreePath to be activated
5966  *
5967  * Activates the item determined by @path.
5968  **/
5969 void
exo_icon_view_item_activated(ExoIconView * icon_view,GtkTreePath * path)5970 exo_icon_view_item_activated (ExoIconView *icon_view,
5971                               GtkTreePath *path)
5972 {
5973   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
5974   g_return_if_fail (gtk_tree_path_get_depth (path) > 0);
5975 
5976   g_signal_emit (icon_view, icon_view_signals[ITEM_ACTIVATED], 0, path);
5977 }
5978 
5979 
5980 
5981 /**
5982  * exo_icon_view_get_item_column:
5983  * @icon_view : A #ExoIconView.
5984  * @path      : The #GtkTreePath of the item.
5985  *
5986  * Gets the column in which the item @path is currently
5987  * displayed. Column numbers start at 0.
5988  *
5989  * Returns: The column in which the item is displayed
5990  *
5991  * Since: 0.7.1
5992  **/
5993 gint
exo_icon_view_get_item_column(ExoIconView * icon_view,GtkTreePath * path)5994 exo_icon_view_get_item_column (ExoIconView *icon_view,
5995                                GtkTreePath *path)
5996 {
5997   ExoIconViewItem *item;
5998 
5999   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), -1);
6000   g_return_val_if_fail (icon_view->priv->model != NULL, -1);
6001   g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, -1);
6002 
6003   item = g_list_nth_data (icon_view->priv->items, gtk_tree_path_get_indices(path)[0]);
6004   if (G_LIKELY (item != NULL))
6005     return item->col;
6006 
6007   return -1;
6008 }
6009 
6010 
6011 
6012 /**
6013  * exo_icon_view_get_item_row:
6014  * @icon_view : A #ExoIconView.
6015  * @path      : The #GtkTreePath of the item.
6016  *
6017  * Gets the row in which the item @path is currently
6018  * displayed. Row numbers start at 0.
6019  *
6020  * Returns: The row in which the item is displayed
6021  *
6022  * Since: 0.7.1
6023  */
6024 gint
exo_icon_view_get_item_row(ExoIconView * icon_view,GtkTreePath * path)6025 exo_icon_view_get_item_row (ExoIconView *icon_view,
6026                             GtkTreePath *path)
6027 {
6028   ExoIconViewItem *item;
6029 
6030   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), -1);
6031   g_return_val_if_fail (icon_view->priv->model != NULL, -1);
6032   g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, -1);
6033 
6034   item = g_list_nth_data (icon_view->priv->items, gtk_tree_path_get_indices(path)[0]);
6035   if (G_LIKELY (item != NULL))
6036     return item->row;
6037 
6038   return -1;
6039 }
6040 
6041 
6042 
6043 /**
6044  * exo_icon_view_get_cursor:
6045  * @icon_view : A #ExoIconView
6046  * @path      : Return location for the current cursor path, or %NULL
6047  * @cell      : Return location the current focus cell, or %NULL
6048  *
6049  * Fills in @path and @cell with the current cursor path and cell.
6050  * If the cursor isn't currently set, then *@path will be %NULL.
6051  * If no cell currently has focus, then *@cell will be %NULL.
6052  *
6053  * The returned #GtkTreePath must be freed with gtk_tree_path_free().
6054  *
6055  * Returns: %TRUE if the cursor is set.
6056  *
6057  * Since: 0.3.1
6058  **/
6059 gboolean
exo_icon_view_get_cursor(const ExoIconView * icon_view,GtkTreePath ** path,GtkCellRenderer ** cell)6060 exo_icon_view_get_cursor (const ExoIconView *icon_view,
6061                           GtkTreePath      **path,
6062                           GtkCellRenderer  **cell)
6063 {
6064   ExoIconViewCellInfo *info;
6065   ExoIconViewItem     *item;
6066 
6067   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
6068 
6069   item = icon_view->priv->cursor_item;
6070   info = (icon_view->priv->cursor_cell < 0) ? NULL : g_list_nth_data (icon_view->priv->cell_list, icon_view->priv->cursor_cell);
6071 
6072   if (G_LIKELY (path != NULL))
6073     *path = (item != NULL) ? gtk_tree_path_new_from_indices (g_list_index (icon_view->priv->items, item), -1) : NULL;
6074 
6075   if (G_LIKELY (cell != NULL))
6076     *cell = (info != NULL) ? info->cell : NULL;
6077 
6078   return (item != NULL);
6079 }
6080 
6081 
6082 
6083 /**
6084  * exo_icon_view_set_cursor:
6085  * @icon_view     : a #ExoIconView
6086  * @path          : a #GtkTreePath
6087  * @cell          : a #GtkCellRenderer or %NULL
6088  * @start_editing : %TRUE if the specified cell should start being edited.
6089  *
6090  * Sets the current keyboard focus to be at @path, and selects it.  This is
6091  * useful when you want to focus the user's attention on a particular item.
6092  * If @cell is not %NULL, then focus is given to the cell specified by
6093  * it. Additionally, if @start_editing is %TRUE, then editing should be
6094  * started in the specified cell.
6095  *
6096  * This function is often followed by <literal>gtk_widget_grab_focus
6097  * (icon_view)</literal> in order to give keyboard focus to the widget.
6098  * Please note that editing can only happen when the widget is realized.
6099  *
6100  * Since: 0.3.1
6101  **/
6102 void
exo_icon_view_set_cursor(ExoIconView * icon_view,GtkTreePath * path,GtkCellRenderer * cell,gboolean start_editing)6103 exo_icon_view_set_cursor (ExoIconView     *icon_view,
6104                           GtkTreePath     *path,
6105                           GtkCellRenderer *cell,
6106                           gboolean         start_editing)
6107 {
6108   ExoIconViewItem *item;
6109   ExoIconViewCellInfo *info =  NULL;
6110   GList *l;
6111   gint i, cell_pos;
6112 
6113   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6114   g_return_if_fail (path != NULL);
6115   g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell));
6116 
6117   exo_icon_view_stop_editing (icon_view, TRUE);
6118 
6119   item = g_list_nth_data (icon_view->priv->items, gtk_tree_path_get_indices(path)[0]);
6120   if (G_UNLIKELY (item == NULL))
6121     return;
6122 
6123   cell_pos = -1;
6124   for (l = icon_view->priv->cell_list, i = 0; l; l = l->next, i++)
6125     {
6126       info = l->data;
6127 
6128       if (info->cell == cell)
6129         {
6130           cell_pos = i;
6131           break;
6132         }
6133 
6134       info = NULL;
6135     }
6136 
6137   /* place the cursor on the item */
6138   exo_icon_view_set_cursor_item (icon_view, item, cell_pos);
6139   icon_view->priv->anchor_item = item;
6140 
6141   /* scroll to the item (maybe delayed) */
6142   exo_icon_view_scroll_to_path (icon_view, path, FALSE, 0.0f, 0.0f);
6143 
6144   if (!info)
6145     return;
6146 
6147   if (start_editing)
6148     exo_icon_view_start_editing (icon_view, item, info, NULL);
6149 }
6150 
6151 
6152 
6153 /**
6154  * exo_icon_view_scroll_to_path:
6155  * @icon_view: A #ExoIconView.
6156  * @path: The path of the item to move to.
6157  * @use_align: whether to use alignment arguments, or %FALSE.
6158  * @row_align: The vertical alignment of the item specified by @path.
6159  * @col_align: The horizontal alignment of the item specified by @column.
6160  *
6161  * Moves the alignments of @icon_view to the position specified by @path.
6162  * @row_align determines where the row is placed, and @col_align determines where
6163  * @column is placed.  Both are expected to be between 0.0 and 1.0.
6164  * 0.0 means left/top alignment, 1.0 means right/bottom alignment, 0.5 means center.
6165  *
6166  * If @use_align is %FALSE, then the alignment arguments are ignored, and the
6167  * tree does the minimum amount of work to scroll the item onto the screen.
6168  * This means that the item will be scrolled to the edge closest to its current
6169  * position.  If the item is currently visible on the screen, nothing is done.
6170  *
6171  * This function only works if the model is set, and @path is a valid row on the
6172  * model.  If the model changes before the @tree_view is realized, the centered
6173  * path will be modified to reflect this change.
6174  *
6175  * Since: 0.3.1
6176  **/
6177 void
exo_icon_view_scroll_to_path(ExoIconView * icon_view,GtkTreePath * path,gboolean use_align,gfloat row_align,gfloat col_align)6178 exo_icon_view_scroll_to_path (ExoIconView *icon_view,
6179                               GtkTreePath *path,
6180                               gboolean     use_align,
6181                               gfloat       row_align,
6182                               gfloat       col_align)
6183 {
6184   ExoIconViewItem *item;
6185   GtkAllocation    allocation;
6186 
6187   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6188   g_return_if_fail (gtk_tree_path_get_depth (path) > 0);
6189   g_return_if_fail (row_align >= 0.0 && row_align <= 1.0);
6190   g_return_if_fail (col_align >= 0.0 && col_align <= 1.0);
6191 
6192   gtk_widget_get_allocation (GTK_WIDGET (icon_view), &allocation);
6193 
6194   /* Delay scrolling if either not realized or pending layout() */
6195   if (!gtk_widget_get_realized (GTK_WIDGET (icon_view)) || icon_view->priv->layout_idle_id != 0)
6196     {
6197       /* release the previous scroll_to_path reference */
6198       if (G_UNLIKELY (icon_view->priv->scroll_to_path != NULL))
6199         gtk_tree_row_reference_free (icon_view->priv->scroll_to_path);
6200 
6201       /* remember a reference for the new path and settings */
6202       icon_view->priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (icon_view), icon_view->priv->model, path);
6203       icon_view->priv->scroll_to_use_align = use_align;
6204       icon_view->priv->scroll_to_row_align = row_align;
6205       icon_view->priv->scroll_to_col_align = col_align;
6206     }
6207   else
6208     {
6209       item = g_list_nth_data (icon_view->priv->items, gtk_tree_path_get_indices(path)[0]);
6210       if (G_UNLIKELY (item == NULL))
6211         return;
6212 
6213       if (use_align)
6214         {
6215           gint x, y;
6216           gint focus_width;
6217           gfloat offset, value;
6218 
6219           gtk_widget_style_get (GTK_WIDGET (icon_view),
6220                                 "focus-line-width", &focus_width,
6221                                 NULL);
6222 
6223           gdk_window_get_position (icon_view->priv->bin_window, &x, &y);
6224 
6225           offset =  y + item->area.y - focus_width -
6226             row_align * (allocation.height - item->area.height);
6227           value = CLAMP (gtk_adjustment_get_value (icon_view->priv->vadjustment) + offset,
6228                          gtk_adjustment_get_lower (icon_view->priv->vadjustment),
6229                          gtk_adjustment_get_upper (icon_view->priv->vadjustment) - gtk_adjustment_get_page_size (icon_view->priv->vadjustment));
6230           gtk_adjustment_set_value (icon_view->priv->vadjustment, value);
6231 
6232           offset = x + item->area.x - focus_width -
6233             col_align * (allocation.width - item->area.width);
6234           value = CLAMP (gtk_adjustment_get_value (icon_view->priv->hadjustment) + offset,
6235                          gtk_adjustment_get_lower (icon_view->priv->hadjustment),
6236                          gtk_adjustment_get_upper (icon_view->priv->hadjustment) - gtk_adjustment_get_page_size (icon_view->priv->hadjustment));
6237           gtk_adjustment_set_value (icon_view->priv->hadjustment, value);
6238         }
6239       else
6240         {
6241           exo_icon_view_scroll_to_item (icon_view, item);
6242         }
6243     }
6244 }
6245 
6246 
6247 
6248 /**
6249  * exo_icon_view_get_orientation:
6250  * @icon_view : a #ExoIconView
6251  *
6252  * Returns the value of the ::orientation property which determines
6253  * whether the labels are drawn beside the icons instead of below.
6254  *
6255  * Returns: the relative position of texts and icons
6256  *
6257  * Since: 0.3.1
6258  **/
6259 GtkOrientation
exo_icon_view_get_orientation(const ExoIconView * icon_view)6260 exo_icon_view_get_orientation (const ExoIconView *icon_view)
6261 {
6262   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), GTK_ORIENTATION_VERTICAL);
6263   return icon_view->priv->orientation;
6264 }
6265 
6266 
6267 
6268 /**
6269  * exo_icon_view_set_orientation:
6270  * @icon_view   : a #ExoIconView
6271  * @orientation : the relative position of texts and icons
6272  *
6273  * Sets the ::orientation property which determines whether the labels
6274  * are drawn beside the icons instead of below.
6275  *
6276  * Since: 0.3.1
6277  **/
6278 void
exo_icon_view_set_orientation(ExoIconView * icon_view,GtkOrientation orientation)6279 exo_icon_view_set_orientation (ExoIconView   *icon_view,
6280                                GtkOrientation orientation)
6281 {
6282   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6283 
6284   if (G_LIKELY (icon_view->priv->orientation != orientation))
6285     {
6286       icon_view->priv->orientation = orientation;
6287 
6288       exo_icon_view_stop_editing (icon_view, TRUE);
6289       exo_icon_view_invalidate_sizes (icon_view);
6290 
6291       update_text_cell (icon_view);
6292       update_pixbuf_cell (icon_view);
6293 
6294       g_object_notify (G_OBJECT (icon_view), "orientation");
6295     }
6296 }
6297 
6298 
6299 
6300 /**
6301  * exo_icon_view_get_columns:
6302  * @icon_view: a #ExoIconView
6303  *
6304  * Returns the value of the ::columns property.
6305  *
6306  * Returns: the number of columns, or -1
6307  */
6308 gint
exo_icon_view_get_columns(const ExoIconView * icon_view)6309 exo_icon_view_get_columns (const ExoIconView *icon_view)
6310 {
6311   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), -1);
6312   return icon_view->priv->columns;
6313 }
6314 
6315 
6316 
6317 /**
6318  * exo_icon_view_set_columns:
6319  * @icon_view : a #ExoIconView
6320  * @columns   : the number of columns
6321  *
6322  * Sets the ::columns property which determines in how
6323  * many columns the icons are arranged. If @columns is
6324  * -1, the number of columns will be chosen automatically
6325  * to fill the available area.
6326  *
6327  * Since: 0.3.1
6328  */
6329 void
exo_icon_view_set_columns(ExoIconView * icon_view,gint columns)6330 exo_icon_view_set_columns (ExoIconView *icon_view,
6331                            gint         columns)
6332 {
6333   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6334 
6335   if (G_LIKELY (icon_view->priv->columns != columns))
6336     {
6337       icon_view->priv->columns = columns;
6338 
6339       exo_icon_view_stop_editing (icon_view, TRUE);
6340       exo_icon_view_queue_layout (icon_view);
6341 
6342       g_object_notify (G_OBJECT (icon_view), "columns");
6343     }
6344 }
6345 
6346 
6347 
6348 /**
6349  * exo_icon_view_get_item_width:
6350  * @icon_view: a #ExoIconView
6351  *
6352  * Returns the value of the ::item-width property.
6353  *
6354  * Returns: the width of a single item, or -1
6355  *
6356  * Since: 0.3.1
6357  */
6358 gint
exo_icon_view_get_item_width(const ExoIconView * icon_view)6359 exo_icon_view_get_item_width (const ExoIconView *icon_view)
6360 {
6361   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), -1);
6362   return icon_view->priv->item_width;
6363 }
6364 
6365 
6366 
6367 /**
6368  * exo_icon_view_set_item_width:
6369  * @icon_view  : a #ExoIconView
6370  * @item_width : the width for each item
6371  *
6372  * Sets the ::item-width property which specifies the width
6373  * to use for each item. If it is set to -1, the icon view will
6374  * automatically determine a suitable item size.
6375  *
6376  * Since: 0.3.1
6377  */
6378 void
exo_icon_view_set_item_width(ExoIconView * icon_view,gint item_width)6379 exo_icon_view_set_item_width (ExoIconView *icon_view,
6380                               gint         item_width)
6381 {
6382   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6383 
6384   if (icon_view->priv->item_width != item_width)
6385     {
6386       icon_view->priv->item_width = item_width;
6387 
6388       exo_icon_view_stop_editing (icon_view, TRUE);
6389       exo_icon_view_invalidate_sizes (icon_view);
6390 
6391       update_text_cell (icon_view);
6392 
6393       g_object_notify (G_OBJECT (icon_view), "item-width");
6394     }
6395 }
6396 
6397 
6398 
6399 /**
6400  * exo_icon_view_get_spacing:
6401  * @icon_view: a #ExoIconView
6402  *
6403  * Returns the value of the ::spacing property.
6404  *
6405  * Returns: the space between cells
6406  *
6407  * Since: 0.3.1
6408  */
6409 gint
exo_icon_view_get_spacing(const ExoIconView * icon_view)6410 exo_icon_view_get_spacing (const ExoIconView *icon_view)
6411 {
6412   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), -1);
6413   return icon_view->priv->spacing;
6414 }
6415 
6416 
6417 
6418 /**
6419  * exo_icon_view_set_spacing:
6420  * @icon_view : a #ExoIconView
6421  * @spacing   : the spacing
6422  *
6423  * Sets the ::spacing property which specifies the space
6424  * which is inserted between the cells (i.e. the icon and
6425  * the text) of an item.
6426  *
6427  * Since: 0.3.1
6428  */
6429 void
exo_icon_view_set_spacing(ExoIconView * icon_view,gint spacing)6430 exo_icon_view_set_spacing (ExoIconView *icon_view,
6431                            gint         spacing)
6432 {
6433   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6434 
6435   if (G_LIKELY (icon_view->priv->spacing != spacing))
6436     {
6437       icon_view->priv->spacing = spacing;
6438 
6439       exo_icon_view_stop_editing (icon_view, TRUE);
6440       exo_icon_view_invalidate_sizes (icon_view);
6441 
6442       g_object_notify (G_OBJECT (icon_view), "spacing");
6443     }
6444 }
6445 
6446 
6447 
6448 /**
6449  * exo_icon_view_get_row_spacing:
6450  * @icon_view: a #ExoIconView
6451  *
6452  * Returns the value of the ::row-spacing property.
6453  *
6454  * Returns: the space between rows
6455  *
6456  * Since: 0.3.1
6457  */
6458 gint
exo_icon_view_get_row_spacing(const ExoIconView * icon_view)6459 exo_icon_view_get_row_spacing (const ExoIconView *icon_view)
6460 {
6461   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), -1);
6462   return icon_view->priv->row_spacing;
6463 }
6464 
6465 
6466 
6467 /**
6468  * exo_icon_view_set_row_spacing:
6469  * @icon_view   : a #ExoIconView
6470  * @row_spacing : the row spacing
6471  *
6472  * Sets the ::row-spacing property which specifies the space
6473  * which is inserted between the rows of the icon view.
6474  *
6475  * Since: 0.3.1
6476  */
6477 void
exo_icon_view_set_row_spacing(ExoIconView * icon_view,gint row_spacing)6478 exo_icon_view_set_row_spacing (ExoIconView *icon_view,
6479                                gint         row_spacing)
6480 {
6481   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6482 
6483   if (G_LIKELY (icon_view->priv->row_spacing != row_spacing))
6484     {
6485       icon_view->priv->row_spacing = row_spacing;
6486 
6487       exo_icon_view_stop_editing (icon_view, TRUE);
6488       exo_icon_view_invalidate_sizes (icon_view);
6489 
6490       g_object_notify (G_OBJECT (icon_view), "row-spacing");
6491     }
6492 }
6493 
6494 
6495 
6496 /**
6497  * exo_icon_view_get_column_spacing:
6498  * @icon_view: a #ExoIconView
6499  *
6500  * Returns the value of the ::column-spacing property.
6501  *
6502  * Returns: the space between columns
6503  *
6504  * Since: 0.3.1
6505  **/
6506 gint
exo_icon_view_get_column_spacing(const ExoIconView * icon_view)6507 exo_icon_view_get_column_spacing (const ExoIconView *icon_view)
6508 {
6509   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), -1);
6510   return icon_view->priv->column_spacing;
6511 }
6512 
6513 
6514 
6515 /**
6516  * exo_icon_view_set_column_spacing:
6517  * @icon_view      : a #ExoIconView
6518  * @column_spacing : the column spacing
6519  *
6520  * Sets the ::column-spacing property which specifies the space
6521  * which is inserted between the columns of the icon view.
6522  *
6523  * Since: 0.3.1
6524  **/
6525 void
exo_icon_view_set_column_spacing(ExoIconView * icon_view,gint column_spacing)6526 exo_icon_view_set_column_spacing (ExoIconView *icon_view,
6527                                   gint         column_spacing)
6528 {
6529   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6530 
6531   if (G_LIKELY (icon_view->priv->column_spacing != column_spacing))
6532     {
6533       icon_view->priv->column_spacing = column_spacing;
6534 
6535       exo_icon_view_stop_editing (icon_view, TRUE);
6536       exo_icon_view_invalidate_sizes (icon_view);
6537 
6538       g_object_notify (G_OBJECT (icon_view), "column-spacing");
6539     }
6540 }
6541 
6542 
6543 
6544 /**
6545  * exo_icon_view_get_margin:
6546  * @icon_view : a #ExoIconView
6547  *
6548  * Returns the value of the ::margin property.
6549  *
6550  * Returns: the space at the borders
6551  *
6552  * Since: 0.3.1
6553  **/
6554 gint
exo_icon_view_get_margin(const ExoIconView * icon_view)6555 exo_icon_view_get_margin (const ExoIconView *icon_view)
6556 {
6557   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), -1);
6558   return icon_view->priv->margin;
6559 }
6560 
6561 
6562 
6563 /**
6564  * exo_icon_view_set_margin:
6565  * @icon_view : a #ExoIconView
6566  * @margin    : the margin
6567  *
6568  * Sets the ::margin property which specifies the space
6569  * which is inserted at the top, bottom, left and right
6570  * of the icon view.
6571  *
6572  * Since: 0.3.1
6573  **/
6574 void
exo_icon_view_set_margin(ExoIconView * icon_view,gint margin)6575 exo_icon_view_set_margin (ExoIconView *icon_view,
6576                           gint         margin)
6577 {
6578   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6579 
6580   if (G_LIKELY (icon_view->priv->margin != margin))
6581     {
6582       icon_view->priv->margin = margin;
6583 
6584       exo_icon_view_stop_editing (icon_view, TRUE);
6585       exo_icon_view_invalidate_sizes (icon_view);
6586 
6587       g_object_notify (G_OBJECT (icon_view), "margin");
6588     }
6589 }
6590 
6591 
6592 
6593 /* Get/set whether drag_motion requested the drag data and
6594  * drag_data_received should thus not actually insert the data,
6595  * since the data doesn't result from a drop.
6596  */
6597 static void
set_status_pending(GdkDragContext * context,GdkDragAction suggested_action)6598 set_status_pending (GdkDragContext *context,
6599                     GdkDragAction   suggested_action)
6600 {
6601   g_object_set_data (G_OBJECT (context),
6602                      I_("exo-icon-view-status-pending"),
6603                      GINT_TO_POINTER (suggested_action));
6604 }
6605 
6606 static GdkDragAction
get_status_pending(GdkDragContext * context)6607 get_status_pending (GdkDragContext *context)
6608 {
6609   return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context), I_("exo-icon-view-status-pending")));
6610 }
6611 
6612 static void
unset_reorderable(ExoIconView * icon_view)6613 unset_reorderable (ExoIconView *icon_view)
6614 {
6615   if (icon_view->priv->reorderable)
6616     {
6617       icon_view->priv->reorderable = FALSE;
6618       g_object_notify (G_OBJECT (icon_view), "reorderable");
6619     }
6620 }
6621 
6622 static void
clear_source_info(ExoIconView * icon_view)6623 clear_source_info (ExoIconView *icon_view)
6624 {
6625   if (icon_view->priv->source_targets)
6626     gtk_target_list_unref (icon_view->priv->source_targets);
6627   icon_view->priv->source_targets = NULL;
6628 
6629   icon_view->priv->source_set = FALSE;
6630 }
6631 
6632 static void
clear_dest_info(ExoIconView * icon_view)6633 clear_dest_info (ExoIconView *icon_view)
6634 {
6635   if (icon_view->priv->dest_targets)
6636     gtk_target_list_unref (icon_view->priv->dest_targets);
6637   icon_view->priv->dest_targets = NULL;
6638 
6639   icon_view->priv->dest_set = FALSE;
6640 }
6641 
6642 static void
set_source_row(GdkDragContext * context,GtkTreeModel * model,GtkTreePath * source_row)6643 set_source_row (GdkDragContext *context,
6644                 GtkTreeModel   *model,
6645                 GtkTreePath    *source_row)
6646 {
6647   if (source_row)
6648     g_object_set_data_full (G_OBJECT (context),
6649                             I_("exo-icon-view-source-row"),
6650                             gtk_tree_row_reference_new (model, source_row),
6651                             (GDestroyNotify) gtk_tree_row_reference_free);
6652   else
6653     g_object_set_data_full (G_OBJECT (context),
6654                             I_("exo-icon-view-source-row"),
6655                             NULL, NULL);
6656 }
6657 
6658 static GtkTreePath*
get_source_row(GdkDragContext * context)6659 get_source_row (GdkDragContext *context)
6660 {
6661   GtkTreeRowReference *ref;
6662 
6663   ref = g_object_get_data (G_OBJECT (context), I_("exo-icon-view-source-row"));
6664 
6665   if (ref)
6666     return gtk_tree_row_reference_get_path (ref);
6667   else
6668     return NULL;
6669 }
6670 
6671 typedef struct
6672 {
6673   GtkTreeRowReference *dest_row;
6674   gboolean             empty_view_drop;
6675   gboolean             drop_append_mode;
6676 } DestRow;
6677 
6678 static void
dest_row_free(gpointer data)6679 dest_row_free (gpointer data)
6680 {
6681   DestRow *dr = (DestRow *)data;
6682 
6683   gtk_tree_row_reference_free (dr->dest_row);
6684   g_slice_free (DestRow, dr);
6685 }
6686 
6687 static void
set_dest_row(GdkDragContext * context,GtkTreeModel * model,GtkTreePath * dest_row,gboolean empty_view_drop,gboolean drop_append_mode)6688 set_dest_row (GdkDragContext *context,
6689               GtkTreeModel   *model,
6690               GtkTreePath    *dest_row,
6691               gboolean        empty_view_drop,
6692               gboolean        drop_append_mode)
6693 {
6694   DestRow *dr;
6695 
6696   if (!dest_row)
6697     {
6698       g_object_set_data_full (G_OBJECT (context),
6699                               I_("exo-icon-view-dest-row"),
6700                               NULL, NULL);
6701       return;
6702     }
6703 
6704   dr = g_slice_new0 (DestRow);
6705 
6706   dr->dest_row = gtk_tree_row_reference_new (model, dest_row);
6707   dr->empty_view_drop = empty_view_drop;
6708   dr->drop_append_mode = drop_append_mode;
6709   g_object_set_data_full (G_OBJECT (context),
6710                           I_("exo-icon-view-dest-row"),
6711                           dr, (GDestroyNotify) dest_row_free);
6712 }
6713 
6714 
6715 
6716 static GtkTreePath*
get_dest_row(GdkDragContext * context)6717 get_dest_row (GdkDragContext *context)
6718 {
6719   DestRow *dr;
6720 
6721   dr = g_object_get_data (G_OBJECT (context), I_("exo-icon-view-dest-row"));
6722 
6723   if (dr)
6724     {
6725       GtkTreePath *path = NULL;
6726 
6727       if (dr->dest_row)
6728         path = gtk_tree_row_reference_get_path (dr->dest_row);
6729       else if (dr->empty_view_drop)
6730         path = gtk_tree_path_new_from_indices (0, -1);
6731       else
6732         path = NULL;
6733 
6734       if (path && dr->drop_append_mode)
6735         gtk_tree_path_next (path);
6736 
6737       return path;
6738     }
6739   else
6740     return NULL;
6741 }
6742 
6743 
6744 
6745 static gboolean
check_model_dnd(GtkTreeModel * model,GType required_iface,const gchar * _signal)6746 check_model_dnd (GtkTreeModel *model,
6747                  GType         required_iface,
6748                  const gchar  *_signal)
6749 {
6750   if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface))
6751     {
6752       g_warning ("You must override the default '%s' handler "
6753                  "on ExoIconView when using models that don't support "
6754                  "the %s interface and enabling drag-and-drop. The simplest way to do this "
6755                  "is to connect to '%s' and call "
6756                  "g_signal_stop_emission_by_name() in your signal handler to prevent "
6757                  "the default handler from running. Look at the source code "
6758                  "for the default handler in gtkiconview.c to get an idea what "
6759                  "your handler should do. (gtkiconview.c is in the GTK+ source "
6760                  "code.) If you're using GTK+ from a language other than C, "
6761                  "there may be a more natural way to override default handlers, e.g. via derivation.",
6762                  _signal, g_type_name (required_iface), _signal);
6763       return FALSE;
6764     }
6765   else
6766     return TRUE;
6767 }
6768 
6769 
6770 
6771 static void
remove_scroll_timeout(ExoIconView * icon_view)6772 remove_scroll_timeout (ExoIconView *icon_view)
6773 {
6774   if (icon_view->priv->scroll_timeout_id != 0)
6775     {
6776       g_source_remove (icon_view->priv->scroll_timeout_id);
6777 
6778       icon_view->priv->scroll_timeout_id = 0;
6779     }
6780 }
6781 
6782 
6783 
6784 static void
exo_icon_view_autoscroll(ExoIconView * icon_view)6785 exo_icon_view_autoscroll (ExoIconView *icon_view)
6786 {
6787   gint px, py, x, y, width, height;
6788   gint hoffset, voffset;
6789   gfloat value;
6790 
6791   GdkSeat          *seat;
6792   GdkDevice        *pointer_dev;
6793 
6794   seat = gdk_display_get_default_seat (gdk_window_get_display (gtk_widget_get_window (GTK_WIDGET (icon_view))));
6795   pointer_dev = gdk_seat_get_pointer (seat);
6796 
6797   gdk_window_get_device_position (gtk_widget_get_window (GTK_WIDGET (icon_view)), pointer_dev, &px, &py, NULL);
6798   gdk_window_get_geometry (gtk_widget_get_window (GTK_WIDGET (icon_view)), &x, &y, &width, &height);
6799 
6800   /* see if we are near the edge. */
6801   voffset = py - (y + 2 * SCROLL_EDGE_SIZE);
6802   if (voffset > 0)
6803     voffset = MAX (py - (y + height - 2 * SCROLL_EDGE_SIZE), 0);
6804 
6805   hoffset = px - (x + 2 * SCROLL_EDGE_SIZE);
6806   if (hoffset > 0)
6807     hoffset = MAX (px - (x + width - 2 * SCROLL_EDGE_SIZE), 0);
6808 
6809   if (voffset != 0)
6810     {
6811       value = CLAMP (gtk_adjustment_get_value (icon_view->priv->vadjustment) + voffset,
6812                      gtk_adjustment_get_lower (icon_view->priv->vadjustment),
6813                      gtk_adjustment_get_upper (icon_view->priv->vadjustment) - gtk_adjustment_get_page_size (icon_view->priv->vadjustment));
6814       gtk_adjustment_set_value (icon_view->priv->vadjustment, value);
6815     }
6816   if (hoffset != 0)
6817     {
6818       value = CLAMP (gtk_adjustment_get_value (icon_view->priv->hadjustment) + hoffset,
6819                      gtk_adjustment_get_lower (icon_view->priv->hadjustment),
6820                      gtk_adjustment_get_upper (icon_view->priv->hadjustment) - gtk_adjustment_get_page_size (icon_view->priv->hadjustment));
6821       gtk_adjustment_set_value (icon_view->priv->hadjustment, value);
6822     }
6823 }
6824 
6825 
6826 static gboolean
drag_scroll_timeout(gpointer data)6827 drag_scroll_timeout (gpointer data)
6828 {
6829   ExoIconView *icon_view = EXO_ICON_VIEW (data);
6830 
6831   exo_icon_view_autoscroll (icon_view);
6832 
6833   return TRUE;
6834 }
6835 
6836 
6837 static gboolean
set_destination(ExoIconView * icon_view,GdkDragContext * context,gint x,gint y,GdkDragAction * suggested_action,GdkAtom * target)6838 set_destination (ExoIconView    *icon_view,
6839                  GdkDragContext *context,
6840                  gint            x,
6841                  gint            y,
6842                  GdkDragAction  *suggested_action,
6843                  GdkAtom        *target)
6844 {
6845   GtkWidget *widget;
6846   GtkTreePath *path = NULL;
6847   ExoIconViewDropPosition pos;
6848   ExoIconViewDropPosition old_pos;
6849   GtkTreePath *old_dest_path = NULL;
6850   gboolean can_drop = FALSE;
6851 
6852   widget = GTK_WIDGET (icon_view);
6853 
6854   *suggested_action = 0;
6855   *target = GDK_NONE;
6856 
6857   if (!icon_view->priv->dest_set)
6858     {
6859       /* someone unset us as a drag dest, note that if
6860        * we return FALSE drag_leave isn't called
6861        */
6862 
6863       exo_icon_view_set_drag_dest_item (icon_view,
6864                                         NULL,
6865                                         EXO_ICON_VIEW_DROP_LEFT);
6866 
6867       remove_scroll_timeout (EXO_ICON_VIEW (widget));
6868 
6869       return FALSE; /* no longer a drop site */
6870     }
6871 
6872   *target = gtk_drag_dest_find_target (widget, context, icon_view->priv->dest_targets);
6873   if (*target == GDK_NONE)
6874     return FALSE;
6875 
6876   if (!exo_icon_view_get_dest_item_at_pos (icon_view, x, y, &path, &pos))
6877     {
6878       gint n_children;
6879       GtkTreeModel *model;
6880 
6881       /* the row got dropped on empty space, let's setup a special case
6882        */
6883 
6884       if (path)
6885         gtk_tree_path_free (path);
6886 
6887       model = exo_icon_view_get_model (icon_view);
6888 
6889       n_children = gtk_tree_model_iter_n_children (model, NULL);
6890       if (n_children)
6891         {
6892           pos = EXO_ICON_VIEW_DROP_BELOW;
6893           path = gtk_tree_path_new_from_indices (n_children - 1, -1);
6894         }
6895       else
6896         {
6897           pos = EXO_ICON_VIEW_DROP_ABOVE;
6898           path = gtk_tree_path_new_from_indices (0, -1);
6899         }
6900 
6901       can_drop = TRUE;
6902 
6903       goto out;
6904     }
6905 
6906   g_assert (path);
6907 
6908   exo_icon_view_get_drag_dest_item (icon_view,
6909                                     &old_dest_path,
6910                                     &old_pos);
6911 
6912   if (old_dest_path)
6913     gtk_tree_path_free (old_dest_path);
6914 
6915   if (TRUE /* FIXME if the location droppable predicate */)
6916     {
6917       can_drop = TRUE;
6918     }
6919 
6920 out:
6921   if (can_drop)
6922     {
6923       GtkWidget *source_widget;
6924 
6925       *suggested_action = gdk_drag_context_get_suggested_action (context);
6926       source_widget = gtk_drag_get_source_widget (context);
6927 
6928       if (source_widget == widget)
6929         {
6930           /* Default to MOVE, unless the user has
6931            * pressed ctrl or shift to affect available actions
6932            */
6933           if ((gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE) != 0)
6934             *suggested_action = GDK_ACTION_MOVE;
6935         }
6936 
6937       exo_icon_view_set_drag_dest_item (EXO_ICON_VIEW (widget),
6938                                         path, pos);
6939     }
6940   else
6941     {
6942       /* can't drop here */
6943       exo_icon_view_set_drag_dest_item (EXO_ICON_VIEW (widget),
6944                                         NULL,
6945                                         EXO_ICON_VIEW_DROP_LEFT);
6946     }
6947 
6948   if (path)
6949     gtk_tree_path_free (path);
6950 
6951   return TRUE;
6952 }
6953 
6954 static GtkTreePath*
get_logical_destination(ExoIconView * icon_view,gboolean * drop_append_mode)6955 get_logical_destination (ExoIconView *icon_view,
6956                          gboolean    *drop_append_mode)
6957 {
6958   /* adjust path to point to the row the drop goes in front of */
6959   GtkTreePath *path = NULL;
6960   ExoIconViewDropPosition pos;
6961 
6962   *drop_append_mode = FALSE;
6963 
6964   exo_icon_view_get_drag_dest_item (icon_view, &path, &pos);
6965 
6966   if (path == NULL)
6967     return NULL;
6968 
6969   if (pos == EXO_ICON_VIEW_DROP_RIGHT ||
6970       pos == EXO_ICON_VIEW_DROP_BELOW)
6971     {
6972       GtkTreeIter iter;
6973       GtkTreeModel *model = icon_view->priv->model;
6974 
6975       if (!gtk_tree_model_get_iter (model, &iter, path) ||
6976           !gtk_tree_model_iter_next (model, &iter))
6977         *drop_append_mode = TRUE;
6978       else
6979         {
6980           *drop_append_mode = FALSE;
6981           gtk_tree_path_next (path);
6982         }
6983     }
6984 
6985   return path;
6986 }
6987 
6988 static gboolean
exo_icon_view_maybe_begin_drag(ExoIconView * icon_view,GdkEventMotion * event)6989 exo_icon_view_maybe_begin_drag (ExoIconView    *icon_view,
6990                                 GdkEventMotion *event)
6991 {
6992   GdkDragContext *context;
6993   GtkTreePath *path = NULL;
6994   gint button;
6995   GtkTreeModel *model;
6996   gboolean retval = FALSE;
6997 
6998   if (!icon_view->priv->source_set)
6999     goto out;
7000 
7001   if (icon_view->priv->pressed_button < 0)
7002     goto out;
7003 
7004   if (!gtk_drag_check_threshold (GTK_WIDGET (icon_view),
7005                                  icon_view->priv->press_start_x,
7006                                  icon_view->priv->press_start_y,
7007                                  event->x, event->y))
7008     goto out;
7009 
7010   model = exo_icon_view_get_model (icon_view);
7011 
7012   if (model == NULL)
7013     goto out;
7014 
7015   button = icon_view->priv->pressed_button;
7016   icon_view->priv->pressed_button = -1;
7017 
7018   path = exo_icon_view_get_path_at_pos (icon_view,
7019                                         icon_view->priv->press_start_x,
7020                                         icon_view->priv->press_start_y);
7021 
7022   if (path == NULL)
7023     goto out;
7024 
7025   if (!GTK_IS_TREE_DRAG_SOURCE (model) ||
7026       !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model),
7027                                            path))
7028     goto out;
7029 
7030   /* FIXME Check whether we're a start button, if not return FALSE and
7031    * free path
7032    */
7033 
7034   /* Now we can begin the drag */
7035 
7036   retval = TRUE;
7037 
7038   context = gtk_drag_begin_with_coordinates (GTK_WIDGET (icon_view),
7039                                              icon_view->priv->source_targets,
7040                                              icon_view->priv->source_actions,
7041                                              button,
7042                                              (GdkEvent *)event,
7043                                              event->x, event->y);
7044 
7045   set_source_row (context, model, path);
7046 
7047  out:
7048   if (path)
7049     gtk_tree_path_free (path);
7050 
7051   return retval;
7052 }
7053 
7054 /* Source side drag signals */
7055 static void
exo_icon_view_drag_begin(GtkWidget * widget,GdkDragContext * context)7056 exo_icon_view_drag_begin (GtkWidget      *widget,
7057                           GdkDragContext *context)
7058 {
7059   ExoIconView *icon_view;
7060   ExoIconViewItem *item;
7061   cairo_surface_t *icon;
7062   GtkTreePath *path;
7063 
7064   icon_view = EXO_ICON_VIEW (widget);
7065 
7066   /* if the user uses a custom DnD impl, we don't set the icon here */
7067   if (!icon_view->priv->dest_set && !icon_view->priv->source_set)
7068     return;
7069 
7070   item = exo_icon_view_get_item_at_coords (icon_view,
7071                                            icon_view->priv->press_start_x,
7072                                            icon_view->priv->press_start_y,
7073                                            TRUE,
7074                                            NULL);
7075 
7076   _exo_return_if_fail (item != NULL);
7077 
7078   path = gtk_tree_path_new_from_indices (g_list_index (icon_view->priv->items, item), -1);
7079   icon = exo_icon_view_create_drag_icon (icon_view, path);
7080   gtk_tree_path_free (path);
7081 
7082   gtk_drag_set_icon_surface (context, icon);
7083 
7084   g_object_unref (icon);
7085 }
7086 
7087 static void
exo_icon_view_drag_end(GtkWidget * widget,GdkDragContext * context)7088 exo_icon_view_drag_end (GtkWidget      *widget,
7089                         GdkDragContext *context)
7090 {
7091   ExoIconView *icon_view;
7092 
7093   icon_view = EXO_ICON_VIEW (widget);
7094 
7095   /* check if we're in single click mode */
7096   if (G_UNLIKELY (icon_view->priv->single_click))
7097     {
7098       /* reset the cursor if we're still realized */
7099       if (G_LIKELY (icon_view->priv->bin_window != NULL))
7100         gdk_window_set_cursor (icon_view->priv->bin_window, NULL);
7101 
7102       /* reset the last single clicked item */
7103       icon_view->priv->last_single_clicked = NULL;
7104     }
7105 
7106   /* reset the pressed_button state */
7107   icon_view->priv->pressed_button = -1;
7108 }
7109 
7110 static void
exo_icon_view_drag_data_get(GtkWidget * widget,GdkDragContext * context,GtkSelectionData * selection_data,guint info,guint drag_time)7111 exo_icon_view_drag_data_get (GtkWidget        *widget,
7112                              GdkDragContext   *context,
7113                              GtkSelectionData *selection_data,
7114                              guint             info,
7115                              guint             drag_time)
7116 {
7117   ExoIconView *icon_view;
7118   GtkTreeModel *model;
7119   GtkTreePath *source_row;
7120 
7121   icon_view = EXO_ICON_VIEW (widget);
7122   model = exo_icon_view_get_model (icon_view);
7123 
7124   if (model == NULL)
7125     return;
7126 
7127   if (!icon_view->priv->dest_set)
7128     return;
7129 
7130   source_row = get_source_row (context);
7131 
7132   if (source_row == NULL)
7133     return;
7134 
7135   /* We can implement the GTK_TREE_MODEL_ROW target generically for
7136    * any model; for DragSource models there are some other targets
7137    * we also support.
7138    */
7139 
7140   if (GTK_IS_TREE_DRAG_SOURCE (model) &&
7141       gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model),
7142                                           source_row,
7143                                           selection_data))
7144     goto done;
7145 
7146   /* If drag_data_get does nothing, try providing row data. */
7147   if (gtk_selection_data_get_target (selection_data) == gdk_atom_intern ("GTK_TREE_MODEL_ROW", FALSE))
7148     gtk_tree_set_row_drag_data (selection_data,
7149                                 model,
7150                                 source_row);
7151 
7152  done:
7153   gtk_tree_path_free (source_row);
7154 }
7155 
7156 static void
exo_icon_view_drag_data_delete(GtkWidget * widget,GdkDragContext * context)7157 exo_icon_view_drag_data_delete (GtkWidget      *widget,
7158                                 GdkDragContext *context)
7159 {
7160   GtkTreeModel *model;
7161   ExoIconView *icon_view;
7162   GtkTreePath *source_row;
7163 
7164   icon_view = EXO_ICON_VIEW (widget);
7165   model = exo_icon_view_get_model (icon_view);
7166 
7167   if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete"))
7168     return;
7169 
7170   if (!icon_view->priv->dest_set)
7171     return;
7172 
7173   source_row = get_source_row (context);
7174 
7175   if (source_row == NULL)
7176     return;
7177 
7178   gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model),
7179                                          source_row);
7180 
7181   gtk_tree_path_free (source_row);
7182 
7183   set_source_row (context, NULL, NULL);
7184 }
7185 
7186 /* Target side drag signals */
7187 static void
exo_icon_view_drag_leave(GtkWidget * widget,GdkDragContext * context,guint drag_time)7188 exo_icon_view_drag_leave (GtkWidget      *widget,
7189                           GdkDragContext *context,
7190                           guint           drag_time)
7191 {
7192   ExoIconView *icon_view;
7193 
7194   icon_view = EXO_ICON_VIEW (widget);
7195 
7196   /* unset any highlight row */
7197   exo_icon_view_set_drag_dest_item (icon_view,
7198                                     NULL,
7199                                     EXO_ICON_VIEW_DROP_LEFT);
7200 
7201   remove_scroll_timeout (icon_view);
7202 }
7203 
7204 static gboolean
exo_icon_view_drag_motion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint drag_time)7205 exo_icon_view_drag_motion (GtkWidget      *widget,
7206                            GdkDragContext *context,
7207                            gint            x,
7208                            gint            y,
7209                            guint           drag_time)
7210 {
7211   ExoIconViewDropPosition pos;
7212   GdkDragAction           suggested_action = 0;
7213   GtkTreePath            *path = NULL;
7214   ExoIconView            *icon_view = EXO_ICON_VIEW (widget);
7215   gboolean                empty;
7216   GdkAtom                 target;
7217 
7218   if (!set_destination (icon_view, context, x, y, &suggested_action, &target))
7219     return FALSE;
7220 
7221   exo_icon_view_get_drag_dest_item (icon_view, &path, &pos);
7222 
7223   /* we only know this *after* set_desination_row */
7224   empty = icon_view->priv->empty_view_drop;
7225 
7226   if (path == NULL && !empty)
7227     {
7228       /* Can't drop here. */
7229       gdk_drag_status (context, 0, drag_time);
7230     }
7231   else
7232     {
7233       if (icon_view->priv->scroll_timeout_id == 0)
7234         icon_view->priv->scroll_timeout_id = gdk_threads_add_timeout (50, drag_scroll_timeout, icon_view);
7235 
7236       if (target == gdk_atom_intern ("GTK_TREE_MODEL_ROW", FALSE))
7237         {
7238           /* Request data so we can use the source row when
7239            * determining whether to accept the drop
7240            */
7241           set_status_pending (context, suggested_action);
7242           gtk_drag_get_data (widget, context, target, drag_time);
7243         }
7244       else
7245         {
7246           set_status_pending (context, 0);
7247           gdk_drag_status (context, suggested_action, drag_time);
7248         }
7249     }
7250 
7251   if (path != NULL)
7252     gtk_tree_path_free (path);
7253 
7254   return TRUE;
7255 }
7256 
7257 static gboolean
exo_icon_view_drag_drop(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint drag_time)7258 exo_icon_view_drag_drop (GtkWidget      *widget,
7259                          GdkDragContext *context,
7260                          gint            x,
7261                          gint            y,
7262                          guint           drag_time)
7263 {
7264   ExoIconView *icon_view;
7265   GtkTreePath *path;
7266   GdkDragAction suggested_action = 0;
7267   GdkAtom target = GDK_NONE;
7268   GtkTreeModel *model;
7269   gboolean drop_append_mode;
7270 
7271   icon_view = EXO_ICON_VIEW (widget);
7272   model = exo_icon_view_get_model (icon_view);
7273 
7274   remove_scroll_timeout (EXO_ICON_VIEW (widget));
7275 
7276   if (!icon_view->priv->dest_set)
7277     return FALSE;
7278 
7279   if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_drop"))
7280     return FALSE;
7281 
7282   if (!set_destination (icon_view, context, x, y, &suggested_action, &target))
7283     return FALSE;
7284 
7285   path = get_logical_destination (icon_view, &drop_append_mode);
7286 
7287   if (target != GDK_NONE && path != NULL)
7288     {
7289       /* in case a motion had requested drag data, change things so we
7290        * treat drag data receives as a drop.
7291        */
7292       set_status_pending (context, 0);
7293       set_dest_row (context, model, path,
7294                     icon_view->priv->empty_view_drop, drop_append_mode);
7295     }
7296 
7297   if (path)
7298     gtk_tree_path_free (path);
7299 
7300   /* Unset this thing */
7301   exo_icon_view_set_drag_dest_item (icon_view, NULL, EXO_ICON_VIEW_DROP_LEFT);
7302 
7303   if (target != GDK_NONE)
7304     {
7305       gtk_drag_get_data (widget, context, target, drag_time);
7306       return TRUE;
7307     }
7308   else
7309     return FALSE;
7310 }
7311 
7312 static void
exo_icon_view_drag_data_received(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint drag_time)7313 exo_icon_view_drag_data_received (GtkWidget        *widget,
7314                                   GdkDragContext   *context,
7315                                   gint              x,
7316                                   gint              y,
7317                                   GtkSelectionData *selection_data,
7318                                   guint             info,
7319                                   guint             drag_time)
7320 {
7321   GtkTreePath *path;
7322   gboolean accepted = FALSE;
7323   GtkTreeModel *model;
7324   ExoIconView *icon_view;
7325   GtkTreePath *dest_row;
7326   GdkDragAction suggested_action;
7327   gboolean drop_append_mode;
7328 
7329   icon_view = EXO_ICON_VIEW (widget);
7330   model = exo_icon_view_get_model (icon_view);
7331 
7332   if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_data_received"))
7333     return;
7334 
7335   if (!icon_view->priv->dest_set)
7336     return;
7337 
7338   suggested_action = get_status_pending (context);
7339 
7340   if (suggested_action)
7341     {
7342       /* We are getting this data due to a request in drag_motion,
7343        * rather than due to a request in drag_drop, so we are just
7344        * supposed to call drag_status, not actually paste in the
7345        * data.
7346        */
7347       path = get_logical_destination (icon_view, &drop_append_mode);
7348 
7349       if (path == NULL)
7350         suggested_action = 0;
7351 
7352       if (suggested_action)
7353         {
7354           if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
7355                                                      path,
7356                                                      selection_data))
7357             suggested_action = 0;
7358         }
7359 
7360       gdk_drag_status (context, suggested_action, drag_time);
7361 
7362       if (path)
7363         gtk_tree_path_free (path);
7364 
7365       /* If you can't drop, remove user drop indicator until the next motion */
7366       if (suggested_action == 0)
7367         exo_icon_view_set_drag_dest_item (icon_view,
7368                                           NULL,
7369                                           EXO_ICON_VIEW_DROP_LEFT);
7370       return;
7371     }
7372 
7373 
7374   dest_row = get_dest_row (context);
7375 
7376   if (dest_row == NULL)
7377     return;
7378 
7379   if (gtk_selection_data_get_length (selection_data) >= 0)
7380     {
7381       if (gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model),
7382                                                  dest_row,
7383                                                  selection_data))
7384         accepted = TRUE;
7385     }
7386 
7387   gtk_drag_finish (context,
7388                    accepted,
7389                    (gdk_drag_context_get_selected_action (context) == GDK_ACTION_MOVE),
7390                    drag_time);
7391 
7392   gtk_tree_path_free (dest_row);
7393 
7394   /* drop dest_row */
7395   set_dest_row (context, NULL, NULL, FALSE, FALSE);
7396 }
7397 
7398 
7399 
7400 /**
7401  * exo_icon_view_enable_model_drag_source:
7402  * @icon_view         : a #GtkIconTreeView
7403  * @start_button_mask : Mask of allowed buttons to start drag
7404  * @targets           : the table of targets that the drag will support
7405  * @n_targets         : the number of items in @targets
7406  * @actions           : the bitmask of possible actions for a drag from this widget
7407  *
7408  * Turns @icon_view into a drag source for automatic DND.
7409  *
7410  * Since: 0.3.1
7411  **/
7412 void
exo_icon_view_enable_model_drag_source(ExoIconView * icon_view,GdkModifierType start_button_mask,const GtkTargetEntry * targets,gint n_targets,GdkDragAction actions)7413 exo_icon_view_enable_model_drag_source (ExoIconView              *icon_view,
7414                                         GdkModifierType           start_button_mask,
7415                                         const GtkTargetEntry     *targets,
7416                                         gint                      n_targets,
7417                                         GdkDragAction             actions)
7418 {
7419   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
7420 
7421   gtk_drag_source_set (GTK_WIDGET (icon_view), 0, NULL, 0, actions);
7422 
7423   clear_source_info (icon_view);
7424   icon_view->priv->start_button_mask = start_button_mask;
7425   icon_view->priv->source_targets = gtk_target_list_new (targets, n_targets);
7426   icon_view->priv->source_actions = actions;
7427 
7428   icon_view->priv->source_set = TRUE;
7429 
7430   unset_reorderable (icon_view);
7431 }
7432 
7433 
7434 
7435 /**
7436  * exo_icon_view_enable_model_drag_dest:
7437  * @icon_view : a #ExoIconView
7438  * @targets   : the table of targets that the drag will support
7439  * @n_targets : the number of items in @targets
7440  * @actions   : the bitmask of possible actions for a drag from this widget
7441  *
7442  * Turns @icon_view into a drop destination for automatic DND.
7443  *
7444  * Since: 0.3.1
7445  **/
7446 void
exo_icon_view_enable_model_drag_dest(ExoIconView * icon_view,const GtkTargetEntry * targets,gint n_targets,GdkDragAction actions)7447 exo_icon_view_enable_model_drag_dest (ExoIconView          *icon_view,
7448                                       const GtkTargetEntry *targets,
7449                                       gint                  n_targets,
7450                                       GdkDragAction         actions)
7451 {
7452   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
7453 
7454   gtk_drag_dest_set (GTK_WIDGET (icon_view), 0, NULL, 0, actions);
7455 
7456   clear_dest_info (icon_view);
7457 
7458   icon_view->priv->dest_targets = gtk_target_list_new (targets, n_targets);
7459   icon_view->priv->dest_actions = actions;
7460 
7461   icon_view->priv->dest_set = TRUE;
7462 
7463   unset_reorderable (icon_view);
7464 }
7465 
7466 
7467 
7468 /**
7469  * exo_icon_view_unset_model_drag_source:
7470  * @icon_view : a #ExoIconView
7471  *
7472  * Undoes the effect of #exo_icon_view_enable_model_drag_source().
7473  *
7474  * Since: 0.3.1
7475  **/
7476 void
exo_icon_view_unset_model_drag_source(ExoIconView * icon_view)7477 exo_icon_view_unset_model_drag_source (ExoIconView *icon_view)
7478 {
7479   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
7480 
7481   if (icon_view->priv->source_set)
7482     {
7483       gtk_drag_source_unset (GTK_WIDGET (icon_view));
7484       clear_source_info (icon_view);
7485     }
7486 
7487   unset_reorderable (icon_view);
7488 }
7489 
7490 
7491 
7492 /**
7493  * exo_icon_view_unset_model_drag_dest:
7494  * @icon_view : a #ExoIconView
7495  *
7496  * Undoes the effect of #exo_icon_view_enable_model_drag_dest().
7497  *
7498  * Since: 0.3.1
7499  **/
7500 void
exo_icon_view_unset_model_drag_dest(ExoIconView * icon_view)7501 exo_icon_view_unset_model_drag_dest (ExoIconView *icon_view)
7502 {
7503   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
7504 
7505   if (icon_view->priv->dest_set)
7506     {
7507       gtk_drag_dest_unset (GTK_WIDGET (icon_view));
7508       clear_dest_info (icon_view);
7509     }
7510 
7511   unset_reorderable (icon_view);
7512 }
7513 
7514 
7515 
7516 /**
7517  * exo_icon_view_set_drag_dest_item:
7518  * @icon_view : a #ExoIconView
7519  * @path      : The path of the item to highlight, or %NULL.
7520  * @pos       : Specifies whether to drop, relative to the item
7521  *
7522  * Sets the item that is highlighted for feedback.
7523  *
7524  * Since: 0.3.1
7525  */
7526 void
exo_icon_view_set_drag_dest_item(ExoIconView * icon_view,GtkTreePath * path,ExoIconViewDropPosition pos)7527 exo_icon_view_set_drag_dest_item (ExoIconView            *icon_view,
7528                                   GtkTreePath            *path,
7529                                   ExoIconViewDropPosition pos)
7530 {
7531   ExoIconViewItem *item;
7532   GtkTreePath     *previous_path;
7533 
7534   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
7535 
7536   /* Note; this function is exported to allow a custom DND
7537    * implementation, so it can't touch TreeViewDragInfo
7538    */
7539 
7540   if (icon_view->priv->dest_item != NULL)
7541     {
7542       /* determine and reset the previous path */
7543       previous_path = gtk_tree_row_reference_get_path (icon_view->priv->dest_item);
7544       gtk_tree_row_reference_free (icon_view->priv->dest_item);
7545       icon_view->priv->dest_item = NULL;
7546 
7547       /* check if the path is still valid */
7548       if (G_LIKELY (previous_path != NULL))
7549         {
7550           /* schedule a redraw for the previous path */
7551           item = g_list_nth_data (icon_view->priv->items, gtk_tree_path_get_indices (previous_path)[0]);
7552           if (G_LIKELY (item != NULL))
7553             exo_icon_view_queue_draw_item (icon_view, item);
7554           gtk_tree_path_free (previous_path);
7555         }
7556     }
7557 
7558   /* special case a drop on an empty model */
7559   icon_view->priv->empty_view_drop = FALSE;
7560   if (pos == EXO_ICON_VIEW_NO_DROP
7561       && path != NULL
7562       && gtk_tree_path_get_depth (path) == 1
7563       && gtk_tree_path_get_indices (path)[0] == 0)
7564     {
7565       gint n_children;
7566 
7567       n_children = gtk_tree_model_iter_n_children (icon_view->priv->model,
7568                                                    NULL);
7569 
7570       if (n_children == 0)
7571         icon_view->priv->empty_view_drop = TRUE;
7572     }
7573 
7574   icon_view->priv->dest_pos = pos;
7575 
7576   if (G_LIKELY (path != NULL))
7577     {
7578       /* take a row reference for the new item path */
7579       icon_view->priv->dest_item = gtk_tree_row_reference_new_proxy (G_OBJECT (icon_view), icon_view->priv->model, path);
7580 
7581       /* schedule a redraw on the new path */
7582       item = g_list_nth_data (icon_view->priv->items, gtk_tree_path_get_indices (path)[0]);
7583       if (G_LIKELY (item != NULL))
7584         exo_icon_view_queue_draw_item (icon_view, item);
7585     }
7586 }
7587 
7588 
7589 
7590 /**
7591  * exo_icon_view_get_drag_dest_item:
7592  * @icon_view : a #ExoIconView
7593  * @path      : Return location for the path of the highlighted item, or %NULL.
7594  * @pos       : Return location for the drop position, or %NULL
7595  *
7596  * Gets information about the item that is highlighted for feedback.
7597  *
7598  * Since: 0.3.1
7599  **/
7600 void
exo_icon_view_get_drag_dest_item(ExoIconView * icon_view,GtkTreePath ** path,ExoIconViewDropPosition * pos)7601 exo_icon_view_get_drag_dest_item (ExoIconView              *icon_view,
7602                                   GtkTreePath             **path,
7603                                   ExoIconViewDropPosition  *pos)
7604 {
7605   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
7606 
7607   if (path)
7608     {
7609       if (icon_view->priv->dest_item)
7610         *path = gtk_tree_row_reference_get_path (icon_view->priv->dest_item);
7611       else
7612         *path = NULL;
7613     }
7614 
7615   if (pos)
7616     *pos = icon_view->priv->dest_pos;
7617 }
7618 
7619 
7620 
7621 /**
7622  * exo_icon_view_get_dest_item_at_pos:
7623  * @icon_view : a #ExoIconView
7624  * @drag_x    : the position to determine the destination item for
7625  * @drag_y    : the position to determine the destination item for
7626  * @path      : Return location for the path of the highlighted item, or %NULL.
7627  * @pos       : Return location for the drop position, or %NULL
7628  *
7629  * Determines the destination item for a given position.
7630  *
7631  * Both @drag_x and @drag_y are given in icon window coordinates. Use
7632  * #exo_icon_view_widget_to_icon_coords() if you need to translate
7633  * widget coordinates first.
7634  *
7635  * Returns: whether there is an item at the given position.
7636  *
7637  * Since: 0.3.1
7638  **/
7639 gboolean
exo_icon_view_get_dest_item_at_pos(ExoIconView * icon_view,gint drag_x,gint drag_y,GtkTreePath ** path,ExoIconViewDropPosition * pos)7640 exo_icon_view_get_dest_item_at_pos (ExoIconView              *icon_view,
7641                                     gint                      drag_x,
7642                                     gint                      drag_y,
7643                                     GtkTreePath             **path,
7644                                     ExoIconViewDropPosition  *pos)
7645 {
7646   ExoIconViewItem *item;
7647 
7648   /* Note; this function is exported to allow a custom DND
7649    * implementation, so it can't touch TreeViewDragInfo
7650    */
7651 
7652   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
7653   g_return_val_if_fail (drag_x >= 0, FALSE);
7654   g_return_val_if_fail (drag_y >= 0, FALSE);
7655   g_return_val_if_fail (icon_view->priv->bin_window != NULL, FALSE);
7656 
7657   if (G_LIKELY (path != NULL))
7658     *path = NULL;
7659 
7660   item = exo_icon_view_get_item_at_coords (icon_view, drag_x, drag_y, FALSE, NULL);
7661 
7662   if (G_UNLIKELY (item == NULL))
7663     return FALSE;
7664 
7665   if (G_LIKELY (path != NULL))
7666     *path = gtk_tree_path_new_from_indices (g_list_index (icon_view->priv->items, item), -1);
7667 
7668   if (G_LIKELY (pos != NULL))
7669     {
7670       if (drag_x < item->area.x + item->area.width / 4)
7671         *pos = EXO_ICON_VIEW_DROP_LEFT;
7672       else if (drag_x > item->area.x + item->area.width * 3 / 4)
7673         *pos = EXO_ICON_VIEW_DROP_RIGHT;
7674       else if (drag_y < item->area.y + item->area.height / 4)
7675         *pos = EXO_ICON_VIEW_DROP_ABOVE;
7676       else if (drag_y > item->area.y + item->area.height * 3 / 4)
7677         *pos = EXO_ICON_VIEW_DROP_BELOW;
7678       else
7679         *pos = EXO_ICON_VIEW_DROP_INTO;
7680     }
7681 
7682   return TRUE;
7683 }
7684 
7685 
7686 
7687 /**
7688  * exo_icon_view_create_drag_icon:
7689  * @icon_view : a #ExoIconView
7690  * @path      : a #GtkTreePath in @icon_view
7691  *
7692  * Creates a #cairo_surface_t representation of the item at @path.
7693  * This image is used for a drag icon.
7694  *
7695  * Returns: a newly-allocated pixmap of the drag icon.
7696  *
7697  * Since: 0.3.1
7698  **/
7699 cairo_surface_t*
exo_icon_view_create_drag_icon(ExoIconView * icon_view,GtkTreePath * path)7700 exo_icon_view_create_drag_icon (ExoIconView *icon_view,
7701                                 GtkTreePath *path)
7702 {
7703   cairo_surface_t *surface;
7704   cairo_t         *cr;
7705   GList           *lp;
7706   gint             idx;
7707   ExoIconViewItem *item;
7708 
7709   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), NULL);
7710   g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, NULL);
7711 
7712   /* verify that the widget is realized */
7713   if (G_UNLIKELY (!gtk_widget_get_realized (GTK_WIDGET (icon_view))))
7714     return NULL;
7715 
7716   idx = gtk_tree_path_get_indices (path)[0];
7717 
7718   for (lp = icon_view->priv->items; lp != NULL; lp = lp->next)
7719     {
7720       item = lp->data;
7721       if (G_UNLIKELY (idx == g_list_index (icon_view->priv->items, item)))
7722         {
7723           surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
7724                                                 item->area.width + 2,
7725                                                 item->area.height + 2);
7726 
7727           cr = cairo_create (surface);
7728 
7729           /* TODO: background / rectangles */
7730 
7731           exo_icon_view_paint_item (icon_view, item, cr, 1, 1, FALSE);
7732 
7733           cairo_destroy (cr);
7734 
7735           return surface;
7736         }
7737     }
7738 
7739   return NULL;
7740 }
7741 
7742 
7743 
7744 /**
7745  * exo_icon_view_set_pixbuf_column:
7746  * @icon_view : a #ExoIconView
7747  * @column    : The column that contains the pixbuf to render.
7748  *
7749  * Sets the column that contains the pixbuf to render.
7750  *
7751  * Since: 0.10.2
7752  **/
7753 void
exo_icon_view_set_pixbuf_column(ExoIconView * icon_view,gint column)7754 exo_icon_view_set_pixbuf_column (ExoIconView *icon_view, gint column)
7755 {
7756   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
7757 
7758   icon_view->priv->pixbuf_column = column;
7759 
7760   update_pixbuf_cell(icon_view);
7761 }
7762 
7763 
7764 
7765 /**
7766  * exo_icon_view_set_icon_column:
7767  * @icon_view : a #ExoIconView
7768  * @column    : The column that contains file to render.
7769  *
7770  * Sets the column that contains the file to render.
7771  *
7772  * Since: 0.10.2
7773  **/
7774 void
exo_icon_view_set_icon_column(ExoIconView * icon_view,gint column)7775 exo_icon_view_set_icon_column (ExoIconView *icon_view, gint column)
7776 {
7777   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
7778 
7779   icon_view->priv->icon_column = column;
7780 
7781   update_pixbuf_cell(icon_view);
7782 }
7783 
7784 
7785 
7786 /**
7787  * exo_icon_view_get_reorderable:
7788  * @icon_view : a #ExoIconView
7789  *
7790  * Retrieves whether the user can reorder the list via drag-and-drop.
7791  * See exo_icon_view_set_reorderable().
7792  *
7793  * Returns: %TRUE if the list can be reordered.
7794  *
7795  * Since: 0.3.1
7796  **/
7797 gboolean
exo_icon_view_get_reorderable(ExoIconView * icon_view)7798 exo_icon_view_get_reorderable (ExoIconView *icon_view)
7799 {
7800   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
7801 
7802   return icon_view->priv->reorderable;
7803 }
7804 
7805 
7806 
7807 /**
7808  * exo_icon_view_set_reorderable:
7809  * @icon_view   : A #ExoIconView.
7810  * @reorderable : %TRUE, if the list of items can be reordered.
7811  *
7812  * This function is a convenience function to allow you to reorder models that
7813  * support the #GtkTreeDragSourceIface and the #GtkTreeDragDestIface.  Both
7814  * #GtkTreeStore and #GtkListStore support these.  If @reorderable is %TRUE, then
7815  * the user can reorder the model by dragging and dropping rows.  The
7816  * developer can listen to these changes by connecting to the model's
7817  * ::row-inserted and ::row-deleted signals.
7818  *
7819  * This function does not give you any degree of control over the order -- any
7820  * reordering is allowed.  If more control is needed, you should probably
7821  * handle drag and drop manually.
7822  *
7823  * Since: 0.3.1
7824  **/
7825 void
exo_icon_view_set_reorderable(ExoIconView * icon_view,gboolean reorderable)7826 exo_icon_view_set_reorderable (ExoIconView *icon_view,
7827                                gboolean     reorderable)
7828 {
7829   static const GtkTargetEntry item_targets[] =
7830   {
7831     { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0, },
7832   };
7833 
7834   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
7835 
7836   reorderable = (reorderable != FALSE);
7837 
7838   if (G_UNLIKELY (icon_view->priv->reorderable == reorderable))
7839     return;
7840 
7841   if (G_LIKELY (reorderable))
7842     {
7843       exo_icon_view_enable_model_drag_source (icon_view, GDK_BUTTON1_MASK, item_targets, G_N_ELEMENTS (item_targets), GDK_ACTION_MOVE);
7844       exo_icon_view_enable_model_drag_dest (icon_view, item_targets, G_N_ELEMENTS (item_targets), GDK_ACTION_MOVE);
7845     }
7846   else
7847     {
7848       exo_icon_view_unset_model_drag_source (icon_view);
7849       exo_icon_view_unset_model_drag_dest (icon_view);
7850     }
7851 
7852   icon_view->priv->reorderable = reorderable;
7853 
7854   g_object_notify (G_OBJECT (icon_view), "reorderable");
7855 }
7856 
7857 
7858 
7859 /*----------------------*
7860  * Single-click support *
7861  *----------------------*/
7862 
7863 /**
7864  * exo_icon_view_get_single_click:
7865  * @icon_view : a #ExoIconView.
7866  *
7867  * Returns %TRUE if @icon_view is currently in single click mode,
7868  * else %FALSE will be returned.
7869  *
7870  * Returns: whether @icon_view is currently in single click mode.
7871  *
7872  * Since: 0.3.1.3
7873  **/
7874 gboolean
exo_icon_view_get_single_click(const ExoIconView * icon_view)7875 exo_icon_view_get_single_click (const ExoIconView *icon_view)
7876 {
7877   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
7878   return icon_view->priv->single_click;
7879 }
7880 
7881 
7882 
7883 /**
7884  * exo_icon_view_set_single_click:
7885  * @icon_view    : a #ExoIconView.
7886  * @single_click : %TRUE for single click, %FALSE for double click mode.
7887  *
7888  * If @single_click is %TRUE, @icon_view will be in single click mode
7889  * afterwards, else @icon_view will be in double click mode.
7890  *
7891  * Since: 0.3.1.3
7892  **/
7893 void
exo_icon_view_set_single_click(ExoIconView * icon_view,gboolean single_click)7894 exo_icon_view_set_single_click (ExoIconView *icon_view,
7895                                 gboolean     single_click)
7896 {
7897   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
7898 
7899   /* normalize the value */
7900   single_click = !!single_click;
7901 
7902   /* check if we have a new setting here */
7903   if (icon_view->priv->single_click != single_click)
7904     {
7905       icon_view->priv->single_click = single_click;
7906       g_object_notify (G_OBJECT (icon_view), "single-click");
7907     }
7908 }
7909 
7910 
7911 
7912 /**
7913  * exo_icon_view_get_single_click_timeout:
7914  * @icon_view : a #ExoIconView.
7915  *
7916  * Returns the amount of time in milliseconds after which the
7917  * item under the mouse cursor will be selected automatically
7918  * in single click mode. A value of %0 means that the behavior
7919  * is disabled and the user must alter the selection manually.
7920  *
7921  * Returns: the single click autoselect timeout or %0 if
7922  *          the behavior is disabled.
7923  *
7924  * Since: 0.3.1.5
7925  **/
7926 guint
exo_icon_view_get_single_click_timeout(const ExoIconView * icon_view)7927 exo_icon_view_get_single_click_timeout (const ExoIconView *icon_view)
7928 {
7929   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), 0u);
7930   return icon_view->priv->single_click_timeout;
7931 }
7932 
7933 
7934 
7935 /**
7936  * exo_icon_view_set_single_click_timeout:
7937  * @icon_view            : a #ExoIconView.
7938  * @single_click_timeout : the new timeout or %0 to disable.
7939  *
7940  * If @single_click_timeout is a value greater than zero, it specifies
7941  * the amount of time in milliseconds after which the item under the
7942  * mouse cursor will be selected automatically in single click mode.
7943  * A value of %0 for @single_click_timeout disables the autoselection
7944  * for @icon_view.
7945  *
7946  * This setting does not have any effect unless the @icon_view is in
7947  * single-click mode, see exo_icon_view_set_single_click().
7948  *
7949  * Since: 0.3.1.5
7950  **/
7951 void
exo_icon_view_set_single_click_timeout(ExoIconView * icon_view,guint single_click_timeout)7952 exo_icon_view_set_single_click_timeout (ExoIconView *icon_view,
7953                                         guint        single_click_timeout)
7954 {
7955   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
7956 
7957   /* check if we have a new setting */
7958   if (icon_view->priv->single_click_timeout != single_click_timeout)
7959     {
7960       /* apply the new setting */
7961       icon_view->priv->single_click_timeout = single_click_timeout;
7962 
7963       /* be sure to cancel any pending single click timeout */
7964       if (G_UNLIKELY (icon_view->priv->single_click_timeout_id != 0))
7965         g_source_remove (icon_view->priv->single_click_timeout_id);
7966 
7967       /* notify listeners */
7968       g_object_notify (G_OBJECT (icon_view), "single-click-timeout");
7969     }
7970 }
7971 
7972 
7973 
7974 static gboolean
exo_icon_view_single_click_timeout(gpointer user_data)7975 exo_icon_view_single_click_timeout (gpointer user_data)
7976 {
7977   ExoIconViewItem *item;
7978   gboolean         dirty = FALSE;
7979   ExoIconView     *icon_view = EXO_ICON_VIEW (user_data);
7980   GtkWidget       *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (icon_view));
7981 
7982   /* verify that we are in single-click mode on an active window and have a prelit item */
7983   if (GTK_IS_WINDOW (toplevel) && gtk_window_is_active (GTK_WINDOW (toplevel)) && icon_view->priv->single_click && icon_view->priv->prelit_item != NULL)
7984     {
7985       gtk_widget_grab_focus (GTK_WIDGET (icon_view));
7986 
7987       /* work on the prelit item */
7988       item = icon_view->priv->prelit_item;
7989 
7990       /* be sure the item is fully visible */
7991       exo_icon_view_scroll_to_item (icon_view, item);
7992 
7993       /* change the selection appropriately */
7994       if (G_UNLIKELY (icon_view->priv->selection_mode == GTK_SELECTION_NONE))
7995         {
7996           exo_icon_view_set_cursor_item (icon_view, item, -1);
7997         }
7998       else if ((icon_view->priv->single_click_timeout_state & GDK_SHIFT_MASK) != 0
7999             && icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE)
8000         {
8001           if (!(icon_view->priv->single_click_timeout_state & GDK_CONTROL_MASK))
8002             /* unselect all previously selected items */
8003             exo_icon_view_unselect_all_internal (icon_view);
8004 
8005           /* select all items between the anchor and the prelit item */
8006           exo_icon_view_set_cursor_item (icon_view, item, -1);
8007           if (icon_view->priv->anchor_item == NULL)
8008             icon_view->priv->anchor_item = item;
8009           else
8010             exo_icon_view_select_all_between (icon_view, icon_view->priv->anchor_item, item);
8011 
8012           /* selection was changed */
8013           dirty = TRUE;
8014         }
8015       else
8016         {
8017           if ((icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE ||
8018               ((icon_view->priv->selection_mode == GTK_SELECTION_SINGLE) && item->selected)) &&
8019               (icon_view->priv->single_click_timeout_state & GDK_CONTROL_MASK) != 0)
8020             {
8021               item->selected = !item->selected;
8022               exo_icon_view_queue_draw_item (icon_view, item);
8023               dirty = TRUE;
8024             }
8025           else if (!item->selected)
8026             {
8027               exo_icon_view_unselect_all_internal (icon_view);
8028               exo_icon_view_queue_draw_item (icon_view, item);
8029               item->selected = TRUE;
8030               dirty = TRUE;
8031             }
8032           exo_icon_view_set_cursor_item (icon_view, item, -1);
8033           icon_view->priv->anchor_item = item;
8034         }
8035     }
8036 
8037   /* emit "selection-changed" and stop drawing keyboard
8038    * focus indicator if the selection was altered
8039    */
8040   if (G_LIKELY (dirty))
8041     {
8042       /* reset "draw keyfocus" flag */
8043       EXO_ICON_VIEW_UNSET_FLAG (icon_view, EXO_ICON_VIEW_DRAW_KEYFOCUS);
8044 
8045       /* emit "selection-changed" */
8046       g_signal_emit (G_OBJECT (icon_view), icon_view_signals[SELECTION_CHANGED], 0);
8047     }
8048 
8049   return FALSE;
8050 }
8051 
8052 
8053 
8054 static void
exo_icon_view_single_click_timeout_destroy(gpointer user_data)8055 exo_icon_view_single_click_timeout_destroy (gpointer user_data)
8056 {
8057   EXO_ICON_VIEW (user_data)->priv->single_click_timeout_id = 0;
8058 }
8059 
8060 
8061 
8062 /*----------------------------*
8063  * Interactive search support *
8064  *----------------------------*/
8065 
8066 /**
8067  * exo_icon_view_get_enable_search:
8068  * @icon_view : an #ExoIconView.
8069  *
8070  * Returns whether or not the @icon_view allows to start
8071  * interactive searching by typing in text.
8072  *
8073  * Returns: whether or not to let the user search interactively.
8074  *
8075  * Since: 0.3.1.3
8076  **/
8077 gboolean
exo_icon_view_get_enable_search(const ExoIconView * icon_view)8078 exo_icon_view_get_enable_search (const ExoIconView *icon_view)
8079 {
8080   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
8081   return icon_view->priv->enable_search;
8082 }
8083 
8084 
8085 
8086 /**
8087  * exo_icon_view_set_enable_search:
8088  * @icon_view     : an #ExoIconView.
8089  * @enable_search : %TRUE if the user can search interactively.
8090  *
8091  * If @enable_search is set, then the user can type in text to search through
8092  * the @icon_view interactively (this is sometimes called "typeahead find").
8093  *
8094  * Note that even if this is %FALSE, the user can still initiate a search
8095  * using the "start-interactive-search" key binding.
8096  *
8097  * Since: 0.3.1.3
8098  **/
8099 void
exo_icon_view_set_enable_search(ExoIconView * icon_view,gboolean enable_search)8100 exo_icon_view_set_enable_search (ExoIconView *icon_view,
8101                                  gboolean     enable_search)
8102 {
8103   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
8104 
8105   enable_search = !!enable_search;
8106 
8107   if (G_LIKELY (icon_view->priv->enable_search != enable_search))
8108     {
8109       icon_view->priv->enable_search = enable_search;
8110       g_object_notify (G_OBJECT (icon_view), "enable-search");
8111     }
8112 }
8113 
8114 
8115 
8116 /**
8117  * exo_icon_view_get_search_column:
8118  * @icon_view : an #ExoIconView.
8119  *
8120  * Returns the column searched on by the interactive search code.
8121  *
8122  * Returns: the column the interactive search code searches in.
8123  *
8124  * Since: 0.3.1.3
8125  **/
8126 gint
exo_icon_view_get_search_column(const ExoIconView * icon_view)8127 exo_icon_view_get_search_column (const ExoIconView *icon_view)
8128 {
8129   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), -1);
8130   return icon_view->priv->search_column;
8131 }
8132 
8133 
8134 
8135 /**
8136  * exo_icon_view_set_search_column:
8137  * @icon_view     : an #ExoIconView.
8138  * @search_column : the column of the model to search in, or -1 to disable searching.
8139  *
8140  * Sets @search_column as the column where the interactive search code should search in.
8141  *
8142  * If the search column is set, user can use the "start-interactive-search" key
8143  * binding to bring up search popup. The "enable-search" property controls
8144  * whether simply typing text will also start an interactive search.
8145  *
8146  * Note that @search_column refers to a column of the model.
8147  *
8148  * Since: 0.3.1.3
8149  **/
8150 void
exo_icon_view_set_search_column(ExoIconView * icon_view,gint search_column)8151 exo_icon_view_set_search_column (ExoIconView *icon_view,
8152                                  gint         search_column)
8153 {
8154   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
8155   g_return_if_fail (search_column >= -1);
8156 
8157   if (G_LIKELY (icon_view->priv->search_column != search_column))
8158     {
8159       icon_view->priv->search_column = search_column;
8160       g_object_notify (G_OBJECT (icon_view), "search-column");
8161     }
8162 }
8163 
8164 
8165 
8166 /**
8167  * exo_icon_view_get_search_equal_func:
8168  * @icon_view : an #ExoIconView.
8169  *
8170  * Returns the compare function currently in use.
8171  *
8172  * Returns: the currently used compare function for the search code.
8173  *
8174  * Since: 0.3.1.3
8175  **/
8176 ExoIconViewSearchEqualFunc
exo_icon_view_get_search_equal_func(const ExoIconView * icon_view)8177 exo_icon_view_get_search_equal_func (const ExoIconView *icon_view)
8178 {
8179   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), NULL);
8180   return icon_view->priv->search_equal_func;
8181 }
8182 
8183 
8184 
8185 /**
8186  * exo_icon_view_set_search_equal_func:
8187  * @icon_view            : an #ExoIconView.
8188  * @search_equal_func    : the compare function to use during the search, or %NULL.
8189  * @search_equal_data    : user data to pass to @search_equal_func, or %NULL.
8190  * @search_equal_destroy : destroy notifier for @search_equal_data, or %NULL.
8191  *
8192  * Sets the compare function for the interactive search capabilities;
8193  * note that some like strcmp() returning 0 for equality
8194  * #ExoIconViewSearchEqualFunc returns %FALSE on matches.
8195  *
8196  * Specifying %NULL for @search_equal_func will reset @icon_view to use the default
8197  * search equal function.
8198  *
8199  * Since: 0.3.1.3
8200  **/
8201 void
exo_icon_view_set_search_equal_func(ExoIconView * icon_view,ExoIconViewSearchEqualFunc search_equal_func,gpointer search_equal_data,GDestroyNotify search_equal_destroy)8202 exo_icon_view_set_search_equal_func (ExoIconView               *icon_view,
8203                                      ExoIconViewSearchEqualFunc search_equal_func,
8204                                      gpointer                   search_equal_data,
8205                                      GDestroyNotify             search_equal_destroy)
8206 {
8207   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
8208   g_return_if_fail (search_equal_func != NULL || (search_equal_data == NULL && search_equal_destroy == NULL));
8209 
8210   /* destroy the previous data (if any) */
8211   if (G_UNLIKELY (icon_view->priv->search_equal_destroy != NULL))
8212     (*icon_view->priv->search_equal_destroy) (icon_view->priv->search_equal_data);
8213 
8214   icon_view->priv->search_equal_func = (search_equal_func != NULL) ? search_equal_func : exo_icon_view_search_equal_func;
8215   icon_view->priv->search_equal_data = search_equal_data;
8216   icon_view->priv->search_equal_destroy = search_equal_destroy;
8217 }
8218 
8219 
8220 
8221 /**
8222  * exo_icon_view_get_search_position_func:
8223  * @icon_view : an #ExoIconView.
8224  *
8225  * Returns the search dialog positioning function currently in use.
8226  *
8227  * Returns: the currently used function for positioning the search dialog.
8228  *
8229  * Since: 0.3.1.3
8230  **/
8231 ExoIconViewSearchPositionFunc
exo_icon_view_get_search_position_func(const ExoIconView * icon_view)8232 exo_icon_view_get_search_position_func (const ExoIconView *icon_view)
8233 {
8234   g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), NULL);
8235   return icon_view->priv->search_position_func;
8236 }
8237 
8238 
8239 
8240 /**
8241  * exo_icon_view_set_search_position_func:
8242  * @icon_view               : an #ExoIconView.
8243  * @search_position_func    : the function to use to position the search dialog, or %NULL.
8244  * @search_position_data    : user data to pass to @search_position_func, or %NULL.
8245  * @search_position_destroy : destroy notifier for @search_position_data, or %NULL.
8246  *
8247  * Sets the function to use when positioning the seach dialog.
8248  *
8249  * Specifying %NULL for @search_position_func will reset @icon_view to use the default
8250  * search position function.
8251  *
8252  * Since: 0.3.1.3
8253  **/
8254 void
exo_icon_view_set_search_position_func(ExoIconView * icon_view,ExoIconViewSearchPositionFunc search_position_func,gpointer search_position_data,GDestroyNotify search_position_destroy)8255 exo_icon_view_set_search_position_func (ExoIconView                  *icon_view,
8256                                         ExoIconViewSearchPositionFunc search_position_func,
8257                                         gpointer                      search_position_data,
8258                                         GDestroyNotify                search_position_destroy)
8259 {
8260   g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
8261   g_return_if_fail (search_position_func != NULL || (search_position_data == NULL && search_position_destroy == NULL));
8262 
8263   /* destroy the previous data (if any) */
8264   if (icon_view->priv->search_position_destroy != NULL)
8265     (*icon_view->priv->search_position_destroy) (icon_view->priv->search_position_data);
8266 
8267   icon_view->priv->search_position_func = (search_position_func != NULL) ? search_position_func : exo_icon_view_search_position_func;
8268   icon_view->priv->search_position_data = search_position_data;
8269   icon_view->priv->search_position_destroy = search_position_destroy;
8270 }
8271 
8272 
8273 
8274 static void
exo_icon_view_search_activate(GtkEntry * entry,ExoIconView * icon_view)8275 exo_icon_view_search_activate (GtkEntry    *entry,
8276                                ExoIconView *icon_view)
8277 {
8278   GtkTreePath *path;
8279 
8280   /* hide the interactive search dialog */
8281   exo_icon_view_search_dialog_hide (icon_view->priv->search_window, icon_view);
8282 
8283   /* check if we have a cursor item, and if so, activate it */
8284   if (exo_icon_view_get_cursor (icon_view, &path, NULL))
8285     {
8286       /* only activate the cursor item if it's selected */
8287       if (exo_icon_view_path_is_selected (icon_view, path))
8288         exo_icon_view_item_activated (icon_view, path);
8289       gtk_tree_path_free (path);
8290     }
8291 }
8292 
8293 
8294 
8295 static void
exo_icon_view_search_dialog_hide(GtkWidget * search_dialog,ExoIconView * icon_view)8296 exo_icon_view_search_dialog_hide (GtkWidget   *search_dialog,
8297                                   ExoIconView *icon_view)
8298 {
8299   _exo_return_if_fail (GTK_IS_WIDGET (search_dialog));
8300   _exo_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
8301 
8302   if (icon_view->priv->search_disable_popdown)
8303     return;
8304 
8305   /* disconnect the "changed" signal handler */
8306   if (icon_view->priv->search_entry_changed_id != 0)
8307     {
8308       g_signal_handler_disconnect (G_OBJECT (icon_view->priv->search_entry), icon_view->priv->search_entry_changed_id);
8309       icon_view->priv->search_entry_changed_id = 0;
8310     }
8311 
8312   /* disable the flush timeout */
8313   if (icon_view->priv->search_timeout_id != 0)
8314     g_source_remove (icon_view->priv->search_timeout_id);
8315 
8316   /* send focus-out event */
8317   _exo_gtk_widget_send_focus_change (icon_view->priv->search_entry, FALSE);
8318   gtk_widget_hide (search_dialog);
8319   gtk_entry_set_text (GTK_ENTRY (icon_view->priv->search_entry), "");
8320 }
8321 
8322 
8323 
8324 static void
exo_icon_view_search_ensure_directory(ExoIconView * icon_view)8325 exo_icon_view_search_ensure_directory (ExoIconView *icon_view)
8326 {
8327   GtkWidget *toplevel;
8328   GtkWidget *frame;
8329   GtkWidget *vbox;
8330 
8331   /* determine the toplevel window */
8332   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (icon_view));
8333 
8334   /* check if we already have a search window */
8335   if (G_LIKELY (icon_view->priv->search_window != NULL))
8336     {
8337       if (gtk_window_get_group (GTK_WINDOW (toplevel)) != NULL)
8338         gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)), GTK_WINDOW (icon_view->priv->search_window));
8339       else if (gtk_window_get_group (GTK_WINDOW (icon_view->priv->search_window)) != NULL)
8340         gtk_window_group_remove_window (gtk_window_get_group (GTK_WINDOW (icon_view->priv->search_window)), GTK_WINDOW (icon_view->priv->search_window));
8341       return;
8342     }
8343 
8344   /* allocate a new search window */
8345   icon_view->priv->search_window = gtk_window_new (GTK_WINDOW_POPUP);
8346   if (gtk_window_get_group (GTK_WINDOW (toplevel)) != NULL)
8347     gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)), GTK_WINDOW (icon_view->priv->search_window));
8348   gtk_window_set_modal (GTK_WINDOW (icon_view->priv->search_window), TRUE);
8349   gtk_window_set_screen (GTK_WINDOW (icon_view->priv->search_window), gtk_widget_get_screen (GTK_WIDGET (icon_view)));
8350 
8351   /* attach the popup window to the toplevel parent (only needed on wayland).
8352    * see https://bugzilla.xfce.org/show_bug.cgi?id=16768
8353    */
8354   gtk_window_set_transient_for (GTK_WINDOW (icon_view->priv->search_window), GTK_WINDOW (toplevel));
8355 
8356   /* connect signal handlers */
8357   g_signal_connect (G_OBJECT (icon_view->priv->search_window), "delete-event", G_CALLBACK (exo_icon_view_search_delete_event), icon_view);
8358   g_signal_connect (G_OBJECT (icon_view->priv->search_window), "scroll-event", G_CALLBACK (exo_icon_view_search_scroll_event), icon_view);
8359   g_signal_connect (G_OBJECT (icon_view->priv->search_window), "key-press-event", G_CALLBACK (exo_icon_view_search_key_press_event), icon_view);
8360   g_signal_connect (G_OBJECT (icon_view->priv->search_window), "button-press-event", G_CALLBACK (exo_icon_view_search_button_press_event), icon_view);
8361 
8362   /* allocate the frame widget */
8363   frame = g_object_new (GTK_TYPE_FRAME, "shadow-type", GTK_SHADOW_ETCHED_IN, NULL);
8364   gtk_container_add (GTK_CONTAINER (icon_view->priv->search_window), frame);
8365   gtk_widget_show (frame);
8366 
8367   /* allocate the vertical box */
8368   vbox = g_object_new (GTK_TYPE_BOX, "orientation", GTK_ORIENTATION_VERTICAL, "border-width", 3, NULL);
8369   gtk_container_add (GTK_CONTAINER (frame), vbox);
8370   gtk_widget_show (vbox);
8371 
8372   /* allocate the search entry widget */
8373   icon_view->priv->search_entry = gtk_entry_new ();
8374 #if GTK_CHECK_VERSION(3, 22, 20)
8375   gtk_entry_set_input_hints (GTK_ENTRY (icon_view->priv->search_entry), GTK_INPUT_HINT_NO_EMOJI);
8376 #endif
8377   g_signal_connect (G_OBJECT (icon_view->priv->search_entry), "activate", G_CALLBACK (exo_icon_view_search_activate), icon_view);
8378   gtk_box_pack_start (GTK_BOX (vbox), icon_view->priv->search_entry, TRUE, TRUE, 0);
8379   gtk_widget_realize (icon_view->priv->search_entry);
8380   gtk_widget_show (icon_view->priv->search_entry);
8381 }
8382 
8383 
8384 
8385 static void
exo_icon_view_search_init(GtkWidget * search_entry,ExoIconView * icon_view)8386 exo_icon_view_search_init (GtkWidget   *search_entry,
8387                            ExoIconView *icon_view)
8388 {
8389   GtkTreeModel *model;
8390   GtkTreeIter   iter;
8391   const gchar  *text;
8392   gint          length;
8393   gint          count = 0;
8394 
8395   _exo_return_if_fail (GTK_IS_ENTRY (search_entry));
8396   _exo_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
8397 
8398   /* determine the current text for the search entry */
8399   text = gtk_entry_get_text (GTK_ENTRY (search_entry));
8400   if (G_UNLIKELY (text == NULL))
8401     return;
8402 
8403   /* unselect all items */
8404   exo_icon_view_unselect_all (icon_view);
8405 
8406   /* renew the flush timeout */
8407   if ((icon_view->priv->search_timeout_id != 0))
8408     {
8409       /* drop the previous timeout */
8410       g_source_remove (icon_view->priv->search_timeout_id);
8411 
8412       /* schedule a new timeout */
8413       icon_view->priv->search_timeout_id = gdk_threads_add_timeout_full (G_PRIORITY_LOW, EXO_ICON_VIEW_SEARCH_DIALOG_TIMEOUT,
8414                                                                exo_icon_view_search_timeout, icon_view,
8415                                                                exo_icon_view_search_timeout_destroy);
8416     }
8417 
8418   /* verify that we have a search text */
8419   length = strlen (text);
8420   if (length < 1)
8421     return;
8422 
8423   /* verify that we have a valid model */
8424   model = exo_icon_view_get_model (icon_view);
8425   if (G_UNLIKELY (model == NULL))
8426     return;
8427 
8428   /* start the interactive search */
8429   if (gtk_tree_model_get_iter_first (model, &iter))
8430     {
8431       /* let's see if we have a match */
8432       if (exo_icon_view_search_iter (icon_view, model, &iter, text, &count, 1))
8433         icon_view->priv->search_selected_iter = 1;
8434     }
8435 }
8436 
8437 
8438 
8439 static gboolean
exo_icon_view_search_iter(ExoIconView * icon_view,GtkTreeModel * model,GtkTreeIter * iter,const gchar * text,gint * count,gint n)8440 exo_icon_view_search_iter (ExoIconView  *icon_view,
8441                            GtkTreeModel *model,
8442                            GtkTreeIter  *iter,
8443                            const gchar  *text,
8444                            gint         *count,
8445                            gint          n)
8446 {
8447   GtkTreePath *path;
8448 
8449   _exo_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
8450   _exo_return_val_if_fail (GTK_IS_TREE_MODEL (model), FALSE);
8451   _exo_return_val_if_fail (count != NULL, FALSE);
8452 
8453   /* search for a matching item */
8454   do
8455     {
8456       if (!(*icon_view->priv->search_equal_func) (model, icon_view->priv->search_column, text, iter, icon_view->priv->search_equal_data))
8457         {
8458           (*count) += 1;
8459           if (*count == n)
8460             {
8461               /* place cursor on the item and select it */
8462               path = gtk_tree_model_get_path (model, iter);
8463               exo_icon_view_select_path (icon_view, path);
8464               exo_icon_view_set_cursor (icon_view, path, NULL, FALSE);
8465               gtk_tree_path_free (path);
8466               return TRUE;
8467             }
8468         }
8469     }
8470   while (gtk_tree_model_iter_next (model, iter));
8471 
8472   /* no match */
8473   return FALSE;
8474 }
8475 
8476 
8477 
8478 static void
exo_icon_view_search_move(GtkWidget * widget,ExoIconView * icon_view,gboolean move_up)8479 exo_icon_view_search_move (GtkWidget   *widget,
8480                            ExoIconView *icon_view,
8481                            gboolean     move_up)
8482 {
8483   GtkTreeModel *model;
8484   const gchar  *text;
8485   GtkTreeIter   iter;
8486   gboolean      retval;
8487   gint          length;
8488   gint          count = 0;
8489 
8490   /* determine the current text for the search entry */
8491   text = gtk_entry_get_text (GTK_ENTRY (icon_view->priv->search_entry));
8492   if (G_UNLIKELY (text == NULL))
8493     return;
8494 
8495   /* if we already selected the first item, we cannot go up */
8496   if (move_up && icon_view->priv->search_selected_iter == 1)
8497     return;
8498 
8499   /* determine the length of the search text */
8500   length = strlen (text);
8501   if (G_UNLIKELY (length < 1))
8502     return;
8503 
8504   /* unselect all items */
8505   exo_icon_view_unselect_all (icon_view);
8506 
8507   /* verify that we have a valid model */
8508   model = exo_icon_view_get_model (icon_view);
8509   if (G_UNLIKELY (model == NULL))
8510     return;
8511 
8512   /* determine the iterator to the first item */
8513   if (!gtk_tree_model_get_iter_first (model, &iter))
8514     return;
8515 
8516   /* first attempt to search */
8517   retval = exo_icon_view_search_iter (icon_view, model, &iter, text, &count, move_up
8518                                       ? (icon_view->priv->search_selected_iter - 1)
8519                                       : (icon_view->priv->search_selected_iter + 1));
8520 
8521   /* check if we found something */
8522   if (G_LIKELY (retval))
8523     {
8524       /* match found */
8525       icon_view->priv->search_selected_iter += move_up ? -1 : 1;
8526     }
8527   else
8528     {
8529       /* return to old iter */
8530       if (gtk_tree_model_get_iter_first (model, &iter))
8531         {
8532           count = 0;
8533           exo_icon_view_search_iter (icon_view, model, &iter, text, &count,
8534                                      icon_view->priv->search_selected_iter);
8535         }
8536     }
8537 }
8538 
8539 
8540 
8541 static gboolean
exo_icon_view_search_start(ExoIconView * icon_view,gboolean keybinding)8542 exo_icon_view_search_start (ExoIconView *icon_view,
8543                             gboolean     keybinding)
8544 {
8545   /* check if typeahead is enabled */
8546   if (G_UNLIKELY (!icon_view->priv->enable_search && !keybinding))
8547     return FALSE;
8548 
8549   /* check if we already display the search window */
8550   if (icon_view->priv->search_window != NULL && gtk_widget_get_visible (icon_view->priv->search_window))
8551     return TRUE;
8552 
8553   /* we only start interactive search if we have focus,
8554    * we don't want to start interactive search if one of
8555    * our children has the focus.
8556    */
8557   if (!gtk_widget_has_focus (GTK_WIDGET (icon_view)))
8558     return FALSE;
8559 
8560   /* verify that we have a search column */
8561   if (G_UNLIKELY (icon_view->priv->search_column < 0))
8562     return FALSE;
8563 
8564   exo_icon_view_search_ensure_directory (icon_view);
8565 
8566   /* clear search entry if we were started by a keybinding */
8567   if (G_UNLIKELY (keybinding))
8568     gtk_entry_set_text (GTK_ENTRY (icon_view->priv->search_entry), "");
8569 
8570   /* determine the position for the search dialog */
8571   (*icon_view->priv->search_position_func) (icon_view, icon_view->priv->search_window, icon_view->priv->search_position_data);
8572 
8573   gtk_entry_grab_focus_without_selecting (GTK_ENTRY (icon_view->priv->search_entry));
8574 
8575   /* display the search dialog */
8576   gtk_widget_show (icon_view->priv->search_window);
8577 
8578   /* connect "changed" signal for the entry */
8579   if (G_UNLIKELY (icon_view->priv->search_entry_changed_id == 0))
8580     {
8581       icon_view->priv->search_entry_changed_id = g_signal_connect (G_OBJECT (icon_view->priv->search_entry), "changed",
8582                                                                    G_CALLBACK (exo_icon_view_search_init), icon_view);
8583     }
8584 
8585   /* start the search timeout */
8586   icon_view->priv->search_timeout_id = gdk_threads_add_timeout_full (G_PRIORITY_LOW, EXO_ICON_VIEW_SEARCH_DIALOG_TIMEOUT,
8587                                                            exo_icon_view_search_timeout, icon_view,
8588                                                            exo_icon_view_search_timeout_destroy);
8589 
8590   /* send focus-in event */
8591   _exo_gtk_widget_send_focus_change (icon_view->priv->search_entry, TRUE);
8592 
8593   /* search first matching iter */
8594   exo_icon_view_search_init (icon_view->priv->search_entry, icon_view);
8595 
8596   return TRUE;
8597 }
8598 
8599 
8600 
8601 static gboolean
exo_icon_view_search_equal_func(GtkTreeModel * model,gint column,const gchar * key,GtkTreeIter * iter,gpointer user_data)8602 exo_icon_view_search_equal_func (GtkTreeModel *model,
8603                                  gint          column,
8604                                  const gchar  *key,
8605                                  GtkTreeIter  *iter,
8606                                  gpointer      user_data)
8607 {
8608   const gchar *str;
8609   gboolean     retval = TRUE;
8610   GValue       transformed = { 0, };
8611   GValue       value = { 0, };
8612   gchar       *case_normalized_string = NULL;
8613   gchar       *case_normalized_key = NULL;
8614   gchar       *normalized_string;
8615   gchar       *normalized_key;
8616 
8617   /* determine the value for the column/iter */
8618   gtk_tree_model_get_value (model, iter, column, &value);
8619 
8620   /* try to transform the value to a string */
8621   g_value_init (&transformed, G_TYPE_STRING);
8622   if (!g_value_transform (&value, &transformed))
8623     {
8624       g_value_unset (&value);
8625       return TRUE;
8626     }
8627   g_value_unset (&value);
8628 
8629   /* check if we have a string value */
8630   str = g_value_get_string (&transformed);
8631   if (G_UNLIKELY (str == NULL))
8632     {
8633       g_value_unset (&transformed);
8634       return TRUE;
8635     }
8636 
8637   /* normalize the string and the key */
8638   normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
8639   normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
8640 
8641   /* check if we have normalized both string */
8642   if (G_LIKELY (normalized_string != NULL && normalized_key != NULL))
8643     {
8644       case_normalized_string = g_utf8_casefold (normalized_string, -1);
8645       case_normalized_key = g_utf8_casefold (normalized_key, -1);
8646 
8647       /* compare the casefolded strings */
8648       if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0)
8649         retval = FALSE;
8650     }
8651 
8652   /* cleanup */
8653   g_free (case_normalized_string);
8654   g_free (case_normalized_key);
8655   g_value_unset (&transformed);
8656   g_free (normalized_string);
8657   g_free (normalized_key);
8658 
8659   return retval;
8660 }
8661 
8662 
8663 
8664 static void
exo_icon_view_search_position_func(ExoIconView * icon_view,GtkWidget * search_dialog,gpointer user_data)8665 exo_icon_view_search_position_func (ExoIconView *icon_view,
8666                                     GtkWidget   *search_dialog,
8667                                     gpointer     user_data)
8668 {
8669   GtkRequisition requisition;
8670   GdkWindow     *view_window = gtk_widget_get_window (GTK_WIDGET (icon_view));
8671   GdkRectangle   work_area_dimensions;
8672   gint           view_width, view_height;
8673   gint           view_x, view_y;
8674   gint           x, y;
8675   GdkDisplay    *display;
8676   GdkRectangle   monitor_dimensions;
8677   GdkMonitor    *monitor;
8678 
8679   /* make sure the search dialog is realized */
8680   gtk_widget_realize (search_dialog);
8681 
8682   gdk_window_get_origin (view_window, &view_x, &view_y);
8683   view_width = gdk_window_get_width (view_window);
8684   view_height = gdk_window_get_height (view_window);
8685 
8686   /* FIXME: make actual use of new Gtk3 layout system */
8687   gtk_widget_get_preferred_width (search_dialog, NULL, &requisition.width);
8688   gtk_widget_get_preferred_height (search_dialog, NULL, &requisition.height);
8689 
8690   exo_icon_view_get_work_area_dimensions (view_window, &work_area_dimensions);
8691   if (view_x + view_width > work_area_dimensions.x + work_area_dimensions.width)
8692     x = work_area_dimensions.x + work_area_dimensions.width - requisition.width;
8693   else if (view_x + view_width - requisition.width < work_area_dimensions.x)
8694     x = work_area_dimensions.x;
8695   else
8696     x = view_x + view_width - requisition.width;
8697 
8698   if (view_y + view_height > work_area_dimensions.y + work_area_dimensions.height)
8699     y = work_area_dimensions.y + work_area_dimensions.height - requisition.height;
8700   else if (view_y + view_height < work_area_dimensions.y)
8701     y = work_area_dimensions.y;
8702   else
8703     y = view_y + view_height;
8704 
8705   display = gdk_window_get_display (view_window);
8706   if (display)
8707     {
8708       monitor = gdk_display_get_monitor_at_window (display, view_window);
8709       if (monitor)
8710         {
8711           gdk_monitor_get_geometry (monitor, &monitor_dimensions);
8712           if (y + requisition.height > monitor_dimensions.height)
8713             y = monitor_dimensions.height - requisition.height;
8714         }
8715     }
8716 
8717   gtk_window_move (GTK_WINDOW (search_dialog), x, y);
8718 }
8719 
8720 
8721 
8722 static gboolean
exo_icon_view_search_button_press_event(GtkWidget * widget,GdkEventButton * event,ExoIconView * icon_view)8723 exo_icon_view_search_button_press_event (GtkWidget      *widget,
8724                                          GdkEventButton *event,
8725                                          ExoIconView    *icon_view)
8726 {
8727   _exo_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
8728   _exo_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
8729 
8730   /* hide the search dialog */
8731   exo_icon_view_search_dialog_hide (widget, icon_view);
8732 
8733   if (event->window == icon_view->priv->bin_window)
8734     exo_icon_view_button_press_event (GTK_WIDGET (icon_view), event);
8735 
8736   return TRUE;
8737 }
8738 
8739 
8740 
8741 static gboolean
exo_icon_view_search_delete_event(GtkWidget * widget,GdkEventAny * event,ExoIconView * icon_view)8742 exo_icon_view_search_delete_event (GtkWidget   *widget,
8743                                    GdkEventAny *event,
8744                                    ExoIconView *icon_view)
8745 {
8746   _exo_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
8747   _exo_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
8748 
8749   /* hide the search dialog */
8750   exo_icon_view_search_dialog_hide (widget, icon_view);
8751 
8752   return TRUE;
8753 }
8754 
8755 
8756 
8757 static gboolean
exo_icon_view_search_key_press_event(GtkWidget * widget,GdkEventKey * event,ExoIconView * icon_view)8758 exo_icon_view_search_key_press_event (GtkWidget   *widget,
8759                                       GdkEventKey *event,
8760                                       ExoIconView *icon_view)
8761 {
8762   gboolean retval = FALSE;
8763 
8764   _exo_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
8765   _exo_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
8766 
8767 
8768   /* close window and cancel the search */
8769   if (event->keyval == GDK_KEY_Escape || event->keyval == GDK_KEY_Tab)
8770     {
8771       exo_icon_view_search_dialog_hide (widget, icon_view);
8772       return TRUE;
8773     }
8774 
8775   /* select previous matching iter */
8776   if (event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_KP_Up)
8777     {
8778       exo_icon_view_search_move (widget, icon_view, TRUE);
8779       retval = TRUE;
8780     }
8781 
8782   if (((event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == (GDK_CONTROL_MASK | GDK_SHIFT_MASK))
8783       && (event->keyval == GDK_KEY_g || event->keyval == GDK_KEY_G))
8784     {
8785       exo_icon_view_search_move (widget, icon_view, TRUE);
8786       retval = TRUE;
8787     }
8788 
8789   /* select next matching iter */
8790   if (event->keyval == GDK_KEY_Down || event->keyval == GDK_KEY_KP_Down)
8791     {
8792       exo_icon_view_search_move (widget, icon_view, FALSE);
8793       retval = TRUE;
8794     }
8795 
8796   if (((event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == GDK_CONTROL_MASK)
8797       && (event->keyval == GDK_KEY_g || event->keyval == GDK_KEY_G))
8798     {
8799       exo_icon_view_search_move (widget, icon_view, FALSE);
8800       retval = TRUE;
8801     }
8802 
8803   /* renew the flush timeout */
8804   if (retval && (icon_view->priv->search_timeout_id != 0))
8805     {
8806       /* drop the previous timeout */
8807       g_source_remove (icon_view->priv->search_timeout_id);
8808 
8809       /* schedule a new timeout */
8810       icon_view->priv->search_timeout_id = gdk_threads_add_timeout_full (G_PRIORITY_LOW, EXO_ICON_VIEW_SEARCH_DIALOG_TIMEOUT,
8811                                                                exo_icon_view_search_timeout, icon_view,
8812                                                                exo_icon_view_search_timeout_destroy);
8813     }
8814 
8815   return retval;
8816 }
8817 
8818 
8819 
8820 static gboolean
exo_icon_view_search_scroll_event(GtkWidget * widget,GdkEventScroll * event,ExoIconView * icon_view)8821 exo_icon_view_search_scroll_event (GtkWidget      *widget,
8822                                    GdkEventScroll *event,
8823                                    ExoIconView    *icon_view)
8824 {
8825   gboolean retval = TRUE;
8826 
8827   _exo_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
8828   _exo_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
8829 
8830   if (event->direction == GDK_SCROLL_UP)
8831     exo_icon_view_search_move (widget, icon_view, TRUE);
8832   else if (event->direction == GDK_SCROLL_DOWN)
8833     exo_icon_view_search_move (widget, icon_view, FALSE);
8834   else
8835     retval = FALSE;
8836 
8837   return retval;
8838 }
8839 
8840 
8841 
8842 static gboolean
exo_icon_view_search_timeout(gpointer user_data)8843 exo_icon_view_search_timeout (gpointer user_data)
8844 {
8845   ExoIconView *icon_view = EXO_ICON_VIEW (user_data);
8846 
8847   exo_icon_view_search_dialog_hide (icon_view->priv->search_window, icon_view);
8848 
8849   return FALSE;
8850 }
8851 
8852 
8853 
8854 static void
exo_icon_view_search_timeout_destroy(gpointer user_data)8855 exo_icon_view_search_timeout_destroy (gpointer user_data)
8856 {
8857   EXO_ICON_VIEW (user_data)->priv->search_timeout_id = 0;
8858 }
8859 
8860 
8861 
8862 #define __EXO_ICON_VIEW_C__
8863 #include <exo/exo-aliasdef.c>
8864