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